https://gcc.gnu.org/g:c90a932447215c54e4b12692de475bc1fdd6e31f
commit c90a932447215c54e4b12692de475bc1fdd6e31f Author: Mikael Morin <mik...@gcc.gnu.org> Date: Mon Mar 3 17:29:47 2025 +0100 Sauvegarde implementation gimple-exec Diff: --- gcc/c-family/c.opt | 4 + gcc/cgraphunit.cc | 4180 ++++++++++++++++++++++++++++++++++++++++++--- gcc/fortran/trans-decl.cc | 2 +- gcc/selftest-run-tests.cc | 2 + gcc/selftest.h | 1 + 5 files changed, 3944 insertions(+), 245 deletions(-) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 15698dc65bb2..d2e16ca50492 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -214,6 +214,10 @@ fgimple C Var(flag_gimple) Init(0) Enable parsing GIMPLE. +fgimple-exec +C Var(flag_gimple_exec) Init(0) +Enable execution of GIMPLE. + H C ObjC C++ ObjC++ Print the name of header files as they are used. diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc index 82f205488e90..c6dfc0f486b0 100644 --- a/gcc/cgraphunit.cc +++ b/gcc/cgraphunit.cc @@ -210,6 +210,9 @@ along with GCC; see the file COPYING3. If not see #include "ipa-inline.h" #include "omp-offload.h" #include "symtab-thunks.h" +#include "wide-int.h" +#include "selftest.h" +#include "tree-ssanames.h" /* Queue of cgraph nodes scheduled to be added into cgraph. This is a secondary queue used during optimization to accommodate passes that @@ -2321,349 +2324,4038 @@ symbol_table::output_weakrefs (void) } } -/* Perform simple optimizations based on callgraph. */ -void -symbol_table::compile (void) +class exec_context; +class data_storage; + + + +static bool +get_constant_type_size (tree type, unsigned &result) { - if (seen_error ()) - return; + tree tree_size = TYPE_SIZE (type); + gcc_assert (TREE_CODE (tree_size) == INTEGER_CST); + wide_int wi_size = wi::to_wide (tree_size); - symtab_node::checking_verify_symtab_nodes (); + gcc_assert (wi::fits_uhwi_p (wi_size)); + unsigned HOST_WIDE_INT hwi_size = wi_size.to_uhwi (); - symtab_node::check_ifunc_callee_symtab_nodes (); + gcc_assert (hwi_size <= UINT_MAX); + result = hwi_size; + return true; +} - timevar_push (TV_CGRAPHOPT); - if (pre_ipa_mem_report) - dump_memory_report ("Memory consumption before IPA"); - if (!quiet_flag) - fprintf (stderr, "Performing interprocedural optimizations\n"); - state = IPA; - /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE. */ - if (flag_generate_lto || flag_generate_offload) - lto_streamer_hooks_init (); +static unsigned +get_constant_type_size (tree type) +{ + unsigned result; + gcc_assert (get_constant_type_size (type, result)); + return result; +} - /* Don't run the IPA passes if there was any error or sorry messages. */ - if (!seen_error ()) - { - timevar_start (TV_CGRAPH_IPA_PASSES); - ipa_passes (); - timevar_stop (TV_CGRAPH_IPA_PASSES); - } - /* Do nothing else if any IPA pass found errors or if we are just streaming LTO. */ - if (seen_error () - || ((!in_lto_p || flag_incremental_link == INCREMENTAL_LINK_LTO) - && flag_lto && !flag_fat_lto_objects)) - { - timevar_pop (TV_CGRAPHOPT); - return; - } - global_info_ready = true; - if (dump_file) - { - fprintf (dump_file, "Optimized "); - symtab->dump (dump_file); - } - if (post_ipa_mem_report) - dump_memory_report ("Memory consumption after IPA"); - timevar_pop (TV_CGRAPHOPT); +enum value_type +{ + VAL_NONE, + VAL_UNDEFINED, + VAL_ADDRESS, + VAL_CONSTANT, + VAL_MIXED +}; - /* Output everything. */ - switch_to_section (text_section); - (*debug_hooks->assembly_start) (); - if (!quiet_flag) - fprintf (stderr, "Assembling functions:\n"); - symtab_node::checking_verify_symtab_nodes (); - bitmap_obstack_initialize (NULL); - execute_ipa_pass_list (g->get_passes ()->all_late_ipa_passes); - bitmap_obstack_release (NULL); - mark_functions_to_output (); +struct storage_ref +{ + const exec_context & context; + unsigned storage_index; - /* When weakref support is missing, we automatically translate all - references to NODE to references to its ultimate alias target. - The renaming mechanism uses flag IDENTIFIER_TRANSPARENT_ALIAS and - TREE_CHAIN. + storage_ref (const exec_context & ctx, unsigned idx) + : context (ctx), storage_index (idx) + {} + data_storage & get () const; +}; - Set up this mapping before we output any assembler but once we are sure - that all symbol renaming is done. - FIXME: All this ugliness can go away if we just do renaming at gimple - level by physically rewriting the IL. At the moment we can only redirect - calls, so we need infrastructure for renaming references as well. */ -#ifndef ASM_OUTPUT_WEAKREF - symtab_node *node; +struct storage_address +{ + storage_ref storage; + unsigned offset; - FOR_EACH_SYMBOL (node) - if (node->alias - && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->decl))) - { - IDENTIFIER_TRANSPARENT_ALIAS - (DECL_ASSEMBLER_NAME (node->decl)) = 1; - TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl)) - = (node->alias_target ? node->alias_target - : DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl)); - } -#endif + storage_address (const exec_context & ctx, unsigned idx, unsigned off) + : storage (ctx, idx), offset (off) + {} +}; - state = EXPANSION; +namespace selftest +{ + void data_value_classify_tests (); + void data_value_set_address_tests (); + void data_value_set_tests (); + void data_value_set_at_tests (); + void data_value_set_address_tests (); + void data_value_print_tests (); + void context_printer_print_first_data_ref_part_tests (); + void context_printer_print_value_update_tests (); + void exec_context_evaluate_tests (); + void exec_context_evaluate_unary_tests (); + void exec_context_evaluate_literal_tests (); + void exec_context_execute_assign_tests (); + void exec_context_execute_call_tests (); +} - /* Output first asm statements and anything ordered. The process - flag is cleared for these nodes, so we skip them later. */ - output_in_order (); - timevar_start (TV_CGRAPH_FUNC_EXPANSION); - expand_all_functions (); - timevar_stop (TV_CGRAPH_FUNC_EXPANSION); +class data_value +{ + const exec_context & context; + unsigned bit_width; + wide_int constant_mask; + wide_int address_mask; + wide_int constant_value; + vec<storage_address> addresses; + void set_cst_at (unsigned dest_offset, unsigned value_width, + const wide_int &val, unsigned src_offset); + storage_address *find_address (unsigned offset) const; + void set_at (unsigned dest_offset, unsigned value_width, + const data_value & value_src, unsigned src_offset); + + friend void selftest::data_value_classify_tests (); + friend void selftest::data_value_set_address_tests (); + friend void selftest::data_value_set_tests (); + friend void selftest::data_value_set_at_tests (); + +public: + data_value (const exec_context &ctx, unsigned width) + : context (ctx), bit_width (width), + constant_mask (wi::shwi (HOST_WIDE_INT_0, width)), + address_mask (wi::shwi (HOST_WIDE_INT_0, width)), + constant_value (wi::shwi (HOST_WIDE_INT_0, width)), + addresses () + {} + data_value (const exec_context &ctx, tree type) + : data_value (ctx, get_constant_type_size (type)) + {} + data_value (const data_value &) = default; + data_value & operator= (const data_value &); + value_type classify () const; + value_type classify (unsigned offset, unsigned width) const; + unsigned get_bitwidth () const { return bit_width; } + void set_address_at (data_storage &storage, unsigned offset); + void set_address (data_storage &); + void set_at (const data_value & value, unsigned offset); + void set (const data_value & value); + void set_cst_at (const wide_int & val, unsigned offset); + void set_cst (const wide_int & val); + wide_int get_cst_at (unsigned offset, unsigned width) const; + wide_int get_cst () const; + data_storage *get_address () const; + data_storage *get_address_at (unsigned offset) const; + data_value get_at (unsigned offset, unsigned width) const; + bool is_fully_defined () const { return (~(constant_mask | address_mask)) == 0; } + void print_at (pretty_printer & pp, tree type, unsigned offset, + unsigned width) const; + void print_at (pretty_printer & pp, tree type, unsigned offset) const; + void print (pretty_printer & pp, tree type) const; +}; - output_variables (); - process_new_functions (); - state = FINISHED; - output_weakrefs (); +enum storage_type +{ + STRG_VARIABLE, + STRG_ALLOC +}; - if (dump_file) - { - fprintf (dump_file, "\nFinal "); - symtab->dump (dump_file); - } - if (!flag_checking) - return; - symtab_node::verify_symtab_nodes (); - /* Double check that all inline clones are gone and that all - function bodies have been released from memory. */ - if (!seen_error ()) + +class data_storage +{ + const exec_context & context; + const storage_type type; + data_value value; + + union u { - cgraph_node *node; - bool error_found = false; + u (tree t) : variable (t) {} + u (unsigned alloc_idx, unsigned alloc_amount) + : allocated (alloc_idx, alloc_amount) + {} + //~u () {} - FOR_EACH_DEFINED_FUNCTION (node) - if (node->inlined_to - || gimple_has_body_p (node->decl)) - { - if (DECL_STRUCT_FUNCTION (node->decl) - && (DECL_STRUCT_FUNCTION (node->decl)->curr_properties - & PROP_assumptions_done) != 0) - continue; - error_found = true; - node->debug (); - } - if (error_found) - internal_error ("nodes with unreleased memory found"); + struct v + { + v (tree t) : decl (t) {} + const tree decl; + } + variable; + + const struct a + { + a (unsigned alloc_idx, unsigned alloc_amount) + : index (alloc_idx), amount_bits (alloc_amount) + {} + unsigned index; + unsigned amount_bits; + } + allocated; } -} + u; + +public: + data_storage (const exec_context &ctx, tree decl) + : context (ctx), type (STRG_VARIABLE), value (ctx, TREE_TYPE (decl)), + u (decl) + {} + data_storage (const exec_context &ctx, unsigned alloc_index, unsigned alloc_amount) + : context (ctx), type (STRG_ALLOC), value (ctx, alloc_amount), + u (alloc_index, alloc_amount) + {} + storage_type get_type () const { return type; } + const exec_context & get_context () const { return context; } + tree get_variable () const; + + bool matches (tree var) const + { return type == STRG_VARIABLE && u.variable.decl == var; } + + bool matches_alloc (unsigned index) const + { return type == STRG_ALLOC && u.allocated.index == index; } + + const data_value & get_value () const { return value; } + data_storage & operator= (const data_storage& other) = default; + void set (const data_value & val) { return value.set (val); } + void set_at (const data_value & val, unsigned offset) { return value.set_at (val, offset); } + void print (pretty_printer & pp) const; +}; -/* Earlydebug dump file, flags, and number. */ -static int debuginfo_early_dump_nr; -static FILE *debuginfo_early_dump_file; -static dump_flags_t debuginfo_early_dump_flags; +class context_printer +{ + pretty_printer pp; + dump_flags_t flags; + unsigned indent; + + friend void selftest::exec_context_evaluate_tests (); + friend void selftest::data_value_print_tests (); + friend void selftest::context_printer_print_first_data_ref_part_tests (); + friend void selftest::context_printer_print_value_update_tests (); + +public: + context_printer (); + context_printer (dump_flags_t f); + void begin_stmt (gimple *); + void print (tree); + void print_newline (); + void print_function_entry (struct function * func); + void print_function_exit (struct function * func); + void print_bb_jump (edge e); + void print_bb_entry (basic_block bb); + tree print_first_data_ref_part (exec_context & context, tree data_ref, unsigned offset); + void print_value_update (exec_context & context, tree, const data_value &); + void end_stmt (gimple *); +}; -/* Debug dump file, flags, and number. */ -static int debuginfo_dump_nr; -static FILE *debuginfo_dump_file; -static dump_flags_t debuginfo_dump_flags; +static data_value +execute (struct function *func, exec_context *caller, + context_printer & printer, vec<tree> * args); -/* Register the debug and earlydebug dump files. */ +class exec_context +{ + exec_context * const parent; + context_printer & printer; + vec<data_storage> storages; + unsigned next_alloc_index; + //void add_variables (const exec_context &); + data_value evaluate_constructor (tree cstr) const; + data_value evaluate_literal (enum tree_code code, tree value) const; + data_value evaluate_unary (enum tree_code code, tree arg) const; + template <typename A, typename L> + void add_variables (vec<tree, A, L> *variables, unsigned vars_count); + template <typename A> + void add_variables (vec<tree, A, vl_ptr> *variables); + template <typename A> + void add_variables (vec<tree, A, vl_embed> *variables); + void execute (gimple *g); + void execute_assign (gassign *g); + void execute_call (gcall *g); + data_storage *find_var (tree variable) const; + data_storage *find_alloc (unsigned index) const; + data_storage *allocate (unsigned amount); + void decompose_ref (tree data_ref, data_storage * & storage, int & offset) const; + void execute_phi (gphi *phi, edge e); +#if 0 + template <typename A, typename L> + exec_context (exec_context *caller, context_printer & printer, + unsigned storage_size, + vec<tree, A, L> *local_decls, vec<tree, A, L> *ssa_names); +#endif -void -debuginfo_early_init (void) + friend void selftest::data_value_print_tests (); + friend void selftest::data_value_set_address_tests (); + friend void selftest::data_value_set_tests (); + friend void selftest::exec_context_evaluate_unary_tests (); + friend void selftest::exec_context_evaluate_literal_tests (); + friend void selftest::exec_context_execute_assign_tests (); + friend void selftest::exec_context_execute_call_tests (); + +public: + exec_context (exec_context *caller, context_printer & printer, + vec<tree> & decls); +#if 0 + template <typename A> + exec_context (exec_context *caller, context_printer & printer, + vec<tree, A, vl_embed> *local_decls, vec<tree, A, vl_embed> *ssa_names); + template <typename A> + exec_context (exec_context *caller, context_printer & printer, + vec<tree, A, vl_ptr> *local_decls, vec<tree, A, vl_ptr> *ssa_names); +#endif + const exec_context & root () const; + int find (const data_storage &storage) const; + data_storage *find_reachable_var (tree variable) const; + data_storage *find_malloc (unsigned index) const; + gimple * execute (basic_block bb); + data_storage & get_storage (unsigned idx) const; + context_printer & get_printer () const { return printer; } + data_value evaluate (tree expr) const; + data_value execute_function (struct function *); + edge select_leaving_edge (basic_block bb, gimple *last_stmt); + void jump (edge e); +// bool evaluate (tree var, tree *result) const; +}; + + +class context_builder { - gcc::dump_manager *dumps = g->get_dumps (); - debuginfo_early_dump_nr = dumps->dump_register (".earlydebug", "earlydebug", - "earlydebug", DK_tree, - OPTGROUP_NONE, - false); - debuginfo_dump_nr = dumps->dump_register (".debug", "debug", - "debug", DK_tree, - OPTGROUP_NONE, - false); -} + vec<tree> decls; + +public: + context_builder (); + exec_context build (exec_context * caller, context_printer & printer); + template <typename A, typename L> + void add_decls (vec<tree, A, L> *additional_decls); + //void add_decls (vec<tree> *additional_decls); +}; -/* Initialize the debug and earlydebug dump files. */ +context_builder::context_builder () + : decls () +{} + + +template <typename A, typename L> void -debuginfo_init (void) +context_builder::add_decls (vec<tree, A, L> *additional_decls) { - gcc::dump_manager *dumps = g->get_dumps (); - debuginfo_dump_file = dump_begin (debuginfo_dump_nr, NULL); - debuginfo_dump_flags = dumps->get_dump_file_info (debuginfo_dump_nr)->pflags; - debuginfo_early_dump_file = dump_begin (debuginfo_early_dump_nr, NULL); - debuginfo_early_dump_flags - = dumps->get_dump_file_info (debuginfo_early_dump_nr)->pflags; + if (additional_decls == nullptr) + return; + + decls.reserve (additional_decls->length ()); + + tree *declp; + unsigned i; + FOR_EACH_VEC_ELT (*additional_decls, i, declp) + decls.quick_push (*declp); } -/* Finalize the debug and earlydebug dump files. */ +#if 0 void -debuginfo_fini (void) +context_builder::add_decls (vec<tree> *additional_decls) { - if (debuginfo_dump_file) - dump_end (debuginfo_dump_nr, debuginfo_dump_file); - if (debuginfo_early_dump_file) - dump_end (debuginfo_early_dump_nr, debuginfo_early_dump_file); + if (additional_decls == nullptr) + return; + + decls.reserve (additional_decls->length ()); + + tree *declp; + unsigned i; + FOR_EACH_VEC_ELT (*additional_decls, i, declp) + decls.quick_push (*declp); } +#endif -/* Set dump_file to the debug dump file. */ -void -debuginfo_start (void) +exec_context +context_builder::build (exec_context * caller, context_printer & printer) { - set_dump_file (debuginfo_dump_file); + return exec_context (caller, printer, decls); } -/* Undo setting dump_file to the debug dump file. */ -void -debuginfo_stop (void) +exec_context::exec_context (exec_context *caller, context_printer & printer, + vec<tree> & decls) + : parent (caller), printer (printer), storages (vNULL), next_alloc_index (0) { - set_dump_file (NULL); + add_variables (&decls); } -/* Set dump_file to the earlydebug dump file. */ +#if 0 void -debuginfo_early_start (void) +exec_context::add_variables (const exec_context & ctx) { - set_dump_file (debuginfo_early_dump_file); + storages.reserve (ctx.storages.length ()); + + auto_vec<tree> ctx_vars(ctx.storages.length ()); + + data_storage *strgp; + unsigned i; + FOR_EACH_VEC_ELT (ctx.storages, i, strgp) + ctx_vars.quick_push (strgp->get_variable ()); + + add_variables (&ctx_vars); } +#endif -/* Undo setting dump_file to the earlydebug dump file. */ +template <typename A, typename L> void -debuginfo_early_stop (void) +exec_context::add_variables (vec<tree, A, L> *variables, unsigned vars_count) { - set_dump_file (NULL); + if (vars_count == 0) + return; + + storages.reserve (vars_count); + + tree *varp; + unsigned i; + FOR_EACH_VEC_ELT (*variables, i, varp) + if (*varp != NULL_TREE + && TYPE_SIZE (TREE_TYPE (*varp)) != NULL_TREE) + storages.quick_push (data_storage (*this, *varp)); } -/* Analyze the whole compilation unit once it is parsed completely. */ +template <typename A> +void +exec_context::add_variables (vec<tree, A, vl_ptr> *variables) +{ + add_variables (variables, variables->length ()); +} +template <typename A> void -symbol_table::finalize_compilation_unit (void) +exec_context::add_variables (vec<tree, A, vl_embed> *variables) { - timevar_push (TV_CGRAPH); + add_variables (variables, vec_safe_length (variables)); +} - /* If we're here there's no current function anymore. Some frontends - are lazy in clearing these. */ - current_function_decl = NULL; - set_cfun (NULL); +#if 0 +template <typename A, typename L> +exec_context::exec_context (exec_context *caller, + context_printer & cp, + vec<tree> &decls) + : parent (caller), printer (cp), storages (vNULL), next_alloc_index (0) +{ + if (vars_count == 0) + return; - /* Do not skip analyzing the functions if there were errors, we - miss diagnostics for following functions otherwise. */ + storages.reserve (vars_count); - /* Emit size functions we didn't inline. */ - finalize_size_functions (); + tree *varp; + unsigned i; + FOR_EACH_VEC_ELT (decls, i, varp) + if (*varp != NULL_TREE + && TYPE_SIZE (TREE_TYPE (*varp)) != NULL_TREE) + storages.quick_push (data_storage (*this, *varp)); +} +#endif - /* Mark alias targets necessary and emit diagnostics. */ - handle_alias_pairs (); - if (!quiet_flag) - { - fprintf (stderr, "\nAnalyzing compilation unit\n"); - fflush (stderr); - } - if (flag_dump_passes) - dump_passes (); +context_printer::context_printer (dump_flags_t f) + : pp (), flags (f), indent (0) +{ + pp_needs_newline (&pp) = true; +} - /* Gimplify and lower all functions, compute reachability and - remove unreachable nodes. */ - analyze_functions (/*first_time=*/true); +context_printer::context_printer () + : context_printer (TDF_NONE) +{} - /* Mark alias targets necessary and emit diagnostics. */ - handle_alias_pairs (); - /* Gimplify and lower thunks. */ - analyze_functions (/*first_time=*/false); +void +context_printer::print_newline () +{ + int indent = pp_indentation (&pp); + pp_newline_and_flush (&pp); + pp_indentation (&pp) = indent; +} - /* All nested functions should be lowered now. */ - nested_function_info::release (); - /* Offloading requires LTO infrastructure. */ - if (!in_lto_p && g->have_offload) - flag_generate_offload = 1; +void +context_printer::begin_stmt (gimple *g) +{ + pp_indent (&pp); + pp_gimple_stmt_1 (&pp, g, pp_indentation (&pp), TDF_NONE); + print_newline (); + pp_indentation (&pp) += 2; +} - if (!seen_error ()) +void +context_printer::print (tree expr) +{ + dump_generic_node (&pp, expr, pp_indentation (&pp), flags, false); +} + + +static const char * +get_func_name (struct function *func) +{ + tree decl = func->decl; + tree name = DECL_NAME (decl); + return IDENTIFIER_POINTER (name); +} + +void +context_printer::print_function_entry (struct function *func) +{ + pp_indent (&pp); + pp_string (&pp, "# Entering function "); + pp_string (&pp, get_func_name (func)); + print_newline (); + pp_indentation (&pp) += 2; +} + +void +context_printer::print_function_exit (struct function *func) +{ + pp_indentation (&pp) -= 2; + pp_indent (&pp); + pp_string (&pp, "# Leaving function "); + pp_string (&pp, get_func_name (func)); + print_newline (); +} + +void +context_printer::print_bb_jump (edge e) +{ + pp_indent (&pp); + pp_string (&pp, "# Leaving bb "); + pp_decimal_int (&pp, e->src->index); + pp_string (&pp, ", preparing to enter bb "); + pp_decimal_int (&pp, e->dest->index); + print_newline (); +} + + +void +context_printer::print_bb_entry (basic_block bb) +{ + pp_indent (&pp); + pp_string (&pp, "# Entering bb "); + pp_decimal_int (&pp, bb->index); + print_newline (); +} + + +static tree +find_mem_ref_replacement (exec_context & context, tree data_ref, unsigned offset) +{ + tree ptr = TREE_OPERAND (data_ref, 0); + data_value ptr_val = context.evaluate (ptr); + if (ptr_val.classify () != VAL_ADDRESS) + return NULL_TREE; + + data_storage *ptr_target = ptr_val.get_address (); + gcc_assert (ptr_target != nullptr); + if (ptr_target->get_type () != STRG_VARIABLE) + return NULL_TREE; + + tree access_type = TREE_TYPE (data_ref); + tree var_ref = ptr_target->get_variable (); + tree var_type = TREE_TYPE (var_ref); + + if (var_type == access_type) + return var_ref; + else { - /* Give the frontends the chance to emit early debug based on - what is still reachable in the TU. */ - (*lang_hooks.finalize_early_debug) (); + tree access_offset = TREE_OPERAND (data_ref, 1); + gcc_assert (TREE_CONSTANT (access_offset)); + gcc_assert (tree_fits_shwi_p (access_offset)); + HOST_WIDE_INT shwi_offset = tree_to_shwi (access_offset); + gcc_assert (offset < UINT_MAX - shwi_offset); + HOST_WIDE_INT remaining_offset = shwi_offset * CHAR_BIT + offset; + + while (true) + { + if (TREE_CODE (var_type) == ARRAY_TYPE) + { + tree elt_type = TREE_TYPE (var_type); + unsigned elt_width; + gcc_assert (get_constant_type_size (elt_type, elt_width)); + unsigned HOST_WIDE_INT hw_idx = remaining_offset / elt_width; + tree t_idx = build_int_cst (integer_type_node, hw_idx); + var_ref = build4 (ARRAY_REF, elt_type, var_ref, + t_idx, NULL_TREE, NULL_TREE); + remaining_offset -= hw_idx * elt_width; + } + else if (TREE_CODE (var_type) == RECORD_TYPE) + { + tree field = NULL_TREE; + HOST_WIDE_INT field_position = -1; + tree next_field = TYPE_FIELDS (TREE_TYPE (var_ref)); - /* Clean up anything that needs cleaning up after initial debug - generation. */ - debuginfo_early_start (); - (*debug_hooks->early_finish) (main_input_filename); - debuginfo_early_stop (); + do + { + HOST_WIDE_INT next_position; + next_position = int_bit_position (next_field); + if (next_position > remaining_offset) + break; + + field = next_field; + field_position = next_position; + next_field = TREE_CHAIN (field); + } + while (next_field != NULL_TREE); + + gcc_assert (field != NULL_TREE + && field_position >= 0); + + var_ref = build3 (COMPONENT_REF, TREE_TYPE (field), + var_ref, field, NULL_TREE); + remaining_offset -= field_position; + } + else + break; + var_type = TREE_TYPE (var_ref); + } + gcc_assert (remaining_offset == 0); + return var_ref; } +} - /* Finally drive the pass manager. */ - compile (); +tree +context_printer::print_first_data_ref_part (exec_context & context, tree data_ref, unsigned offset) +{ + switch (TREE_CODE (data_ref)) + { + case MEM_REF: + { + tree mem_replacement = find_mem_ref_replacement (context, data_ref, + offset); + if (mem_replacement != NULL_TREE) + return print_first_data_ref_part (context, mem_replacement, 0); + } - timevar_pop (TV_CGRAPH); + /* Fall through. */ + + default: + print (data_ref); + } + + return TREE_TYPE (data_ref); +} + +void +context_printer::print_value_update (exec_context & context, tree lhs, const data_value & value) +{ + unsigned previously_done = 0; + unsigned width = get_constant_type_size (TREE_TYPE (lhs)); + while (previously_done < width) + { + pp_indent (&pp); + pp_character (&pp, '#'); + pp_space (&pp); + tree type_output = print_first_data_ref_part (context, lhs, previously_done); + unsigned just_done; + gcc_assert (get_constant_type_size (type_output, just_done)); + gcc_assert (just_done > 0); + gcc_assert (just_done <= width - previously_done); + pp_space (&pp); + pp_equal (&pp); + pp_space (&pp); + value.print_at (pp, type_output, previously_done, just_done); + print_newline (); + previously_done += just_done; + } + //pp_newline_and_indent (&pp, -2); +#if 0 + tree type = TREE_TYPE (lhs); + if (TREE_CODE (type) == VECTOR_TYPE) + { + unsigned size = get_constant_type_size (type); + tree elt_type = TREE_TYPE (type); + unsigned chunk_size = get_constant_type_size (elt_type); + gcc_assert (size % chunk_size == 0); + for (unsigned i = 0; i < size / chunk_size; i++) + //for (unsigned i = 0; i < TREE_VEC_LENGTH (type); i++) + { + tree lhs_part = TREE_VEC_ELT (lhs, i); + print_part_update (lhs_part, value, i * chunk_size); + } + } + else + print_part_update (lhs, value, 0); +#endif } -/* Reset all state within cgraphunit.cc so that we can rerun the compiler - within the same process. For use by toplev::finalize. */ void -cgraphunit_cc_finalize (void) +context_printer::end_stmt (gimple *g ATTRIBUTE_UNUSED) { - gcc_assert (cgraph_new_nodes.length () == 0); - cgraph_new_nodes.truncate (0); + pp_indentation (&pp) -= 2; +} - queued_nodes = &symtab_terminator; - first_analyzed = NULL; - first_analyzed_var = NULL; +data_storage & +storage_ref::get () const +{ + return context.get_storage (storage_index); +} + + +data_value & data_value::operator= (const data_value & other) +{ + gcc_assert (other.bit_width == bit_width); + set (other); + return *this; +} + + +enum value_type +data_value::classify () const +{ + return classify (0, bit_width); +} + +value_type +data_value::classify (unsigned offset, unsigned width) const +{ + wide_int mask = wi::shifted_mask (offset, width, false, bit_width); + bool has_address = (address_mask & mask) != 0; + bool has_constant = (constant_mask & mask) != 0; + + int has_count = has_address + has_constant; + if (has_count > 1) + return VAL_MIXED; + else if (has_count == 0) + return VAL_UNDEFINED; + else if (has_constant && ((~constant_mask) & mask) == 0) + return VAL_CONSTANT; + else if (has_address && ((~address_mask) & mask) == 0) + return VAL_ADDRESS; + else + return VAL_MIXED; +} + + +storage_address * +data_value::find_address (unsigned offset) const +{ + gcc_assert (offset <= bit_width - HOST_BITS_PER_PTR); + + storage_address *result = nullptr; + storage_address *strg_address; + unsigned i; + FOR_EACH_VEC_ELT (addresses, i, strg_address) + if (strg_address->offset == offset) + { + gcc_assert (result == nullptr); + result = strg_address; + } + + return result; } -/* Creates a wrapper from cgraph_node to TARGET node. Thunk is used for this - kind of wrapper method. */ void -cgraph_node::create_wrapper (cgraph_node *target) +data_value::set_address_at (data_storage &storage, unsigned offset) { - /* Preserve DECL_RESULT so we get right by reference flag. */ - tree decl_result = DECL_RESULT (decl); + wide_int mask = wi::shifted_mask (offset, HOST_BITS_PER_PTR, false, + bit_width); + enum value_type type = classify (offset, HOST_BITS_PER_PTR); + gcc_assert (type == VAL_ADDRESS || type == VAL_UNDEFINED); - /* Remove the function's body but keep arguments to be reused - for thunk. */ - release_body (true); - reset (); + if (type == VAL_ADDRESS) + { + storage_address *existing_address = find_address (offset); + gcc_assert (existing_address != nullptr); + /* Invalidate existing address. */ + existing_address->offset = -1; + } - DECL_UNINLINABLE (decl) = false; - DECL_RESULT (decl) = decl_result; - DECL_INITIAL (decl) = NULL; - allocate_struct_function (decl, false); - set_cfun (NULL); + constant_mask &= ~mask; + address_mask |= mask; - /* Turn alias into thunk and expand it into GIMPLE representation. */ - definition = true; - semantic_interposition = opt_for_fn (decl, flag_semantic_interposition); + const exec_context & ctx = storage.get_context (); - /* Create empty thunk, but be sure we did not keep former thunk around. - In that case we would need to preserve the info. */ - gcc_checking_assert (!thunk_info::get (this)); - thunk_info::get_create (this); - thunk = true; - create_edge (target, NULL, count); - callees->can_throw_external = !TREE_NOTHROW (target->decl); + int idx = ctx.find (storage); + gcc_assert (idx >= 0); - tree arguments = DECL_ARGUMENTS (decl); + storage_address addr_info (ctx, idx, offset);; + addresses.safe_push (addr_info); +} - while (arguments) + +void +data_value::set_address (data_storage &storage) +{ + gcc_assert (bit_width == HOST_BITS_PER_PTR); + set_address_at (storage, 0); +} + + +void +data_value::set_cst_at (unsigned dest_offset, unsigned value_width, + const wide_int & value_src, unsigned src_offset) +{ + unsigned src_width = value_src.get_precision (); + gcc_assert (dest_offset < bit_width); + gcc_assert (value_width <= bit_width - dest_offset); + gcc_assert (src_offset < src_width); + gcc_assert (value_width <= src_width - src_offset); + + enum value_type orig_type = classify (dest_offset, value_width); + wide_int dest_mask = wi::shifted_mask (dest_offset, value_width, false, + bit_width); + if (orig_type != VAL_CONSTANT) { - TREE_ADDRESSABLE (arguments) = false; - arguments = TREE_CHAIN (arguments); + constant_mask |= dest_mask; + address_mask &= ~dest_mask; } - expand_thunk (this, false, true); - thunk_info::remove (this); + wide_int src_mask = wi::shifted_mask (src_offset, value_width, false, + src_width); + wide_int value = value_src & src_mask; + if (src_offset > 0) + value = wi::lrshift (value, src_offset); - /* Inline summary set-up. */ - analyze (); - inline_analyze_function (this); + wide_int dest_value = wide_int_storage::from (wide_int_ref (value), bit_width, UNSIGNED); + if (dest_offset > 0) + dest_value <<= dest_offset; + + constant_value &= ~dest_mask; + constant_value |= dest_value; +} + +void +data_value::set_at (unsigned dest_offset, unsigned value_width, + const data_value & value_src, unsigned src_offset) +{ + gcc_assert (dest_offset < bit_width); + gcc_assert (value_width <= bit_width - dest_offset); + + enum value_type type = value_src.classify (src_offset, value_width); + switch (type) + { + case VAL_CONSTANT: + set_cst_at (dest_offset, value_width, value_src.constant_value, src_offset); + break; + + case VAL_ADDRESS: + { + if (value_width == HOST_BITS_PER_PTR) + { + storage_address *found_address = value_src.find_address (src_offset); + gcc_assert (found_address != nullptr); + + data_storage &storage = found_address->storage.get (); + set_address_at (storage, dest_offset); + } + else + { + gcc_assert (value_width > HOST_BITS_PER_PTR); + gcc_assert (value_width % HOST_BITS_PER_PTR == 0); + gcc_assert (dest_offset % HOST_BITS_PER_PTR == 0); + for (unsigned i = 0; i < value_width / HOST_BITS_PER_PTR; i++) + { + unsigned off = i * HOST_BITS_PER_PTR; + set_at (dest_offset + off, HOST_BITS_PER_PTR, + value_src, src_offset + off); + } + } + } + break; + + case VAL_MIXED: + { + gcc_assert ((constant_mask & address_mask) == 0); + + wide_int cst_part = value_src.constant_mask; + wide_int address_part = value_src.address_mask; + + unsigned src_width = value_src.bit_width; + + wide_int mask = wi::shifted_mask (src_offset, value_width, false, + value_src.bit_width); + + cst_part &= mask; + address_part &= mask; + + while (cst_part != 0 || address_part != 0) + { + int ctz_cst = wi::ctz (cst_part); + int ctz_addr = wi::ctz (address_part); + + int next_offset; + wide_int selected_part; + if (ctz_cst < ctz_addr) + { + next_offset = ctz_cst; + selected_part = cst_part; + } + else + { + next_offset = ctz_addr; + selected_part = address_part; + } + + int width = wi::ctz (wi::bit_not (wi::lrshift (selected_part, + next_offset))); + + unsigned offset = dest_offset + (next_offset - src_offset); + + set_at (offset, width, value_src, next_offset); + + wide_int mask = wi::shifted_mask (next_offset, width, false, src_width); + + cst_part &= ~mask; + address_part &= ~mask; + } + + } + break; + + default: + gcc_unreachable (); + } +} + +void +data_value::set_at (const data_value & value, unsigned offset) +{ + set_at (offset, value.bit_width, value, 0); +#if 0 + unsigned value_width = value.get_bitwidth (); + gcc_assert (offset < bit_width); + gcc_assert (value_width <= bit_width - offset); + + enum value_type type = value.classify (); + switch (type) + { + case VAL_CONSTANT: + set_cst_at (offset, value.bit_width, value.constant_value, 0); + break; + + case VAL_ADDRESS: + { + storage_address *found_address = value.find_address (0); + gcc_assert (found_address != nullptr); + + data_storage &storage = found_address->storage.get (); + set_address_at (storage, offset); + } + break; + + default: + gcc_unreachable (); + } +#endif +} + + +void +data_value::set (const data_value & value) +{ + gcc_assert (value.get_bitwidth () == bit_width); + set_at (value, 0); +} + +void +data_value::set_cst_at (const wide_int & val, unsigned offset) +{ + set_cst_at (offset, val.get_precision (), val, 0); +} + +void +data_value::set_cst (const wide_int & val) +{ + gcc_assert (val.get_precision () == bit_width); + set_cst_at (val, 0); } + +wide_int +data_value::get_cst_at (unsigned offset, unsigned width) const +{ + gcc_assert (offset < bit_width); + gcc_assert (width <= bit_width - offset); + + enum value_type val_type = classify (offset, width); + gcc_assert (val_type == VAL_CONSTANT); + wide_int tmp = wide_int::from (wide_int_ref (constant_value), bit_width, UNSIGNED); + if (offset > 0) + tmp = wi::lrshift (tmp, offset); + return wide_int::from (tmp, width, UNSIGNED); +} + +wide_int +data_value::get_cst () const +{ + return get_cst_at (0, bit_width); +} + +data_storage * +data_value::get_address_at (unsigned offset) const +{ + gcc_assert (classify (offset, HOST_BITS_PER_PTR) == VAL_ADDRESS); + wide_int mask = wi::shifted_mask (offset, HOST_BITS_PER_PTR, false, + bit_width); + + storage_address *addr_info = find_address (offset); + if (addr_info != nullptr) + return &(addr_info->storage.get ()); + + return nullptr; +} + + +data_storage * +data_value::get_address () const +{ + gcc_assert (bit_width == HOST_BITS_PER_PTR); + return get_address_at (0); +} + + +data_value +data_value::get_at (unsigned offset, unsigned width) const +{ + data_value result (context, width); + switch (classify (offset, width)) + { + case VAL_CONSTANT: + result.set_cst (get_cst_at (offset, width)); + break; + + case VAL_ADDRESS: + gcc_assert (width == HOST_BITS_PER_PTR); + result.set_address (*get_address_at (offset)); + break; + + default: + gcc_unreachable (); + } + + return result; +} + + +void +data_value::print_at (pretty_printer & pp, tree type, unsigned offset, + unsigned width) const +{ + if (TREE_CODE (type) == VECTOR_TYPE) + { + gcc_assert (width == bit_width); + gcc_assert (offset == 0); + tree elt_type = TREE_TYPE (type); + unsigned elt_width; + gcc_assert (get_constant_type_size (elt_type, elt_width)); + gcc_assert (elt_width != 0); + gcc_assert (bit_width % elt_width == 0); + pp_left_brace (&pp); + bool needs_comma = false; + for (unsigned i = 0; i < bit_width / elt_width; i++) + { + if (needs_comma) + pp_comma (&pp); + pp_space (&pp); + print_at (pp, elt_type, i * elt_width); + needs_comma = true; + } + pp_space (&pp); + pp_right_brace (&pp); + } + else + { + enum value_type val_type = classify (offset, width); + switch (val_type) + { + case VAL_ADDRESS: + { + gcc_assert (width == HOST_BITS_PER_PTR); + pp_ampersand (&pp); + data_storage *target_storage = get_address_at (offset); + gcc_assert (target_storage != nullptr); + target_storage->print (pp); + } + break; + + case VAL_CONSTANT: + { + const wide_int value = get_cst_at (offset, width); + if (TREE_CODE (type) == REAL_TYPE) + { + tree int_type = make_signed_type (width); + tree cst = wide_int_to_tree (int_type, value); + tree real = fold_build1 (VIEW_CONVERT_EXPR, type, cst); + context.get_printer ().print (real); + } + else + pp_wide_int (&pp, value, SIGNED); + } + break; + + default: + gcc_unreachable (); + } + } +#if 0 + gcc_assert (TREE_CODE (type) == INTEGER_TYPE); + gcc_assert (val_type == VAL_CONSTANT); + signop sign = TYPE_SIGN (type); + if (sign == SIGNED) + { + gcc_assert (wi::fits_shwi_p (constant_value)); + HOST_WIDE_INT val = constant_value.to_shwi (); + pp_wide_integer (&pp, val); + } + else if (sign == UNSIGNED) + { + gcc_assert (wi::fits_uhwi_p (constant_value)); + unsigned HOST_WIDE_INT val = constant_value.to_uhwi (); + pp_unsigned_wide_integer (&pp, val); + } + else + gcc_unreachable (); +#endif +} + + +void +data_value::print_at (pretty_printer & pp, tree type, unsigned offset) const +{ + unsigned width; + gcc_assert (get_constant_type_size (type, width)); + print_at (pp, type, offset, width); +} + +void +data_value::print (pretty_printer & pp, tree type) const +{ + print_at (pp, type, 0); +} + + +tree +data_storage::get_variable () const +{ + gcc_assert (type == STRG_VARIABLE); + return u.variable.decl; +} + +void +data_storage::print (pretty_printer & pp) const +{ + switch (type) + { + case STRG_VARIABLE: + { + tree decl = get_variable (); + context.get_printer ().print (decl); + } + break; + + case STRG_ALLOC: + { + pp_less (&pp); + pp_string (&pp, "alloc"); + pp_scalar (&pp, "%02d", u.allocated.index); + pp_left_paren (&pp); + unsigned allocated_amount = u.allocated.amount_bits; + gcc_assert (allocated_amount % CHAR_BIT == 0); + pp_decimal_int (&pp, allocated_amount / CHAR_BIT); + pp_right_paren (&pp); + pp_greater (&pp); + } + break; + } +} + + +int +exec_context::find (const data_storage & storage) const +{ + data_storage *strgp; + unsigned i; + FOR_EACH_VEC_ELT (storages, i, strgp) + if (i > INT_MAX) + return -2; + else if (strgp == &storage) + return i; + + return -1; +} + + +const exec_context & +exec_context::root () const +{ + const exec_context * ctx = this; + + while (ctx->parent != nullptr) + ctx = ctx->parent; + + return *ctx; +} + + +data_storage * +exec_context::find_var (tree var) const +{ + data_storage *strgp; + unsigned i; + FOR_EACH_VEC_ELT (storages, i, strgp) + if (strgp->matches (var)) + return strgp; + + return nullptr; +} + + +data_storage * +exec_context::find_reachable_var (tree variable) const +{ + data_storage * result = find_var (variable); + if (result != nullptr) + return result; + + if (parent != nullptr) + return root ().find_var (variable); + + return nullptr; +} + + +data_storage * +exec_context::find_alloc (unsigned index) const +{ + data_storage *strgp; + unsigned i; + FOR_EACH_VEC_ELT (storages, i, strgp) + if (strgp->matches_alloc (index)) + return strgp; + + return nullptr; +} + + +data_storage * +exec_context::allocate (unsigned amount) +{ + unsigned index = next_alloc_index; + storages.safe_push (data_storage (*this, index, amount * CHAR_BIT)); + data_storage * result = find_alloc (index); + next_alloc_index++; + return result; +} + + +data_storage & +exec_context::get_storage (unsigned idx) const +{ + gcc_assert (idx < storages.length ()); + return const_cast <data_storage &> (storages[idx]); +} + + +data_value +exec_context::evaluate (tree expr) const +{ + enum tree_code code = TREE_CODE (expr); + switch (code) + { + case ARRAY_REF: + case COMPONENT_REF: + { + data_storage *storage = nullptr; + int offset = -1; + decompose_ref (expr, storage, offset); + gcc_assert (storage != nullptr && offset >= 0); + data_value var_value = storage->get_value (); + unsigned bitwidth; + bool cst = get_constant_type_size (TREE_TYPE (expr), bitwidth); + gcc_assert (cst); + return var_value.get_at (offset, bitwidth); + } + break; + + case REAL_CST: + { + tree sint = make_signed_type (TYPE_PRECISION (TREE_TYPE (expr))); + tree t = fold_build1 (VIEW_CONVERT_EXPR, sint, expr); + return evaluate (t); + } + break; + + case MEM_REF: + { + tree ptr = TREE_OPERAND (expr, 0); + data_value val_ptr = evaluate (ptr); + gcc_assert (val_ptr.classify () == VAL_ADDRESS); + data_storage *storage = val_ptr.get_address (); + gcc_assert (storage != nullptr); + data_value storage_value = storage->get_value (); + + tree offset_bytes = TREE_OPERAND (expr, 1); + data_value val_off = evaluate (offset_bytes); + gcc_assert (val_off.classify () == VAL_CONSTANT); + wide_int wi_off = val_off.get_cst () * CHAR_BIT; + gcc_assert (wi::fits_uhwi_p (wi_off)); + unsigned offset = wi_off.to_uhwi (); + + unsigned bit_width; + if (!get_constant_type_size (TREE_TYPE (expr), bit_width)) + gcc_unreachable (); + + return storage_value.get_at (offset, bit_width); + } + break; + + case INTEGER_CST: + { + data_value result (*this, TREE_TYPE (expr)); + wide_int wi_expr = wi::to_wide (expr); + result.set_cst (wi_expr); + return result; + } + break; + + case SSA_NAME: + if (SSA_NAME_IS_DEFAULT_DEF (expr)) + return evaluate (SSA_NAME_VAR (expr)); + + /* Fallthrough. */ + case PARM_DECL: + case VAR_DECL: + { + data_storage *data = find_reachable_var (expr); + gcc_assert (data != nullptr); + return data->get_value (); + } + + case ADDR_EXPR: + { + data_storage *strg = find_reachable_var (TREE_OPERAND (expr, 0)); + gcc_assert (strg != nullptr); + data_value result (*this, TREE_TYPE (expr)); + result.set_address (*strg); + return result; + } + + case VECTOR_CST: + { + tree expr_type = TREE_TYPE (expr); + data_value result (*this, expr_type); + tree elt_type = TREE_TYPE (expr_type); + unsigned elt_size; + gcc_assert (get_constant_type_size (elt_type, elt_size)); + + unsigned HOST_WIDE_INT nunits; + gcc_assert (VECTOR_CST_NELTS (expr).is_constant (&nunits)); + for (unsigned i = 0; i < nunits; ++i) + { + tree elt = VECTOR_CST_ELT (expr, i); + if (TREE_CODE (elt) == REAL_CST + && TREE_TYPE (elt) != elt_type) + { + REAL_VALUE_TYPE *elt_val = TREE_REAL_CST_PTR (elt); + machine_mode expected_mode = TYPE_MODE (elt_type); + gcc_assert (exact_real_truncate (expected_mode, elt_val)); + REAL_VALUE_TYPE r; + real_convert (&r, expected_mode, elt_val); + elt = build_real (elt_type, r);; + } + data_value elt_val = evaluate (elt); + result.set_at (elt_val, i * elt_size); + } + return result; + } + + default: + gcc_unreachable (); + } +} + +data_value +exec_context::evaluate_constructor (tree cstr) const +{ + unsigned bit_width; + gcc_assert (TREE_CODE (TREE_TYPE (cstr)) == VECTOR_TYPE); + gcc_assert (get_constant_type_size (TREE_TYPE (cstr), bit_width)); + + data_value result(*this, bit_width); + + unsigned i; + tree idx, elt; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (cstr), i, idx, elt) + { + data_value val = evaluate (elt); + + gcc_assert (idx == NULL_TREE); + //gcc_assert (TREE_CODE (idx) == INTEGER_CST); + //wide_int wi_idx = wi::to_wide (idx); + + unsigned elt_size; + gcc_assert (get_constant_type_size (TREE_TYPE (elt), elt_size)); + gcc_assert (elt_size == HOST_BITS_PER_PTR); + + //wide_int offset = wi_idx * elt_size; + //gcc_assert (wi::fits_uhwi_p (offset)); + gcc_assert (i < bit_width / HOST_BITS_PER_PTR); + unsigned offset = i * elt_size; + + result.set_at (val, offset); + } + + return result; +} + + +data_value +exec_context::evaluate_unary (enum tree_code code, tree arg) const +{ + switch (code) + { + case NOP_EXPR: + return evaluate (arg); + + case CONSTRUCTOR: + return evaluate_constructor (arg); + + default: + gcc_unreachable (); + } +} + +data_value +exec_context::evaluate_literal (enum tree_code code, tree value) const +{ + data_value result(*this, TREE_TYPE (value)); + switch (code) + { + case INTEGER_CST: + { + wide_int wi_val = wi::to_wide (value); + result.set_cst (wi_val); + } + break; + + default: + gcc_unreachable (); + } + return result; +} + +void +exec_context::decompose_ref (tree data_ref, data_storage * & storage, int & offset) const +{ + offset = -1; + switch (TREE_CODE (data_ref)) + { + case VAR_DECL: + case SSA_NAME: + { + tree var = data_ref; + offset = 0; + storage = find_reachable_var (var); + } + break; + + case ARRAY_REF: + { + data_storage *parent_storage = nullptr; + int parent_offset = -1; + tree parent_ref = TREE_OPERAND (data_ref, 0); + decompose_ref (parent_ref, parent_storage, parent_offset); + gcc_assert (parent_offset >= 0); + gcc_assert (parent_storage != nullptr); + + tree idx = TREE_OPERAND (data_ref, 1); + data_value val = evaluate (idx); + gcc_assert (val.classify () == VAL_CONSTANT); + wide_int wi_idx = val.get_cst (); + gcc_assert (wi::fits_uhwi_p (wi_idx)); + unsigned HOST_WIDE_INT hw_idx = wi_idx.to_uhwi (); + + gcc_assert (TREE_OPERAND (data_ref, 3) == NULL_TREE); + tree elt_type = TREE_TYPE (TREE_TYPE (parent_ref)); + unsigned size_bits; + bool found_size = get_constant_type_size (elt_type, size_bits); + gcc_assert (found_size); + unsigned this_offset = hw_idx * size_bits; + + storage = parent_storage; + offset = parent_offset + this_offset; + } + break; + + case COMPONENT_REF: + { + data_storage *parent_storage = nullptr; + int parent_offset = -1; + decompose_ref (TREE_OPERAND (data_ref, 0), parent_storage, parent_offset); + gcc_assert (parent_offset >= 0); + gcc_assert (parent_storage != nullptr); + + int this_offset = int_bit_position (TREE_OPERAND (data_ref, 1)); + gcc_assert (this_offset >= 0); + + storage = parent_storage; + offset = parent_offset + this_offset; + } + break; + + case MEM_REF: + { + tree var = TREE_OPERAND (data_ref, 0); + data_value addr = evaluate (var); + gcc_assert (addr.classify () == VAL_ADDRESS); + storage = addr.get_address (); + + tree off = TREE_OPERAND (data_ref, 1); + data_value off_val = evaluate (off); + gcc_assert (off_val.classify () == VAL_CONSTANT); + wide_int wi_off = off_val.get_cst (); + gcc_assert (wi::fits_uhwi_p (wi_off)); + unsigned HOST_WIDE_INT uhwi_off = wi_off.to_uhwi (); + gcc_assert (uhwi_off <= UINT_MAX / CHAR_BIT); + offset = uhwi_off * CHAR_BIT; + } + break; + + default: + gcc_unreachable (); + } + + gcc_assert (storage != nullptr); + gcc_assert (offset >= 0); +} + +void +exec_context::execute_assign (gassign *g) +{ + tree lhs = gimple_assign_lhs (g); + gcc_assert (TREE_CODE (lhs) == MEM_REF + || TREE_CODE (lhs) == SSA_NAME + || TREE_CODE (lhs) == VAR_DECL + || TREE_CODE (lhs) == COMPONENT_REF); + data_value value (*this, TREE_TYPE (lhs)); + + enum tree_code rhs_code = gimple_assign_rhs_code (g); + switch (rhs_code) + { + case NOP_EXPR: + case CONSTRUCTOR: + value = evaluate_unary (rhs_code, gimple_assign_rhs1 (g)); + break; + + case VECTOR_CST: + case INTEGER_CST: + case SSA_NAME: + case COMPONENT_REF: + value = evaluate (gimple_assign_rhs1 (g)); + break; + + default: + gcc_unreachable (); + } + + printer.print_value_update (*this, lhs, value); + + data_storage *storage = nullptr; + int offset = -1; + decompose_ref (lhs, storage, offset); + gcc_assert (storage != nullptr); + gcc_assert (offset >= 0); + storage->set_at (value, offset); +} + + +void +exec_context::execute_call (gcall *g) +{ + if (gimple_call_builtin_p (g, BUILT_IN_MALLOC)) + { + gcc_assert (gimple_call_num_args (g) == 1); + tree arg = gimple_call_arg (g, 0); + gcc_assert (tree_fits_uhwi_p (arg)); + HOST_WIDE_INT alloc_amount = tree_to_uhwi (arg); + data_storage *storage = allocate (alloc_amount); + + tree lhs = gimple_call_lhs (g); + gcc_assert (lhs != NULL_TREE); + data_value ptr (*this, TREE_TYPE (lhs)); + ptr.set_address (*storage); + + printer.print_value_update (*this, lhs, ptr); + data_storage *lhs_strg = find_var (lhs); + gcc_assert (lhs_strg != nullptr); + lhs_strg->set (ptr); + } + else + { + tree fn = gimple_call_fn (g); + if (TREE_CODE (fn) == ADDR_EXPR) + fn = TREE_OPERAND (fn, 0); + gcc_assert (TREE_CODE (fn) == FUNCTION_DECL); + const char *fn_name = IDENTIFIER_POINTER (DECL_NAME (fn)); + if (strcmp (fn_name, "_gfortran_set_args") == 0 + || strcmp (fn_name, "_gfortran_set_options") == 0) + return; + + tree lhs = gimple_call_lhs (g); + unsigned nargs = gimple_call_num_args (g); + auto_vec <tree> arguments; + arguments.reserve (nargs); + + for (unsigned i = 0; i < nargs; i++) + arguments.quick_push (gimple_call_arg (g, i)); + + data_value result = ::execute (DECL_STRUCT_FUNCTION (fn), this, printer, + &arguments); + printer.print_value_update (*this, lhs, result); + data_storage *lhs_strg = find_var (lhs); + gcc_assert (lhs_strg != nullptr); + lhs_strg->set (result); + } +} + + +void +exec_context::execute (gimple *g) +{ + printer.begin_stmt (g); + switch (g->code) + { + case GIMPLE_ASSIGN: + execute_assign (as_a <gassign *> (g)); + break; + + case GIMPLE_CALL: + execute_call (as_a <gcall *> (g)); + break; + + default: + gcc_unreachable (); + } + printer.end_stmt (g); +} + + + +gimple * +exec_context::execute (basic_block bb) +{ + printer.print_bb_entry (bb); + + gimple *g = bb->il.gimple.seq; + + if (g == nullptr) + return nullptr; + + while (g && !is_ctrl_stmt (g)) + { + execute (g); + g = g->next; + } + + gcc_assert (!g || !g->next); + return g; +} + + +edge +exec_context::select_leaving_edge (basic_block bb, gimple *last_stmt) +{ + if (last_stmt == nullptr || is_a <ggoto *> (last_stmt)) + return single_succ_edge (bb); + + if (is_a <gcond *> (last_stmt)) + { + gcond *cond = as_a <gcond *> (last_stmt); + + enum tree_code code = gimple_cond_code (cond); + tree lhs = gimple_cond_lhs (cond); + data_value lhs_val = evaluate (lhs); + gcc_assert (lhs_val.classify () == VAL_CONSTANT); + wide_int wi_lhs = lhs_val.get_cst (); + tree ltype = build_nonstandard_integer_type (lhs_val.get_bitwidth (), false); + tree lval = wide_int_to_tree (ltype, wi_lhs); + lval = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (lhs), lval); + + tree rhs = gimple_cond_rhs (cond); + data_value rhs_val = evaluate (rhs); + gcc_assert (rhs_val.classify () == VAL_CONSTANT); + wide_int wi_rhs = rhs_val.get_cst (); + tree rtype = build_nonstandard_integer_type (rhs_val.get_bitwidth (), false); + tree rval = wide_int_to_tree (rtype, wi_rhs); + rval = fold_build1 (VIEW_CONVERT_EXPR, TREE_TYPE (rhs), rval); + + tree result = fold_binary (code, boolean_type_node, lval, rval); + gcc_assert (result != NULL_TREE); + + int flag; + if (integer_onep (result)) + flag = EDGE_TRUE_VALUE; + else if (integer_zerop (result)) + flag = EDGE_FALSE_VALUE; + else + gcc_unreachable (); + + edge e, selected = nullptr; + edge_iterator ei; + FOR_EACH_EDGE (e, ei, bb->succs) + if (e->flags & flag) + { + gcc_assert (selected == nullptr); + selected = e; + } + + gcc_assert (selected != nullptr); + return selected; + } + + gcc_unreachable (); +} + + +void +exec_context::execute_phi (gphi *phi, edge e) +{ + printer.begin_stmt (phi); + tree lhs = gimple_phi_result (phi); + tree phi_val = gimple_phi_arg_def_from_edge (phi, e); + gassign *assign = gimple_build_assign (lhs, phi_val); + execute_assign (assign); + printer.end_stmt (phi); +} + + +void +exec_context::jump (edge e) +{ + printer.print_bb_jump (e); + + gphi_iterator gsi; + for (gsi = gsi_start_nonvirtual_phis (e->dest); !gsi_end_p (gsi); + gsi_next_nonvirtual_phi (&gsi)) + execute_phi (*gsi, e); +} + + +data_value +exec_context::execute_function (struct function *func) +{ + printer.print_function_entry (func); + basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (func); + greturn *final_stmt = nullptr; + + while (true) + { + gimple *last_stmt = execute (bb); + + if (last_stmt != nullptr + && is_a <greturn *> (last_stmt)) + { + final_stmt = as_a <greturn *> (last_stmt); + break; + } + + edge e = select_leaving_edge (bb, last_stmt); + jump (e); + bb = e->dest; + } + + tree retexpr = gimple_return_retval (final_stmt); + data_value result = evaluate (retexpr); + printer.print_function_exit (func); + return result; +} + + +static data_value +execute (struct function *func, exec_context *caller, + context_printer & printer, vec<tree> * arg_values) +{ + tree fndecl = func->decl; + + auto_vec <tree> arguments; + + tree arg = DECL_ARGUMENTS (fndecl); + while (arg != NULL_TREE) + { + arguments.safe_push (arg); + arg = TREE_CHAIN (arg); + } + + context_builder builder {}; + builder.add_decls (func->gimple_df->ssa_names); + builder.add_decls (func->local_decls); + builder.add_decls (&arguments); + + exec_context ctx = builder.build (caller, printer); + if (caller != nullptr) + { + tree *valp = nullptr; + unsigned i = 0; + tree arg = DECL_ARGUMENTS (fndecl); + while (arg != NULL_TREE && arg_values->iterate (i, &valp)) + { + data_value value = caller->evaluate (*valp); + data_storage *storage = ctx.find_reachable_var (arg); + gcc_assert (storage != nullptr); + storage->set (value); + + arg = TREE_CHAIN (arg); + i++; + } + + gcc_assert (arg == NULL_TREE && !arg_values->iterate (i, &valp)); + } + return ctx.execute_function (func); +} + + +static struct function * +find_main () +{ + struct cgraph_node * node; + FOR_EACH_DEFINED_FUNCTION (node) + { + struct function *fun = node->get_fun (); + tree decl = fun->decl; + tree name = DECL_NAME (decl); + const char *id = IDENTIFIER_POINTER (name); + if (strcmp (id, "main") == 0) + return fun; + } + return nullptr; +} + +static void +execute (void) +{ + varpool_node *vnode; + + vec<tree> static_vars{}; + + FOR_EACH_VARIABLE (vnode) + { + tree decl = vnode->decl; + if (decl + && (TREE_STATIC (decl) + || TREE_PUBLIC (decl) + || DECL_EXTERNAL (decl))) + static_vars.safe_push (decl); + } + + context_printer printer; + + context_builder builder {}; + builder.add_decls (&static_vars); + exec_context root_context = builder.build (nullptr, printer); + + struct function *main = find_main (); + gcc_assert (main != nullptr); + vec<tree> args{}; + args.safe_push (build_zero_cst (integer_type_node)); + args.safe_push (null_pointer_node); + execute (main, &root_context, printer, &args); +} + +/* Perform simple optimizations based on callgraph. */ + +void +symbol_table::compile (void) +{ + if (seen_error ()) + return; + + symtab_node::checking_verify_symtab_nodes (); + + symtab_node::check_ifunc_callee_symtab_nodes (); + + timevar_push (TV_CGRAPHOPT); + if (pre_ipa_mem_report) + dump_memory_report ("Memory consumption before IPA"); + if (!quiet_flag) + fprintf (stderr, "Performing interprocedural optimizations\n"); + state = IPA; + + /* If LTO is enabled, initialize the streamer hooks needed by GIMPLE. */ + if (flag_generate_lto || flag_generate_offload) + lto_streamer_hooks_init (); + + /* Don't run the IPA passes if there was any error or sorry messages. */ + if (!seen_error ()) + { + timevar_start (TV_CGRAPH_IPA_PASSES); + ipa_passes (); + timevar_stop (TV_CGRAPH_IPA_PASSES); + } + /* Do nothing else if any IPA pass found errors or if we are just streaming LTO. */ + if (seen_error () + || ((!in_lto_p || flag_incremental_link == INCREMENTAL_LINK_LTO) + && flag_lto && !flag_fat_lto_objects)) + { + timevar_pop (TV_CGRAPHOPT); + return; + } + + global_info_ready = true; + if (dump_file) + { + fprintf (dump_file, "Optimized "); + symtab->dump (dump_file); + } + if (post_ipa_mem_report) + dump_memory_report ("Memory consumption after IPA"); + timevar_pop (TV_CGRAPHOPT); + + if (flag_gimple_exec) + execute (); + + /* Output everything. */ + switch_to_section (text_section); + (*debug_hooks->assembly_start) (); + if (!quiet_flag) + fprintf (stderr, "Assembling functions:\n"); + symtab_node::checking_verify_symtab_nodes (); + + bitmap_obstack_initialize (NULL); + execute_ipa_pass_list (g->get_passes ()->all_late_ipa_passes); + bitmap_obstack_release (NULL); + mark_functions_to_output (); + + /* When weakref support is missing, we automatically translate all + references to NODE to references to its ultimate alias target. + The renaming mechanism uses flag IDENTIFIER_TRANSPARENT_ALIAS and + TREE_CHAIN. + + Set up this mapping before we output any assembler but once we are sure + that all symbol renaming is done. + + FIXME: All this ugliness can go away if we just do renaming at gimple + level by physically rewriting the IL. At the moment we can only redirect + calls, so we need infrastructure for renaming references as well. */ +#ifndef ASM_OUTPUT_WEAKREF + symtab_node *node; + + FOR_EACH_SYMBOL (node) + if (node->alias + && lookup_attribute ("weakref", DECL_ATTRIBUTES (node->decl))) + { + IDENTIFIER_TRANSPARENT_ALIAS + (DECL_ASSEMBLER_NAME (node->decl)) = 1; + TREE_CHAIN (DECL_ASSEMBLER_NAME (node->decl)) + = (node->alias_target ? node->alias_target + : DECL_ASSEMBLER_NAME (node->get_alias_target ()->decl)); + } +#endif + + state = EXPANSION; + + /* Output first asm statements and anything ordered. The process + flag is cleared for these nodes, so we skip them later. */ + output_in_order (); + + timevar_start (TV_CGRAPH_FUNC_EXPANSION); + expand_all_functions (); + timevar_stop (TV_CGRAPH_FUNC_EXPANSION); + + output_variables (); + + process_new_functions (); + state = FINISHED; + output_weakrefs (); + + if (dump_file) + { + fprintf (dump_file, "\nFinal "); + symtab->dump (dump_file); + } + if (!flag_checking) + return; + symtab_node::verify_symtab_nodes (); + /* Double check that all inline clones are gone and that all + function bodies have been released from memory. */ + if (!seen_error ()) + { + cgraph_node *node; + bool error_found = false; + + FOR_EACH_DEFINED_FUNCTION (node) + if (node->inlined_to + || gimple_has_body_p (node->decl)) + { + if (DECL_STRUCT_FUNCTION (node->decl) + && (DECL_STRUCT_FUNCTION (node->decl)->curr_properties + & PROP_assumptions_done) != 0) + continue; + error_found = true; + node->debug (); + } + if (error_found) + internal_error ("nodes with unreleased memory found"); + } +} + +/* Earlydebug dump file, flags, and number. */ + +static int debuginfo_early_dump_nr; +static FILE *debuginfo_early_dump_file; +static dump_flags_t debuginfo_early_dump_flags; + +/* Debug dump file, flags, and number. */ + +static int debuginfo_dump_nr; +static FILE *debuginfo_dump_file; +static dump_flags_t debuginfo_dump_flags; + +/* Register the debug and earlydebug dump files. */ + +void +debuginfo_early_init (void) +{ + gcc::dump_manager *dumps = g->get_dumps (); + debuginfo_early_dump_nr = dumps->dump_register (".earlydebug", "earlydebug", + "earlydebug", DK_tree, + OPTGROUP_NONE, + false); + debuginfo_dump_nr = dumps->dump_register (".debug", "debug", + "debug", DK_tree, + OPTGROUP_NONE, + false); +} + +/* Initialize the debug and earlydebug dump files. */ + +void +debuginfo_init (void) +{ + gcc::dump_manager *dumps = g->get_dumps (); + debuginfo_dump_file = dump_begin (debuginfo_dump_nr, NULL); + debuginfo_dump_flags = dumps->get_dump_file_info (debuginfo_dump_nr)->pflags; + debuginfo_early_dump_file = dump_begin (debuginfo_early_dump_nr, NULL); + debuginfo_early_dump_flags + = dumps->get_dump_file_info (debuginfo_early_dump_nr)->pflags; +} + +/* Finalize the debug and earlydebug dump files. */ + +void +debuginfo_fini (void) +{ + if (debuginfo_dump_file) + dump_end (debuginfo_dump_nr, debuginfo_dump_file); + if (debuginfo_early_dump_file) + dump_end (debuginfo_early_dump_nr, debuginfo_early_dump_file); +} + +/* Set dump_file to the debug dump file. */ + +void +debuginfo_start (void) +{ + set_dump_file (debuginfo_dump_file); +} + +/* Undo setting dump_file to the debug dump file. */ + +void +debuginfo_stop (void) +{ + set_dump_file (NULL); +} + +/* Set dump_file to the earlydebug dump file. */ + +void +debuginfo_early_start (void) +{ + set_dump_file (debuginfo_early_dump_file); +} + +/* Undo setting dump_file to the earlydebug dump file. */ + +void +debuginfo_early_stop (void) +{ + set_dump_file (NULL); +} + +/* Analyze the whole compilation unit once it is parsed completely. */ + +void +symbol_table::finalize_compilation_unit (void) +{ + timevar_push (TV_CGRAPH); + + /* If we're here there's no current function anymore. Some frontends + are lazy in clearing these. */ + current_function_decl = NULL; + set_cfun (NULL); + + /* Do not skip analyzing the functions if there were errors, we + miss diagnostics for following functions otherwise. */ + + /* Emit size functions we didn't inline. */ + finalize_size_functions (); + + /* Mark alias targets necessary and emit diagnostics. */ + handle_alias_pairs (); + + if (!quiet_flag) + { + fprintf (stderr, "\nAnalyzing compilation unit\n"); + fflush (stderr); + } + + if (flag_dump_passes) + dump_passes (); + + /* Gimplify and lower all functions, compute reachability and + remove unreachable nodes. */ + analyze_functions (/*first_time=*/true); + + /* Mark alias targets necessary and emit diagnostics. */ + handle_alias_pairs (); + + /* Gimplify and lower thunks. */ + analyze_functions (/*first_time=*/false); + + /* All nested functions should be lowered now. */ + nested_function_info::release (); + + /* Offloading requires LTO infrastructure. */ + if (!in_lto_p && g->have_offload) + flag_generate_offload = 1; + + if (!seen_error ()) + { + /* Give the frontends the chance to emit early debug based on + what is still reachable in the TU. */ + (*lang_hooks.finalize_early_debug) (); + + /* Clean up anything that needs cleaning up after initial debug + generation. */ + debuginfo_early_start (); + (*debug_hooks->early_finish) (main_input_filename); + debuginfo_early_stop (); + } + + /* Finally drive the pass manager. */ + compile (); + + timevar_pop (TV_CGRAPH); +} + +/* Reset all state within cgraphunit.cc so that we can rerun the compiler + within the same process. For use by toplev::finalize. */ + +void +cgraphunit_cc_finalize (void) +{ + gcc_assert (cgraph_new_nodes.length () == 0); + cgraph_new_nodes.truncate (0); + + queued_nodes = &symtab_terminator; + + first_analyzed = NULL; + first_analyzed_var = NULL; +} + +/* Creates a wrapper from cgraph_node to TARGET node. Thunk is used for this + kind of wrapper method. */ + +void +cgraph_node::create_wrapper (cgraph_node *target) +{ + /* Preserve DECL_RESULT so we get right by reference flag. */ + tree decl_result = DECL_RESULT (decl); + + /* Remove the function's body but keep arguments to be reused + for thunk. */ + release_body (true); + reset (); + + DECL_UNINLINABLE (decl) = false; + DECL_RESULT (decl) = decl_result; + DECL_INITIAL (decl) = NULL; + allocate_struct_function (decl, false); + set_cfun (NULL); + + /* Turn alias into thunk and expand it into GIMPLE representation. */ + definition = true; + semantic_interposition = opt_for_fn (decl, flag_semantic_interposition); + + /* Create empty thunk, but be sure we did not keep former thunk around. + In that case we would need to preserve the info. */ + gcc_checking_assert (!thunk_info::get (this)); + thunk_info::get_create (this); + thunk = true; + create_edge (target, NULL, count); + callees->can_throw_external = !TREE_NOTHROW (target->decl); + + tree arguments = DECL_ARGUMENTS (decl); + + while (arguments) + { + TREE_ADDRESSABLE (arguments) = false; + arguments = TREE_CHAIN (arguments); + } + + expand_thunk (this, false, true); + thunk_info::remove (this); + + /* Inline summary set-up. */ + analyze (); + inline_analyze_function (this); +} + +#if CHECKING_P + +namespace selftest +{ + +//void +//get_constant_type_size_tests () +//{ +// ASSERT_EQ (get_constant_type_size (ptr_type_node), HOST_BITS_PER_PTR); +// +// tree vec2ptr = build_vector_type (ptr_type_node, 2); +// +// int val = get_constant_type_size (vec2ptr); +// ASSERT_EQ (val, 2 * HOST_BITS_PER_PTR); +// //ASSERT_EQ (get_constant_type_size (vec2ptr), 2 * HOST_BITS_PER_PTR); +//} + +static tree +create_var (tree type, const char * name) +{ + return build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (name), + type); +} + + +void +data_value_classify_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node,"a"); + + vec<tree> decls{}; + decls.safe_push (a); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_value val(ctx, integer_type_node); + + ASSERT_EQ (val.classify (), VAL_UNDEFINED); + + wide_int i = wi::shwi (17, get_constant_type_size (integer_type_node)); + + val.set_cst (i); + + ASSERT_EQ (val.classify (), VAL_CONSTANT); + + + data_storage *storage_a = ctx.find_reachable_var (a); + gcc_assert (storage_a != nullptr); + + data_value ptr(ctx, ptr_type_node); + + ASSERT_EQ (ptr.classify (), VAL_UNDEFINED); + + ptr.set_address (*storage_a); + + ASSERT_EQ (ptr.classify (), VAL_ADDRESS); + + + tree vec2int = build_vector_type (integer_type_node, 2); + data_value val2(ctx, vec2int); + + ASSERT_EQ (val2.classify (0, HOST_BITS_PER_INT), VAL_UNDEFINED); + ASSERT_EQ (val2.classify (HOST_BITS_PER_INT, HOST_BITS_PER_INT), + VAL_UNDEFINED); + ASSERT_EQ (val2.classify (), VAL_UNDEFINED); + + val2.set_cst_at (i, HOST_BITS_PER_INT); + + ASSERT_EQ (val2.classify (0, HOST_BITS_PER_INT), VAL_UNDEFINED); + ASSERT_EQ (val2.classify (HOST_BITS_PER_INT, HOST_BITS_PER_INT), VAL_CONSTANT); + ASSERT_EQ (val2.classify (), VAL_MIXED); + + val2.set_cst_at (i, 0); + + ASSERT_EQ (val2.classify (0, HOST_BITS_PER_INT), VAL_CONSTANT); + ASSERT_EQ (val2.classify (HOST_BITS_PER_INT, HOST_BITS_PER_INT), VAL_CONSTANT); + ASSERT_EQ (val2.classify (), VAL_CONSTANT); + + + tree vec2ptr = build_vector_type (ptr_type_node, 2); + data_value val3(ctx, vec2ptr); + + ASSERT_EQ (val3.classify (0, HOST_BITS_PER_PTR), VAL_UNDEFINED); + ASSERT_EQ (val3.classify (HOST_BITS_PER_PTR, HOST_BITS_PER_PTR), + VAL_UNDEFINED); + ASSERT_EQ (val3.classify (), VAL_UNDEFINED); + + val3.set_address_at (*storage_a, HOST_BITS_PER_PTR); + + ASSERT_EQ (val3.classify (0, HOST_BITS_PER_PTR), VAL_UNDEFINED); + ASSERT_EQ (val3.classify (HOST_BITS_PER_PTR, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val3.classify (), VAL_MIXED); + + val3.set_address_at (*storage_a, 0); + + ASSERT_EQ (val3.classify (0, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val3.classify (HOST_BITS_PER_PTR, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val3.classify (), VAL_ADDRESS); +} + +void +exec_context_find_reachable_var_tests () +{ + context_printer printer; + //printer.pp.set_output_stream (nullptr); + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + tree c = create_var (integer_type_node, "c"); + + vec<tree> vars{}; + vars.safe_push (a); + vec<tree> regs{}; + regs.safe_push (b); + + context_builder builder {}; + builder.add_decls (&vars); + builder.add_decls (®s); + exec_context ctx = builder.build (nullptr, printer); + + ASSERT_EQ (ctx.find_reachable_var (c), nullptr); + ASSERT_NE (ctx.find_reachable_var (b), nullptr); + ASSERT_NE (ctx.find_reachable_var (a), nullptr); + + data_storage *storage_a = ctx.find_reachable_var (a); + ASSERT_EQ (storage_a->get_variable (), a); + data_storage *storage_b = ctx.find_reachable_var (b); + ASSERT_EQ (storage_b->get_variable (), b); + + tree d = create_var (integer_type_node, "d"); + tree e = create_var (integer_type_node, "e"); + + vec<tree> vars2{}; + vars2.safe_push (d); + vec<tree> regs2{}; + regs2.safe_push (e); + + context_builder builder2 {}; + builder.add_decls (&vars2); + builder.add_decls (®s2); + exec_context ctx2 = builder.build (&ctx, printer); + + ASSERT_NE (ctx2.find_reachable_var (e), nullptr); + ASSERT_NE (ctx2.find_reachable_var (d), nullptr); + ASSERT_NE (ctx2.find_reachable_var (b), nullptr); + ASSERT_NE (ctx2.find_reachable_var (a), nullptr); + + vec<tree> empty{}; + + exec_context ctx3 = context_builder ().build (&ctx2, printer); + + ASSERT_NE (ctx3.find_reachable_var (a), nullptr); + ASSERT_NE (ctx3.find_reachable_var (b), nullptr); + ASSERT_EQ (ctx3.find_reachable_var (d), nullptr); + ASSERT_EQ (ctx3.find_reachable_var (e), nullptr); +} + +void +data_value_set_address_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_value val1(ctx, ptr_type_node); + + data_storage *storage_a = ctx.find_reachable_var (a); + val1.set_address (*storage_a); + + ASSERT_EQ (val1.classify (), VAL_ADDRESS); + ASSERT_EQ (val1.get_address (), storage_a); + + data_storage *storage_b = ctx.find_reachable_var (b); + val1.set_address (*storage_b); + + ASSERT_EQ (val1.classify (), VAL_ADDRESS); + ASSERT_EQ (val1.get_address (), storage_b); + + exec_context ctx2 = context_builder ().build (&ctx, printer); + + data_value val2(ctx2, ptr_type_node); + + ASSERT_EQ (ctx2.find_reachable_var (a), storage_a); +} + +void +data_value_set_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_storage *storage_a = ctx.find_var (a); + + data_value val1(ctx, ptr_type_node); + + val1.set_address (*storage_a); + + data_value val2(ctx, ptr_type_node); + + val2.set (val1); + ASSERT_EQ (val2.classify (), VAL_ADDRESS); + ASSERT_EQ (val2.get_address (), storage_a); +} + +void +data_value_set_at_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_storage *storage_a = ctx.find_reachable_var (a); + data_storage *storage_b = ctx.find_reachable_var (b); + + data_value val1(ctx, ptr_type_node); + + val1.set_address (*storage_a); + + tree vec2ptr = build_vector_type (ptr_type_node, 2); + data_value val2(ctx, vec2ptr); + + val2.set_at (val1, HOST_BITS_PER_PTR); + ASSERT_EQ (val2.classify (HOST_BITS_PER_PTR, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val2.get_address_at (HOST_BITS_PER_PTR), storage_a); + + val1.set_address (*storage_b); + + val2.set_at (val1, 0); + ASSERT_EQ (val2.classify (0, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val2.get_address_at (0), storage_b); + + data_value val3(ctx, vec2ptr); + val3.set_at (val2, 0); + + ASSERT_EQ (val3.classify (0, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val3.get_address_at (0), storage_b); + ASSERT_EQ (val3.classify (HOST_BITS_PER_PTR, HOST_BITS_PER_PTR), VAL_ADDRESS); + ASSERT_EQ (val3.get_address_at (HOST_BITS_PER_PTR), storage_a); + + tree derived = make_node (RECORD_TYPE); + tree field2 = build_decl (input_location, FIELD_DECL, + get_identifier ("field2"), integer_type_node); + DECL_CONTEXT (field2) = derived; + DECL_CHAIN (field2) = NULL_TREE; + tree field1 = build_decl (input_location, FIELD_DECL, + get_identifier ("field1"), integer_type_node); + DECL_CONTEXT (field1) = derived; + DECL_CHAIN (field1) = field2; + TYPE_FIELDS (derived) = field1; + layout_type (derived); + + tree c = create_var (derived, "c"); + + context_builder builder2 {}; + builder2.add_decls (&decls); + exec_context ctx2 = builder2.build (nullptr, printer); + vec<tree> decls2{}; + decls2.safe_push (c); + + data_value val_derived(ctx, derived); + + ASSERT_EQ (val_derived.classify (), VAL_UNDEFINED); + + wide_int cst = wi::shwi (13, HOST_BITS_PER_INT); + val_derived.set_cst_at (cst, 0); + + ASSERT_EQ (val_derived.classify (), VAL_MIXED); + ASSERT_EQ (val_derived.classify (0, HOST_BITS_PER_INT), VAL_CONSTANT); + wide_int wi_val = val_derived.get_cst_at (0, HOST_BITS_PER_INT); + ASSERT_TRUE (wi::fits_shwi_p (wi_val)); + ASSERT_EQ (wi_val.to_shwi (), 13); + + + data_value vv (ctx2, integer_type_node); + wide_int wi23 = wi::shwi (23, HOST_BITS_PER_INT); + vv.set_cst (wi23); + + data_value vv2 (ctx2, derived); + vv2.set_at (vv, HOST_BITS_PER_INT); + + ASSERT_EQ (vv2.classify (), VAL_MIXED); + + ASSERT_EQ (vv2.classify (0, HOST_BITS_PER_INT), VAL_UNDEFINED); + ASSERT_EQ (vv2.classify (HOST_BITS_PER_INT, HOST_BITS_PER_INT), VAL_CONSTANT); + wide_int wi_field2 = vv2.get_cst_at (HOST_BITS_PER_INT, HOST_BITS_PER_INT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_field2); + ASSERT_EQ (wi_field2.to_shwi (), 23); + + + tree c12 = build_array_type_nelts (char_type_node, 12); + + data_value v (ctx2, c12); + + wide_int wi33 = wi::shwi (33, CHAR_BIT); + v.set_cst_at (wi33, 9 * CHAR_BIT); + + data_value v2 (ctx2, c12); + v2.set_at (9 * CHAR_BIT, CHAR_BIT, v, 9 * CHAR_BIT); + + ASSERT_EQ (v2.classify (), VAL_MIXED); + + ASSERT_EQ (v2.classify (8 * CHAR_BIT, CHAR_BIT), VAL_UNDEFINED); + ASSERT_EQ (v2.classify (10 * CHAR_BIT, CHAR_BIT), VAL_UNDEFINED); + ASSERT_EQ (v2.classify (9 * CHAR_BIT, CHAR_BIT), VAL_CONSTANT); + wide_int wi_c9 = v2.get_cst_at (9 * CHAR_BIT, CHAR_BIT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_c9); + ASSERT_EQ (wi_c9.to_shwi (), 33); + + data_value v3 (ctx2, c12); + v3.set (v); + + ASSERT_EQ (v3.classify (), VAL_MIXED); + + ASSERT_EQ (v3.classify (8 * CHAR_BIT, CHAR_BIT), VAL_UNDEFINED); + ASSERT_EQ (v3.classify (10 * CHAR_BIT, CHAR_BIT), VAL_UNDEFINED); + ASSERT_EQ (v3.classify (9 * CHAR_BIT, CHAR_BIT), VAL_CONSTANT); + wide_int wi_c9_bis = v3.get_cst_at (9 * CHAR_BIT, CHAR_BIT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_c9_bis); + ASSERT_EQ (wi_c9_bis.to_shwi (), 33); + + + tree mixed = make_node (RECORD_TYPE); + tree i3 = build_decl (input_location, FIELD_DECL, + get_identifier ("i3"), integer_type_node); + DECL_CONTEXT (i3) = mixed; + DECL_CHAIN (i3) = NULL_TREE; + tree p2 = build_decl (input_location, FIELD_DECL, + get_identifier ("p2"), ptr_type_node); + DECL_CONTEXT (p2) = mixed; + DECL_CHAIN (p2) = i3; + tree i1 = build_decl (input_location, FIELD_DECL, + get_identifier ("i1"), long_integer_type_node); + DECL_CONTEXT (i1) = mixed; + DECL_CHAIN (i1) = p2; + TYPE_FIELDS (mixed) = i1; + layout_type (mixed); + + tree t = create_var (integer_type_node, "t"); + + vec<tree> decls4{}; + decls4.safe_push (t); + + context_builder builder4 {}; + builder4.add_decls (&decls4); + exec_context ctx4 = builder4.build (nullptr, printer); + + data_value mv (ctx4, mixed); + + wide_int wi4 = wi::shwi (4, HOST_BITS_PER_LONG); + mv.set_cst_at (wi4, 0); + + data_storage *storage = ctx4.find_reachable_var (t); + gcc_assert (storage != nullptr); + mv.set_address_at (*storage, HOST_BITS_PER_LONG); + + wide_int wi7 = wi::shwi (7, HOST_BITS_PER_INT); + mv.set_cst_at (wi7, HOST_BITS_PER_LONG + HOST_BITS_PER_PTR); + + data_value mv2 (ctx4, mixed); + mv2.set (mv); + + ASSERT_EQ (mv2.classify (), VAL_MIXED); + + ASSERT_EQ (mv2.classify (0, HOST_BITS_PER_LONG), VAL_CONSTANT); + wide_int wi_i1 = mv2.get_cst_at (0, HOST_BITS_PER_LONG); + ASSERT_PRED1 (wi::fits_shwi_p, wi_i1); + ASSERT_EQ (wi_i1.to_shwi (), 4); + + ASSERT_EQ (mv2.classify (HOST_BITS_PER_LONG, HOST_BITS_PER_PTR), VAL_ADDRESS); + data_storage *storage2 = mv2.get_address_at (HOST_BITS_PER_LONG); + gcc_assert (storage2 != nullptr); + ASSERT_EQ (storage2->get_type (), STRG_VARIABLE); + ASSERT_EQ (storage2->get_variable (), t); + + ASSERT_EQ (mv2.classify (HOST_BITS_PER_LONG + HOST_BITS_PER_PTR, + HOST_BITS_PER_INT), + VAL_CONSTANT); + wide_int wi_i3 = mv2.get_cst_at (HOST_BITS_PER_LONG + HOST_BITS_PER_PTR, + HOST_BITS_PER_INT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_i3); + ASSERT_EQ (wi_i3.to_shwi (), 7); +} + +void +data_value_print_tests () +{ + context_printer printer; + pretty_printer & pp = printer.pp; + + tree my_var = create_var (integer_type_node, "my_var"); + + vec<tree> decls{}; + decls.safe_push (my_var); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_value val1(ctx, ptr_type_node); + data_storage *storage = ctx.find_reachable_var (my_var); + val1.set_address (*storage); + + val1.print (pp, ptr_type_node); + ASSERT_STREQ (pp_formatted_text (&pp), "&my_var"); + + + context_printer printer2; + pretty_printer & pp2 = printer2.pp; + + tree y = create_var (ptr_type_node, "y"); + tree my_lhs = create_var (ptr_type_node, "my_lhs"); + + vec<tree> decls2{}; + decls2.safe_push (my_var); + decls2.safe_push (y); + decls2.safe_push (my_lhs); + + context_builder builder2 {}; + builder2.add_decls (&decls2); + exec_context ctx2 = builder2.build (nullptr, printer2); + + tree vec2ptr = build_vector_type (ptr_type_node, 2); + data_value val2(ctx2, vec2ptr); + data_storage *strg_my_var = ctx2.find_reachable_var (my_var); + val2.set_address_at (*strg_my_var, 0); + data_storage *strg_x = ctx2.find_reachable_var (y); + val2.set_address_at (*strg_x, HOST_BITS_PER_PTR); + + val2.print (pp2, vec2ptr); + const char *str2 = pp_formatted_text (&pp2); + ASSERT_STREQ (str2, "{ &my_var, &y }"); + + context_printer printer3; + pretty_printer & pp3 = printer3.pp; + + exec_context ctx3 = context_builder ().build (nullptr, printer3); + + data_value val_int(ctx3, integer_type_node); + + wide_int wi_val = wi::shwi (17, HOST_BITS_PER_INT); + val_int.set_cst (wi_val); + + val_int.print (pp3, integer_type_node); + + ASSERT_STREQ (pp_formatted_text (&pp3), "17"); + + + context_printer printer4; + pretty_printer & pp4 = printer4.pp; + + exec_context ctx4 = context_builder ().build (nullptr, printer4); + + data_storage *alloc1 = ctx4.allocate (12); + gcc_assert (alloc1 != nullptr); + + data_value val_ptr(ctx4, ptr_type_node); + val_ptr.set_address (*alloc1); + + val_ptr.print (pp4, ptr_type_node); + + ASSERT_STREQ (pp_formatted_text (&pp4), "&<alloc00(12)>"); + + + context_printer printer5; + pretty_printer & pp5 = printer5.pp; + + exec_context ctx5 = context_builder ().build (nullptr, printer5); + + data_storage *alloc1_ctx5 = ctx5.allocate (12); + gcc_assert (alloc1_ctx5 != nullptr); + + data_storage *alloc2_ctx5 = ctx5.allocate (17); + gcc_assert (alloc2_ctx5 != nullptr); + + data_value val_ptr2(ctx5, ptr_type_node); + val_ptr.set_address (*alloc2_ctx5); + + val_ptr.print (pp5, ptr_type_node); + + ASSERT_STREQ (pp_formatted_text (&pp5), "&<alloc01(17)>"); + + + context_printer printer6; + pretty_printer & pp6 = printer6.pp; + + exec_context ctx6 = context_builder ().build (nullptr, printer6); + + data_value val6_259(ctx6, short_integer_type_node); + wide_int cst259 = wi::shwi (259, HOST_BITS_PER_SHORT); + val6_259.set_cst (cst259); + + val6_259.print (pp6, char_type_node); + + ASSERT_STREQ (pp_formatted_text (&pp6), "3"); + + + context_printer printer7; + pretty_printer & pp7 = printer7.pp; + + exec_context ctx7 = context_builder ().build (nullptr, printer7); + + data_value val7_259(ctx7, short_integer_type_node); + val7_259.set_cst (cst259); + + val7_259.print_at (pp7, char_type_node, CHAR_BIT); + + ASSERT_STREQ (pp_formatted_text (&pp7), "1"); + + + context_printer printer8; + pretty_printer & pp8 = printer8.pp; + + exec_context ctx8 = context_builder ().build (nullptr, printer8); + data_storage * strg = ctx8.allocate (10); + + data_value v = strg->get_value (); + wide_int cst41 = wi::shwi (41, CHAR_BIT); + v.set_cst_at (cst41, HOST_BITS_PER_PTR); + + v.print_at (pp8, char_type_node, HOST_BITS_PER_PTR, CHAR_BIT); + + ASSERT_STREQ (pp_formatted_text (&pp8), "41"); + + + context_printer printer9; + pretty_printer & pp9 = printer9.pp; + + exec_context ctx9 = context_builder ().build (nullptr, printer9); + + tree real2 = build_real (float_type_node, dconst2); + tree sint = make_signed_type (TYPE_PRECISION (float_type_node)); + tree int_r2 = fold_build1 (VIEW_CONVERT_EXPR, sint, real2); + wide_int wi_r2 = wi::to_wide (int_r2); + + data_value v9 (ctx9, float_type_node); + v9.set_cst (wi_r2); + + v9.print (pp9, float_type_node); + + ASSERT_STREQ (pp_formatted_text (&pp9), "2.0e+0"); +} + + +void +context_printer_print_first_data_ref_part_tests () +{ + vec<tree> empty{}; + + tree der2i = make_node (RECORD_TYPE); + tree der2i_i2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2i_i2"), integer_type_node); + DECL_CONTEXT (der2i_i2) = der2i; + DECL_CHAIN (der2i_i2) = NULL_TREE; + tree der2i_i1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2i_i1"), integer_type_node); + DECL_CONTEXT (der2i_i1) = der2i; + DECL_CHAIN (der2i_i1) = der2i_i2; + TYPE_FIELDS (der2i) = der2i_i1; + layout_type (der2i); + + tree var2i = create_var (der2i, "var2i"); + + context_printer printer1; + pretty_printer & pp1 = printer1.pp; + exec_context ctx1 = context_builder ().build (nullptr, printer1); + + tree res1 = printer1.print_first_data_ref_part (ctx1, var2i, 0); + + ASSERT_EQ (res1, der2i); + const char * str1 = pp_formatted_text (&pp1); + ASSERT_STREQ (str1, "var2i"); + + + context_printer printer2; + pretty_printer & pp2 = printer2.pp; + + vec<tree> decls2{}; + decls2.safe_push (var2i); + + context_builder builder2 {}; + builder2.add_decls (&decls2); + exec_context ctx2 = builder2.build (nullptr, printer2); + + tree mem_var2i = build2 (MEM_REF, der2i, + build1 (ADDR_EXPR, ptr_type_node, var2i), + build_zero_cst (ptr_type_node)); + + tree res2 = printer2.print_first_data_ref_part (ctx2, mem_var2i, 0); + + ASSERT_EQ (res2, der2i); + const char * str2 = pp_formatted_text (&pp2); + ASSERT_STREQ (str2, "var2i"); + + + context_printer printer3; + pretty_printer & pp3 = printer3.pp; + + context_builder builder3 {}; + builder3.add_decls (&decls2); + exec_context ctx3 = builder3.build (nullptr, printer3); + + tree long_var2i = build2 (MEM_REF, long_integer_type_node, + build1 (ADDR_EXPR, ptr_type_node, var2i), + build_zero_cst (ptr_type_node)); + + tree res3 = printer3.print_first_data_ref_part (ctx3, long_var2i, 0); + + ASSERT_EQ (res3, integer_type_node); + const char * str3 = pp_formatted_text (&pp3); + ASSERT_STREQ (str3, "var2i.der2i_i1"); + + + tree der2s = make_node (RECORD_TYPE); + tree der2s_s2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2s_s2"), + short_integer_type_node); + DECL_CONTEXT (der2s_s2) = der2s; + DECL_CHAIN (der2s_s2) = NULL_TREE; + tree der2s_s1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2s_s1"), + short_integer_type_node); + DECL_CONTEXT (der2s_s1) = der2s; + DECL_CHAIN (der2s_s1) = der2s_s2; + TYPE_FIELDS (der2s) = der2s_s1; + layout_type (der2s); + + + tree der1d1i = make_node (RECORD_TYPE); + tree der1d1i_i2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1d1i_i2"), integer_type_node); + DECL_CONTEXT (der1d1i_i2) = der1d1i; + DECL_CHAIN (der1d1i_i2) = NULL_TREE; + tree der1d1i_d1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1d1i_d1"), der2s); + DECL_CONTEXT (der1d1i_d1) = der1d1i; + DECL_CHAIN (der1d1i_d1) = der1d1i_i2; + TYPE_FIELDS (der1d1i) = der1d1i_d1; + layout_type (der1d1i); + + tree var1d1i = create_var (der1d1i, "var1d1i"); + + context_printer printer4; + pretty_printer & pp4 = printer4.pp; + + vec<tree> decls4{}; + decls4.safe_push (var1d1i); + + context_builder builder4 {}; + builder4.add_decls (&decls4); + exec_context ctx4 = builder4.build (nullptr, printer4); + + tree mem_var1d1i = build2 (MEM_REF, long_integer_type_node, + build1 (ADDR_EXPR, ptr_type_node, var1d1i), + build_zero_cst (ptr_type_node)); + + tree res4 = printer4.print_first_data_ref_part (ctx4, mem_var1d1i, 0); + + ASSERT_EQ (res4, short_integer_type_node); + const char * str4 = pp_formatted_text (&pp4); + ASSERT_STREQ (str4, "var1d1i.der1d1i_d1.der2s_s1"); + + + context_printer printer5; + pretty_printer & pp5 = printer5.pp; + + context_builder builder5 {}; + builder5.add_decls (&decls4); + exec_context ctx5 = builder5.build (nullptr, printer5); + + tree mem_var1d1i_s2 = build2 (MEM_REF, short_integer_type_node, + build1 (ADDR_EXPR, ptr_type_node, var1d1i), + build_int_cst (ptr_type_node, + sizeof (short))); + + tree res5 = printer5.print_first_data_ref_part (ctx5, mem_var1d1i_s2, 0); + + ASSERT_EQ (res5, short_integer_type_node); + const char * str5 = pp_formatted_text (&pp5); + ASSERT_STREQ (str5, "var1d1i.der1d1i_d1.der2s_s2"); + + + tree der4c = make_node (RECORD_TYPE); + tree der4c_c4 = build_decl (input_location, FIELD_DECL, + get_identifier ("der4c_c4"), + char_type_node); + DECL_CONTEXT (der4c_c4) = der4c; + DECL_CHAIN (der4c_c4) = NULL_TREE; + tree der4c_c3 = build_decl (input_location, FIELD_DECL, + get_identifier ("der4c_c3"), + char_type_node); + DECL_CONTEXT (der4c_c3) = der4c; + DECL_CHAIN (der4c_c3) = der4c_c4; + tree der4c_c2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der4c_c2"), + char_type_node); + DECL_CONTEXT (der4c_c2) = der4c; + DECL_CHAIN (der4c_c2) = der4c_c3; + tree der4c_c1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der4c_c1"), + char_type_node); + DECL_CONTEXT (der4c_c1) = der4c; + DECL_CHAIN (der4c_c1) = der4c_c2; + TYPE_FIELDS (der4c) = der4c_c1; + layout_type (der4c); + + tree var4c = create_var (der4c, "var4c"); + + context_printer printer6; + pretty_printer & pp6 = printer6.pp; + + vec<tree> decls6{}; + decls6.safe_push (var4c); + + context_builder builder6 {}; + builder6.add_decls (&decls6); + exec_context ctx6 = builder6.build (nullptr, printer6); + + tree mem_var4c = build2 (MEM_REF, long_integer_type_node, + build1 (ADDR_EXPR, ptr_type_node, var4c), + build_int_cst (ptr_type_node, 2)); + + tree res6 = printer6.print_first_data_ref_part (ctx6, mem_var4c, 0); + + ASSERT_EQ (res6, char_type_node); + const char * str6 = pp_formatted_text (&pp6); + ASSERT_STREQ (str6, "var4c.der4c_c3"); + + + tree der1i1d = make_node (RECORD_TYPE); + tree der1i1d_d2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1i1d_d2"), + der4c); + DECL_CONTEXT (der1i1d_d2) = der1i1d; + DECL_CHAIN (der1i1d_d2) = NULL_TREE; + tree der1i1d_i1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1i1d_i1"), + integer_type_node); + DECL_CONTEXT (der1i1d_i1) = der1i1d; + DECL_CHAIN (der1i1d_i1) = der1i1d_d2; + TYPE_FIELDS (der1i1d) = der1i1d_i1; + layout_type (der1i1d); + + tree var1i1d = create_var (der1i1d, "var1i1d"); + + context_printer printer7; + pretty_printer & pp7 = printer7.pp; + + vec<tree> decls7{}; + decls7.safe_push (var1i1d); + + context_builder builder7 {}; + builder7.add_decls (&decls7); + exec_context ctx7 = builder7.build (nullptr, printer7); + + tree mem_var1i1d = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, var1i1d), + build_int_cst (ptr_type_node, + sizeof (int) + 1)); + + tree res7 = printer7.print_first_data_ref_part (ctx7, mem_var1i1d, 0); + + ASSERT_EQ (res7, char_type_node); + const char * str7 = pp_formatted_text (&pp7); + ASSERT_STREQ (str7, "var1i1d.der1i1d_d2.der4c_c2"); + + + tree i5 = build_array_type_nelts (integer_type_node, 5); + + tree var_i5 = create_var (i5, "var_i5"); + + context_printer printer8; + pretty_printer & pp8 = printer8.pp; + + vec<tree> decls8{}; + decls8.safe_push (var_i5); + + context_builder builder8 {}; + builder8.add_decls (&decls8); + exec_context ctx8 = builder8.build (nullptr, printer8); + + tree mem_var_i5 = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, var_i5), + build_int_cst (ptr_type_node, + 3 * sizeof (int))); + + tree res8 = printer8.print_first_data_ref_part (ctx8, mem_var_i5, 0); + + ASSERT_EQ (res8, integer_type_node); + const char * str8 = pp_formatted_text (&pp8); + ASSERT_STREQ (str8, "var_i5[3]"); + + + context_printer printer9; + pretty_printer & pp9 = printer9.pp; + + context_builder builder9 {}; + builder9.add_decls (&decls8); + exec_context ctx9 = builder9.build (nullptr, printer9); + + tree mem2_var_i5 = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, var_i5), + build_int_cst (ptr_type_node, + sizeof (int))); + + tree res9 = printer9.print_first_data_ref_part (ctx9, mem2_var_i5, + HOST_BITS_PER_INT); + + ASSERT_EQ (res9, integer_type_node); + const char * str9 = pp_formatted_text (&pp9); + ASSERT_STREQ (str9, "var_i5[2]"); + + + tree a5c4 = build_array_type_nelts (der4c, 5); + + tree der1i1a5d = make_node (RECORD_TYPE); + tree der1i1a5d_a5d2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1i1a5d_a5d2"), + a5c4); + DECL_CONTEXT (der1i1a5d_a5d2) = der1i1a5d; + DECL_CHAIN (der1i1a5d_a5d2) = NULL_TREE; + tree der1i1a5d_i1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der1i1a5d_i1"), + integer_type_node); + DECL_CONTEXT (der1i1a5d_i1) = der1i1a5d; + DECL_CHAIN (der1i1a5d_i1) = der1i1a5d_a5d2; + TYPE_FIELDS (der1i1a5d) = der1i1a5d_i1; + layout_type (der1i1a5d); + + tree var_d1i1a5d = create_var (der1i1a5d, "var_d1i1a5d"); + + context_printer printer10; + pretty_printer & pp10 = printer10.pp; + + vec<tree> decls10{}; + decls10.safe_push (var_d1i1a5d); + + context_builder builder10 {}; + builder10.add_decls (&decls10); + exec_context ctx10 = builder10.build (nullptr, printer10); + + tree mem_var_d1i1a5d = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, var_d1i1a5d), + build_int_cst (ptr_type_node, + sizeof (int) + 13)); + + tree res10 = printer10.print_first_data_ref_part (ctx10, mem_var_d1i1a5d, 0); + + ASSERT_EQ (res10, char_type_node); + const char * str10 = pp_formatted_text (&pp10); + ASSERT_STREQ (str10, "var_d1i1a5d.der1i1a5d_a5d2[3].der4c_c2"); + + + tree var_i = create_var (integer_type_node, "var_i"); + tree ptr = create_var (ptr_type_node, "ptr"); + + context_printer printer11; + pretty_printer & pp11 = printer11.pp; + + vec<tree> decls11{}; + decls11.safe_push (var_i); + decls11.safe_push (ptr); + + context_builder builder11 {}; + builder11.add_decls (&decls11); + exec_context ctx11 = builder11.build (nullptr, printer11); + + data_storage *var_storage = ctx11.find_reachable_var (var_i); + + data_value ptr_val (ctx11, ptr_type_node); + ptr_val.set_address (*var_storage); + + data_storage *ptr_storage = ctx11.find_reachable_var (ptr); + ptr_storage->set (ptr_val); + + tree ref_ptr = build2 (MEM_REF, integer_type_node, ptr, + build_zero_cst (ptr_type_node)); + + tree res11 = printer11.print_first_data_ref_part (ctx11, ref_ptr, 0); + + ASSERT_EQ (res11, integer_type_node); + const char * str11 = pp_formatted_text (&pp11); + ASSERT_STREQ (str11, "var_i"); +} + + +void +context_printer_print_value_update_tests () +{ + context_printer printer; + pretty_printer & pp = printer.pp; + pp_buffer (&pp)->m_flush_p = false; + + tree my_var = create_var (ptr_type_node, "my_var"); + tree y = create_var (ptr_type_node, "y"); + tree my_lhs = create_var (ptr_type_node, "my_lhs"); + + vec<tree> decls{}; + decls.safe_push (my_var); + decls.safe_push (y); + decls.safe_push (my_lhs); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_value val1(ctx, ptr_type_node); + data_storage *storage = ctx.find_reachable_var (my_var); + val1.set_address (*storage); + + printer.print_value_update (ctx, my_lhs, val1); + const char *str = pp_formatted_text (&pp); + ASSERT_STREQ (str, "# my_lhs = &my_var\n"); + + context_printer printer2; + pretty_printer & pp2 = printer2.pp; + pp_buffer (&pp2)->m_flush_p = false; + + exec_context ctx2 = builder.build (nullptr, printer2); + + tree vec2ptr = build_vector_type (ptr_type_node, 2); + data_value val2(ctx2, vec2ptr); + data_storage *strg_my_var = ctx2.find_reachable_var (my_var); + val2.set_address_at (*strg_my_var, 0); + data_storage *strg_x = ctx2.find_reachable_var (y); + val2.set_address_at (*strg_x, HOST_BITS_PER_PTR); + + tree vec_lhs = create_var (vec2ptr, "vec_lhs"); + + printer2.print_value_update (ctx2, vec_lhs, val2); + const char *str2 = pp_formatted_text (&pp2); + ASSERT_STREQ (str2, "# vec_lhs = { &my_var, &y }\n"); + + + context_printer printer3; + pretty_printer & pp3 = printer3.pp; + pp_buffer (&pp3)->m_flush_p = false; + + tree der2c = make_node (RECORD_TYPE); + tree der2c_c2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2c_c2"), + char_type_node); + DECL_CONTEXT (der2c_c2) = der2c; + DECL_CHAIN (der2c_c2) = NULL_TREE; + tree der2c_c1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2c_c1"), + char_type_node); + DECL_CONTEXT (der2c_c1) = der2c; + DECL_CHAIN (der2c_c1) = der2c_c2; + TYPE_FIELDS (der2c) = der2c_c1; + layout_type (der2c); + + tree var2c = create_var (der2c, "var2c"); + + vec<tree> decls3{}; + decls3.safe_push (var2c); + + context_builder builder3 {}; + builder3.add_decls (&decls3); + exec_context ctx3 = builder3.build (nullptr, printer3); + + tree mem_var2c = build2 (MEM_REF, short_integer_type_node, + build1 (ADDR_EXPR, ptr_type_node, var2c), + build_int_cst (ptr_type_node, 0)); + + data_value val259 (ctx3, short_integer_type_node); + wide_int wi259 = wi::shwi (259, HOST_BITS_PER_SHORT); + val259.set_cst (wi259); + + printer3.print_value_update (ctx3, mem_var2c, val259); + + const char *str3 = pp_formatted_text (&pp3); + ASSERT_STREQ (str3, "# var2c.der2c_c1 = 3\n# var2c.der2c_c2 = 1\n"); + + + context_printer printer4; + pretty_printer & pp4 = printer4.pp; + pp_buffer (&pp4)->m_flush_p = false; + + tree der2i = make_node (RECORD_TYPE); + tree der2i_i2 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2i_i2"), integer_type_node); + DECL_CONTEXT (der2i_i2) = der2i; + DECL_CHAIN (der2i_i2) = NULL_TREE; + tree der2i_i1 = build_decl (input_location, FIELD_DECL, + get_identifier ("der2i_i1"), integer_type_node); + DECL_CONTEXT (der2i_i1) = der2i; + DECL_CHAIN (der2i_i1) = der2i_i2; + TYPE_FIELDS (der2i) = der2i_i1; + layout_type (der2i); + + tree v2i = create_var (der2i, "v2i"); + + vec<tree> decls4{}; + decls4.safe_push (v2i); + + context_builder builder4 {}; + builder4.add_decls (&decls4); + exec_context ctx4 = builder4.build (nullptr, printer4); + + tree vec2i = build_vector_type (integer_type_node, 2); + + tree mem_v2i = build2 (MEM_REF, vec2i, + build1 (ADDR_EXPR, ptr_type_node, v2i), + build_int_cst (ptr_type_node, 0)); + + data_value val2i = data_value (ctx4, vec2i); + wide_int cst2 = wi::shwi (2, HOST_BITS_PER_INT); + wide_int cst11 = wi::shwi (11, HOST_BITS_PER_INT); + val2i.set_cst_at (cst2, 0); + val2i.set_cst_at (cst11, HOST_BITS_PER_INT); + + printer4.print_value_update (ctx4, mem_v2i, val2i); + + const char *str4 = pp_formatted_text (&pp4); + ASSERT_STREQ (str4, "# v2i.der2i_i1 = 2\n# v2i.der2i_i2 = 11\n"); +} + + +void +exec_context_evaluate_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + tree int_ptr = build_pointer_type (integer_type_node); + tree var_addr = build1 (ADDR_EXPR, int_ptr, a); + + data_value val = ctx.evaluate (var_addr); + data_storage *strg_ptr = val.get_address (); + ASSERT_NE (strg_ptr, nullptr); + ASSERT_PRED1 (strg_ptr->matches, a); + + exec_context ctx2 = context_builder ().build (&ctx, printer); + + data_value val2 = ctx2.evaluate (var_addr); + data_storage *strg_ptr2 = val2.get_address (); + ASSERT_NE (strg_ptr, nullptr); + ASSERT_PRED1 (strg_ptr2->matches, a); + + + data_storage *strg_a = ctx.find_reachable_var (a); + gcc_assert (strg_a != nullptr); + data_value tmp22 (ctx, integer_type_node); + wide_int wi22 = wi::shwi (22, HOST_BITS_PER_INT); + tmp22.set_cst (wi22); + strg_a->set (tmp22); + + data_value val_a = ctx.evaluate (a); + + ASSERT_EQ (val_a.classify (), VAL_CONSTANT); + wide_int wi_a = val_a.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_a); + ASSERT_EQ (wi_a.to_shwi (), 22); + + + tree cst33 = build_int_cst (integer_type_node, 33); + + data_value val_33 = ctx.evaluate (cst33); + + ASSERT_EQ (val_33.get_bitwidth (), HOST_BITS_PER_INT); + ASSERT_EQ (val_33.classify (), VAL_CONSTANT); + wide_int wi33 = val_33.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi33); + ASSERT_EQ (wi33.to_shwi (), 33); + + + vec<constructor_elt, va_gc> * vec_elts = nullptr; + tree cst2 = build_int_cst (integer_type_node, 2); + CONSTRUCTOR_APPEND_ELT (vec_elts, NULL_TREE, cst2); + tree cst11 = build_int_cst (integer_type_node, 11); + CONSTRUCTOR_APPEND_ELT (vec_elts, NULL_TREE, cst11); + + tree vec2int = build_vector_type (integer_type_node, 2); + tree v = build_vector_from_ctor (vec2int, vec_elts); + + data_value val_v = ctx.evaluate (v); + + ASSERT_EQ (val_v.get_bitwidth (), 64); + ASSERT_EQ (val_v.classify (), VAL_CONSTANT); + wide_int low_part = val_v.get_cst_at (0, HOST_BITS_PER_INT); + ASSERT_PRED1 (wi::fits_shwi_p, low_part); + ASSERT_EQ (low_part.to_shwi (), 2); + wide_int high_part = val_v.get_cst_at (HOST_BITS_PER_INT, HOST_BITS_PER_INT); + ASSERT_PRED1 (wi::fits_shwi_p, high_part); + ASSERT_EQ (high_part.to_shwi (), 11); + + + vec<constructor_elt, va_gc> * vec_elts2 = nullptr; + tree cstr2 = build_real (double_type_node, dconst2); + CONSTRUCTOR_APPEND_ELT (vec_elts2, NULL_TREE, cstr2); + tree cstm1 = build_real (double_type_node, dconstm1); + CONSTRUCTOR_APPEND_ELT (vec_elts2, NULL_TREE, cstm1); + + tree vec2float = build_vector_type (float_type_node, 2); + tree v2 = build_vector_from_ctor (vec2float, vec_elts2); + + data_value val_v2 = ctx.evaluate (v2); + + ASSERT_EQ (val_v2.get_bitwidth (), 64); + ASSERT_EQ (val_v2.classify (), VAL_CONSTANT); + + tree sint = make_signed_type (TYPE_PRECISION (float_type_node)); + +#define HOST_BITS_PER_FLOAT (sizeof (float) * CHAR_BIT) + wide_int low_wi = val_v2.get_cst_at (0, HOST_BITS_PER_FLOAT); + tree low_int = wide_int_to_tree (sint, low_wi); + tree low_float = fold_build1 (VIEW_CONVERT_EXPR, float_type_node, low_int); + ASSERT_EQ (TREE_CODE (low_float), REAL_CST); + ASSERT_TRUE (real_identical (TREE_REAL_CST_PTR (low_float), &dconst2)); + + wide_int high_wi = val_v2.get_cst_at (HOST_BITS_PER_FLOAT, HOST_BITS_PER_FLOAT); + tree high_int = wide_int_to_tree (sint, high_wi); + tree high_float = fold_build1 (VIEW_CONVERT_EXPR, float_type_node, high_int); + ASSERT_EQ (TREE_CODE (high_float), REAL_CST); + ASSERT_TRUE (real_identical (TREE_REAL_CST_PTR (high_float), &dconstm1)); +#undef HOST_BITS_PER_FLOAT + + + tree a5i = build_array_type_nelts (integer_type_node, 5); + tree v5i = create_var (a5i, "v5i"); + + vec<tree> decls2{}; + decls2.safe_push (v5i); + + context_builder builder3 {}; + builder3.add_decls (&decls2); + exec_context ctx3 = builder3.build (nullptr, printer); + + wide_int cst18 = wi::shwi (18, HOST_BITS_PER_INT); + + data_value value (ctx3, a5i); + value.set_cst_at (cst18, 3 * HOST_BITS_PER_INT); + + data_storage *storage = ctx3.find_reachable_var (v5i); + gcc_assert (storage != nullptr); + storage->set (value); + + tree v5ref = build4 (ARRAY_REF, integer_type_node, v5i, + build_int_cst (integer_type_node, 3), + NULL_TREE, NULL_TREE); + + data_value evaluation = ctx3.evaluate (v5ref); + + ASSERT_EQ (evaluation.get_bitwidth(), HOST_BITS_PER_INT); + ASSERT_EQ (evaluation.classify (), VAL_CONSTANT); + wide_int wi_val = evaluation.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val); + ASSERT_EQ (wi_val.to_shwi (), 18); + + + tree derived = make_node (RECORD_TYPE); + tree field2 = build_decl (input_location, FIELD_DECL, + get_identifier ("field2"), integer_type_node); + DECL_CONTEXT (field2) = derived; + DECL_CHAIN (field2) = NULL_TREE; + tree field1 = build_decl (input_location, FIELD_DECL, + get_identifier ("field1"), integer_type_node); + DECL_CONTEXT (field1) = derived; + DECL_CHAIN (field1) = field2; + TYPE_FIELDS (derived) = field1; + layout_type (derived); + + tree a3d = build_array_type_nelts (derived, 3); + tree v3d = create_var (a3d, "v3d"); + + vec<tree> decls3{}; + decls3.safe_push (v3d); + + context_builder builder4 {}; + builder4.add_decls (&decls3); + exec_context ctx4 = builder4.build (nullptr, printer); + + wide_int cst15 = wi::shwi (15, HOST_BITS_PER_INT); + + data_value tmp (ctx4, a3d); + tmp.set_cst_at (cst15, 3 * HOST_BITS_PER_INT); + + data_storage *storage2 = ctx4.find_reachable_var (v3d); + gcc_assert (storage2 != nullptr); + storage2->set (tmp); + + tree v3aref = build4 (ARRAY_REF, derived, v3d, + build_int_cst (integer_type_node, 1), + NULL_TREE, NULL_TREE); + tree v3cref = build3 (COMPONENT_REF, integer_type_node, v3aref, + field2, NULL_TREE); + + data_value eval2 = ctx4.evaluate (v3cref); + + ASSERT_EQ (eval2.get_bitwidth(), HOST_BITS_PER_INT); + ASSERT_EQ (eval2.classify (), VAL_CONSTANT); + wide_int wi_val2 = eval2.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val2); + ASSERT_EQ (wi_val2.to_shwi (), 15); + + + tree func_type = build_function_type (void_type_node, NULL_TREE); + layout_type (func_type); + + tree func = build_decl (input_location, FUNCTION_DECL, + get_identifier ("func"), + func_type); + tree result = build_decl (input_location, RESULT_DECL, + get_identifier ("result"), void_type_node); + DECL_CONTEXT (result) = func; + DECL_RESULT (func) = result; + + init_lowered_empty_function (func, true, profile_count::one ()); + + tree def_var = create_var (integer_type_node, "def_var"); + DECL_CONTEXT (def_var) = func; + tree ssa_var = make_ssa_name_fn (DECL_STRUCT_FUNCTION (func), def_var, nullptr); + SSA_NAME_IS_DEFAULT_DEF (ssa_var) = 1; + + vec<tree> decls5{}; + decls5.safe_push (def_var); + decls5.safe_push (ssa_var); + + context_builder builder5 {}; + builder5.add_decls (&decls5); + exec_context ctx5 = builder5.build (nullptr, printer); + + wide_int cst14 = wi::shwi (14, HOST_BITS_PER_INT); + + data_value tmp14 (ctx5, integer_type_node); + tmp14.set_cst (cst14); + + data_storage *storage5 = ctx5.find_reachable_var (def_var); + gcc_assert (storage5 != nullptr); + storage5->set (tmp14); + + data_value eval5 = ctx5.evaluate (ssa_var); + + ASSERT_EQ (eval5.get_bitwidth(), HOST_BITS_PER_INT); + ASSERT_EQ (eval5.classify (), VAL_CONSTANT); + wide_int wi_val5 = eval5.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val5); + ASSERT_EQ (wi_val5.to_shwi (), 14); + + + tree a5c = build_array_type_nelts (char_type_node, 5); + tree v5c = create_var (a5c,"v5c"); + + vec<tree> decls6{}; + decls6.safe_push (v5c); + + context_builder builder6 {}; + builder6.add_decls (&decls6); + exec_context ctx6 = builder6.build (nullptr, printer); + + wide_int cst8 = wi::shwi (8, CHAR_BIT); + + data_value tmp8 (ctx6, a5c); + tmp8.set_cst_at (cst8, 3 * CHAR_BIT); + + data_storage *storage6 = ctx6.find_reachable_var (v5c); + gcc_assert (storage5 != nullptr); + storage6->set (tmp8); + + tree ref = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, v5c), + build_int_cst (ptr_type_node, 3)); + + data_value eval6 = ctx6.evaluate (ref); + + ASSERT_EQ (eval6.get_bitwidth(), CHAR_BIT); + ASSERT_EQ (eval6.classify (), VAL_CONSTANT); + wide_int wi_val6 = eval6.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val6); + ASSERT_EQ (wi_val6.to_shwi (), 8); +} + + +void +exec_context_evaluate_literal_tests () +{ + context_printer printer; + + vec<tree> empty{}; + vec<tree> empty2{}; + + exec_context ctx = context_builder ().build (nullptr, printer); + + tree cst = build_int_cst (integer_type_node, 13); + + data_value val = ctx.evaluate_literal (INTEGER_CST, cst); + ASSERT_EQ (val.classify (), VAL_CONSTANT); + wide_int wi_value = val.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_value); + int int_value = wi_value.to_shwi (); + ASSERT_EQ (int_value, 13); +} + +void +exec_context_evaluate_unary_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + tree b = create_var (integer_type_node, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + tree int_ptr = build_pointer_type (integer_type_node); + + tree vec2ptr = build_vector_type (ptr_type_node, 2); + tree addr1 = build1 (ADDR_EXPR, int_ptr, a); + tree addr2 = build1 (ADDR_EXPR, int_ptr, b); + vec<constructor_elt, va_gc> * vec_elts = nullptr; + CONSTRUCTOR_APPEND_ELT (vec_elts, NULL_TREE, addr1); + CONSTRUCTOR_APPEND_ELT (vec_elts, NULL_TREE, addr2); + tree cstr = build_constructor (vec2ptr, vec_elts); + + data_value val_cstr = ctx.evaluate_unary (CONSTRUCTOR, cstr); + data_storage *strg1 = val_cstr.get_address_at (0); + ASSERT_NE (strg1, nullptr); + ASSERT_PRED1 (strg1->matches, a); + data_storage *strg2 = val_cstr.get_address_at (HOST_BITS_PER_PTR); + ASSERT_NE (strg2, nullptr); + ASSERT_PRED1 (strg2->matches, b); +} + +void +exec_context_execute_assign_tests () +{ + context_printer printer; + + tree a = create_var (integer_type_node, "a"); + + tree derived = make_node (RECORD_TYPE); + tree field2 = build_decl (input_location, FIELD_DECL, + get_identifier ("field2"), integer_type_node); + DECL_CONTEXT (field2) = derived; + DECL_CHAIN (field2) = NULL_TREE; + tree field1 = build_decl (input_location, FIELD_DECL, + get_identifier ("field1"), integer_type_node); + DECL_CONTEXT (field1) = derived; + DECL_CHAIN (field1) = field2; + TYPE_FIELDS (derived) = field1; + layout_type (derived); + + tree b = create_var (derived, "b"); + + vec<tree> decls{}; + decls.safe_push (a); + decls.safe_push (b); + vec<tree> empty{}; + + context_builder builder {}; + builder.add_decls (&decls); + exec_context ctx = builder.build (nullptr, printer); + + data_storage *storage_a = ctx.find_reachable_var (a); + data_storage *storage_b = ctx.find_reachable_var (b); + + tree cst = build_int_cst (integer_type_node, 13); + + gimple *gassign1 = gimple_build_assign (a, cst); + + data_value val = storage_a->get_value (); + ASSERT_EQ (val.classify (), VAL_UNDEFINED); + + ctx.execute (gassign1); + + data_value val2 = storage_a->get_value (); + ASSERT_EQ (val2.classify (), VAL_CONSTANT); + wide_int wi_val2 = val2.get_cst (); + ASSERT_TRUE (wi::fits_shwi_p (wi_val2)); + ASSERT_EQ (wi_val2.to_shwi (), 13); + + + tree lhs = build3 (COMPONENT_REF, integer_type_node, b, field1, NULL_TREE); + + gimple *gassign2 = gimple_build_assign (lhs, cst); + + data_value val3 = storage_b->get_value (); + ASSERT_EQ (val3.classify (), VAL_UNDEFINED); + + ctx.execute (gassign2); + + data_value val4 = storage_b->get_value (); + ASSERT_EQ (val4.classify (), VAL_MIXED); + ASSERT_EQ (val4.classify (0, HOST_BITS_PER_INT), VAL_CONSTANT); + wide_int wi_val4 = val4.get_cst_at (0, HOST_BITS_PER_INT); + ASSERT_TRUE (wi::fits_shwi_p (wi_val4)); + ASSERT_EQ (wi_val4.to_shwi (), 13); + + tree lhs2 = build3 (COMPONENT_REF, integer_type_node, b, field2, NULL_TREE); + tree cst2 = build_int_cst (integer_type_node, 17); + gimple *gassign3 = gimple_build_assign (lhs2, cst2); + + data_value val5 = storage_b->get_value (); + ASSERT_EQ (val5.classify (), VAL_MIXED); + ASSERT_EQ (val5.classify (HOST_BITS_PER_INT, HOST_BITS_PER_INT), VAL_UNDEFINED); + + ctx.execute (gassign3); + + data_value val6 = storage_b->get_value (); + ASSERT_EQ (val6.classify (), VAL_CONSTANT); + wide_int wi_val6 = val6.get_cst_at (HOST_BITS_PER_INT, HOST_BITS_PER_INT); + ASSERT_TRUE (wi::fits_shwi_p (wi_val4)); + ASSERT_EQ (wi_val6.to_shwi (), 17); + + + tree ssa1 = make_node (SSA_NAME); + TREE_TYPE (ssa1) = integer_type_node; + + vec<tree> ssanames{}; + ssanames.safe_push (ssa1); + + context_builder builder2 {}; + builder2.add_decls (&decls); + builder2.add_decls (&ssanames); + exec_context ctx2 = builder2.build (nullptr, printer); + + tree i66 = build_int_cst (integer_type_node, 66); + gimple *gassign5 = gimple_build_assign (ssa1, i66); + + data_storage *ssa1_strg = ctx2.find_reachable_var (ssa1); + gcc_assert (ssa1_strg != nullptr); + data_value ssa1_val = ssa1_strg->get_value (); + + ASSERT_EQ (ssa1_val.classify (), VAL_UNDEFINED); + + ctx2.execute (gassign5); + + data_value ssa1_val2 = ssa1_strg->get_value (); + ASSERT_EQ (ssa1_val2.classify (), VAL_CONSTANT); + wide_int wi_ssa1_2 = ssa1_val2.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_ssa1_2); + ASSERT_EQ (wi_ssa1_2.to_shwi (), 66); + + + data_value cst66 (ctx2, integer_type_node); + wide_int wi66 = wi::shwi (66, HOST_BITS_PER_INT); + cst66.set_cst (wi66); + + ssa1_strg->set (cst66); + + gimple *gassign4 = gimple_build_assign (a, ssa1); + + data_storage *strg_a_ctx2 = ctx2.find_reachable_var (a); + gcc_assert (strg_a_ctx2 != nullptr); + data_value val_a = strg_a_ctx2->get_value (); + + ASSERT_EQ (val_a.classify (), VAL_UNDEFINED); + + ctx2.execute (gassign4); + + data_value val_a2 = strg_a_ctx2->get_value (); + ASSERT_EQ (val_a2.classify (), VAL_CONSTANT); + wide_int wi_a2 = val_a2.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_a2); + HOST_WIDE_INT hwi_a2 = wi_a2.to_shwi (); + ASSERT_EQ (hwi_a2, 66); + + + tree ptr = create_var (ptr_type_node, "ptr"); + + vec<tree> decls2{}; + decls2.safe_push (ptr); + + context_builder builder3 {}; + builder3.add_decls (&decls2); + exec_context ctx3 = builder3.build (nullptr, printer); + + data_storage *alloc1 = ctx3.allocate (12); + + data_storage *pointer = ctx3.find_reachable_var (ptr); + gcc_assert (pointer != nullptr); + data_value p (ctx3, ptr_type_node); + p.set_address (*alloc1); + pointer->set (p); + + tree ref = build2 (MEM_REF, integer_type_node, ptr, + build_int_cst (ptr_type_node, 0)); + tree cst3 = build_int_cst (integer_type_node, 123); + + gimple *assign = gimple_build_assign (ref, cst3); + + data_value val_alloc1 = alloc1->get_value (); + ASSERT_EQ (val_alloc1.classify (), VAL_UNDEFINED); + + ctx3.execute (assign); + + data_value val_alloc2 = alloc1->get_value (); + ASSERT_EQ (val_alloc2.classify (), VAL_MIXED); + ASSERT_EQ (val_alloc2.classify (0, HOST_BITS_PER_INT), VAL_CONSTANT); + wide_int wi_val_alloc2 = val_alloc2.get_cst_at (0, HOST_BITS_PER_INT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val_alloc2); + ASSERT_EQ (wi_val_alloc2.to_shwi (), 123); + + + tree u = create_var (integer_type_node, "u"); + + vec<tree> decls3{}; + decls3.safe_push (u); + + context_builder builder4 {}; + builder4.add_decls (&decls3); + exec_context ctx4 = builder4.build (nullptr, printer); + + data_storage *strg_u = ctx4.find_reachable_var (u); + gcc_assert (strg_u != nullptr); + + tree addr_u = build1 (ADDR_EXPR, ptr_type_node, u); + tree ref_u = build2 (MEM_REF, integer_type_node, addr_u, + build_int_cst (ptr_type_node, 0)); + tree cst44 = build_int_cst (integer_type_node, 44); + + gimple *assign44 = gimple_build_assign (ref_u, cst44); + + data_value val_u = strg_u->get_value (); + ASSERT_EQ (val_u.classify (), VAL_UNDEFINED); + + ctx4.execute (assign44); + + data_value val_u2 = strg_u->get_value (); + ASSERT_EQ (val_u2.classify (), VAL_CONSTANT); + wide_int wi_u2 = val_u2.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi_u2); + ASSERT_EQ (wi_u2.to_shwi (), 44); + + + tree var6 = create_var (integer_type_node, "var6"); + tree var2i = create_var (derived, "var2i"); + + vec<tree> decls5{}; + decls5.safe_push (var2i); + decls5.safe_push (var6); + + context_builder builder5 {}; + builder5.add_decls (&decls5); + exec_context ctx5 = builder5.build (nullptr, printer); + + wide_int wi8 = wi::shwi (8, HOST_BITS_PER_INT); + wide_int wi13 = wi::shwi (13, HOST_BITS_PER_INT); + + data_value val7 (ctx5, derived); + val7.set_cst_at (wi8, 0); + val7.set_cst_at (wi13, HOST_BITS_PER_INT); + + data_storage *strg = ctx5.find_reachable_var (var2i); + gcc_assert (strg != nullptr); + strg->set (val7); + + tree comp = build3 (COMPONENT_REF, integer_type_node, var2i, field2, + NULL_TREE); + + gimple *assign5 = gimple_build_assign (var6, comp); + + data_storage *strg5 = ctx5.find_reachable_var (var6); + gcc_assert (strg5 != nullptr); + data_value before = strg5->get_value (); + ASSERT_EQ (before.classify (), VAL_UNDEFINED); + + ctx5.execute (assign5); + + gcc_assert (strg5 != nullptr); + data_value after = strg5->get_value (); + ASSERT_EQ (after.classify (), VAL_CONSTANT); + wide_int wi7 = after.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, wi7); + ASSERT_EQ (wi7.to_shwi (), 13); + + + tree a5c = build_array_type_nelts (char_type_node, 5); + tree v5c = create_var (a5c,"v5c"); + + vec<tree> decls6{}; + decls6.safe_push (v5c); + + context_builder builder6 {}; + builder6.add_decls (&decls6); + exec_context ctx6 = builder6.build (nullptr, printer); + + tree c8 = build_int_cst (char_type_node, 8); + + tree ref2 = build2 (MEM_REF, char_type_node, + build1 (ADDR_EXPR, ptr_type_node, v5c), + build_int_cst (ptr_type_node, 3)); + + gassign *assign2 = gimple_build_assign (ref2, c8); + + ctx6.execute (assign2); + + data_storage *storage = ctx6.find_reachable_var (v5c); + gcc_assert (storage != nullptr); + data_value c8val = storage->get_value (); + + ASSERT_EQ (c8val.classify(), VAL_MIXED); + ASSERT_EQ (c8val.classify (3 * CHAR_BIT, CHAR_BIT), VAL_CONSTANT); + wide_int wi_val = c8val.get_cst_at (3 * CHAR_BIT, CHAR_BIT); + ASSERT_PRED1 (wi::fits_shwi_p, wi_val); + ASSERT_EQ (wi_val.to_shwi (), 8); +} + +void +exec_context_execute_call_tests () +{ + context_printer printer; + + vec<tree> empty{}; + + tree func_type = build_function_type (void_type_node, NULL_TREE); + layout_type (func_type); + + exec_context ctx = context_builder ().build (nullptr, printer); + + tree set_args_fn = build_decl (input_location, FUNCTION_DECL, + get_identifier ("_gfortran_set_args"), + func_type); + //DECL_EXTERNAL (set_args_fn) = 1; + //TREE_PUBLIC (set_args_fn) = 1; + + gcall * set_args_call = gimple_build_call (set_args_fn, 0); + + ctx.execute (set_args_call); + + tree set_options_fn = build_decl (input_location, FUNCTION_DECL, + get_identifier ("_gfortran_set_options"), + func_type); + //DECL_EXTERNAL (set_args) = 1; + //TREE_PUBLIC (set_args) = 0; + + gcall * set_options_call = gimple_build_call (set_options_fn, 0); + + ctx.execute (set_options_call); + + tree p = create_var (ptr_type_node, "p"); + + vec<tree> decls{}; + decls.safe_push (p); + + context_builder builder2 {}; + builder2.add_decls (&decls); + exec_context ctx2 = builder2.build (nullptr, printer); + + tree malloc_fn = builtin_decl_explicit (BUILT_IN_MALLOC); + tree cst = build_int_cst (size_type_node, 12); + + gcall * malloc_call = gimple_build_call (malloc_fn, 1, cst); + gimple_set_lhs (malloc_call, p); + + ASSERT_EQ (ctx2.find_alloc (0), nullptr); + + ctx2.execute (malloc_call); + + data_storage *alloc_strg = ctx2.find_alloc (0); + ASSERT_NE (alloc_strg, nullptr); + data_value alloc_val = alloc_strg->get_value (); + ASSERT_EQ (alloc_val.classify (), VAL_UNDEFINED); + ASSERT_EQ (alloc_val.get_bitwidth (), 96); + + data_storage *p_strg = ctx2.find_var (p); + ASSERT_NE (p_strg, nullptr); + data_value p_val = p_strg->get_value (); + ASSERT_EQ (p_val.classify (), VAL_ADDRESS); + ASSERT_EQ (p_val.get_address (), alloc_strg); + + tree cst2 = build_int_cst (size_type_node, 10); + + gcall * malloc_call2 = gimple_build_call (malloc_fn, 1, cst2); + gimple_set_lhs (malloc_call2, p); + + ASSERT_EQ (ctx2.find_alloc (1), nullptr); + + ctx2.execute (malloc_call2); + + data_storage *alloc_strg2 = ctx2.find_alloc (1); + ASSERT_NE (alloc_strg2, nullptr); + data_value alloc_val2 = alloc_strg2->get_value (); + ASSERT_EQ (alloc_val2.classify (), VAL_UNDEFINED); + ASSERT_EQ (alloc_val2.get_bitwidth (), 80); + + + tree cst6 = build_int_cst (integer_type_node, 6); + + tree int_func_type = build_function_type (integer_type_node, NULL_TREE); + layout_type (int_func_type); + + tree my_int_func = build_decl (input_location, FUNCTION_DECL, + get_identifier ("my_int_func"), + int_func_type); + tree result = build_decl (input_location, RESULT_DECL, + get_identifier ("result"), integer_type_node); + DECL_CONTEXT (result) = my_int_func; + DECL_RESULT (my_int_func) = result; + + basic_block bb = init_lowered_empty_function (my_int_func, true, profile_count::one ()); + gimple_stmt_iterator gsi = gsi_last_bb (bb); + greturn *ret_stmt = gimple_build_return (cst6); + gsi_insert_after (&gsi, ret_stmt, GSI_CONTINUE_LINKING); + + tree ivar = create_var (integer_type_node, "ivar"); + + gcall *my_call = gimple_build_call (my_int_func, 0); + gimple_set_lhs (my_call, ivar); + + vec<tree> decls2{}; + decls2.safe_push (ivar); + + context_builder builder3 {}; + builder3.add_decls (&decls2); + exec_context ctx3 = builder3.build (nullptr, printer); + + data_value ival = ctx3.evaluate (ivar); + ASSERT_EQ (ival.classify (), VAL_UNDEFINED); + + ctx3.execute (my_call); + + data_value ival2 = ctx3.evaluate (ivar); + ASSERT_EQ (ival2.classify (), VAL_CONSTANT); + wide_int func_result = ival2.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, func_result); + ASSERT_EQ (func_result.to_shwi (), 6); + + + tree int_func_arg_type = build_function_type_list (integer_type_node, + integer_type_node, + NULL_TREE); + layout_type (int_func_arg_type); + + tree my_int_func_with_arg = build_decl (input_location, FUNCTION_DECL, + get_identifier ("my_int_func_with_arg"), + int_func_type); + + tree fn_result = build_decl (input_location, RESULT_DECL, + get_identifier ("fn_result"), integer_type_node); + DECL_CONTEXT (fn_result) = my_int_func_with_arg; + DECL_RESULT (my_int_func_with_arg) = fn_result; + + tree arg = build_decl (input_location, PARM_DECL, + get_identifier ("arg"), integer_type_node); + DECL_ARG_TYPE (arg) = TREE_VALUE (TYPE_ARG_TYPES (int_func_arg_type)); + DECL_CONTEXT (arg) = my_int_func_with_arg; + DECL_ARGUMENTS (my_int_func_with_arg) = arg; + layout_decl (arg, 0); + + basic_block bb2 = init_lowered_empty_function (my_int_func_with_arg, true, profile_count::one ()); + gimple_stmt_iterator gsi2 = gsi_last_bb (bb2); + greturn *ret_stmt2 = gimple_build_return (arg); + gsi_insert_after (&gsi2, ret_stmt2, GSI_CONTINUE_LINKING); + + tree i19 = build_int_cst (integer_type_node, 19); + gcall *my_call2 = gimple_build_call (my_int_func_with_arg, 1, i19); + + tree ivar2 = create_var (integer_type_node, "ivar2"); + gimple_set_lhs (my_call2, ivar2); + + vec<tree> decls3{}; + decls3.safe_push (ivar2); + + context_builder builder4 {}; + builder4.add_decls (&decls3); + exec_context ctx4 = builder4.build (nullptr, printer); + + data_value ival3 = ctx4.evaluate (ivar2); + ASSERT_EQ (ival3.classify (), VAL_UNDEFINED); + + ctx4.execute (my_call2); + + data_value ival4 = ctx4.evaluate (ivar2); + ASSERT_EQ (ival4.classify (), VAL_CONSTANT); + wide_int func_result2 = ival4.get_cst (); + ASSERT_PRED1 (wi::fits_shwi_p, func_result2); + ASSERT_EQ (func_result2.to_shwi (), 19); +} + +void +gimple_exec_cc_tests () +{ + //get_constant_type_size_tests (); + data_value_classify_tests (); + exec_context_find_reachable_var_tests (); + data_value_set_address_tests (); + data_value_set_tests (); + data_value_set_at_tests (); + data_value_print_tests (); + context_printer_print_first_data_ref_part_tests (); + context_printer_print_value_update_tests (); + exec_context_evaluate_tests (); + exec_context_evaluate_literal_tests (); + exec_context_evaluate_unary_tests (); + exec_context_execute_assign_tests (); + exec_context_execute_call_tests (); +} + +} + +#endif diff --git a/gcc/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc index f6def491e5c7..02a87339f953 100644 --- a/gcc/fortran/trans-decl.cc +++ b/gcc/fortran/trans-decl.cc @@ -2105,7 +2105,7 @@ gfc_get_symbol_decl (gfc_symbol * sym) if (sym->attr.vtab || def_init) { DECL_ARTIFICIAL (decl) = 1; - if (def_init && sym->value) + if (sym->attr.vtab || sym->value) TREE_READONLY (decl) = 1; } diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index c4da84a275ed..ca3b40e2c80b 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -110,6 +110,8 @@ selftest::run_tests () attribs_cc_tests (); opts_diagnostic_cc_tests (); + gimple_exec_cc_tests (); + /* This one relies on most of the above. */ function_tests_cc_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index 5adfa908fd34..aa476719eba9 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -236,6 +236,7 @@ extern void gcc_attribute_urlifier_cc_tests (); extern void gcc_urlifier_cc_tests (); extern void ggc_tests_cc_tests (); extern void gimple_cc_tests (); +extern void gimple_exec_cc_tests (); extern void hash_map_tests_cc_tests (); extern void hash_set_tests_cc_tests (); extern void input_cc_tests ();