diff options
author | Martin Liska <mliska@suse.cz> | 2021-08-24 09:29:48 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2021-08-24 09:29:48 +0200 |
commit | eb2de151c582a38efc53ce57416f7bd7a3a9c0eb (patch) | |
tree | 653a76a3d1f126ac3775912c560df0c8fe9e5e8d | |
parent | 041709a62f1e184cf6d8fefa486f67ca7e6b784c (diff) | |
parent | f8977166135de09fe36a3b57cc11daa67587604e (diff) | |
download | gcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.zip gcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.tar.gz gcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.tar.bz2 |
Merge branch 'master' into devel/sphinx
72 files changed, 3529 insertions, 216 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b7a23d3..0183764 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,200 @@ +2021-08-23 Bill Schmidt <wschmidt@linux.ibm.com> + + * config/rs6000/rs6000-gen-builtins.c (parse_bif_entry): Don't call + asprintf, which is not available on AIX. + +2021-08-23 Bill Schmidt <wschmidt@linux.ibm.com> + + * config.gcc (target_gtfiles): Add ./rs6000-builtins.h. + * config/rs6000/t-rs6000 (EXTRA_GTYPE_DEPS): Set. + +2021-08-23 Bill Schmidt <wschmidt@linux.ibm.com> + + * config.gcc (powerpc*-*-*): Add rs6000-builtins.o to extra_objs. + * config/rs6000/rs6000-gen-builtins.c (main): Close init_file + last. + * config/rs6000/t-rs6000 (rs6000-gen-builtins.o): New target. + (rbtree.o): Likewise. + (rs6000-gen-builtins): Likewise. + (rs6000-builtins.c): Likewise. + (rs6000-builtins.h): Likewise. + (rs6000.o): Add dependency. + (EXTRA_HEADERS): Add rs6000-vecdefines.h. + (rs6000-vecdefines.h): New target. + (rs6000-builtins.o): Likewise. + (rs6000-call.o): Add rs6000-builtins.h as a dependency. + (rs6000-c.o): Likewise. + +2021-08-23 Bill Schmidt <wschmidt@linux.ibm.com> + + PR target/101830 + * config/rs6000/rs6000-gen-builtins.c (consume_whitespace): + Diagnose buffer overrun. + (safe_inc_pos): Fix overrun detection. + (match_identifier): Diagnose buffer overrun. + (match_integer): Likewise. + (match_to_right_bracket): Likewise. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + * ipa-modref-tree.h (modref_access_node::range_info_useful_p): + Improve range compare. + (modref_access_node::contains): New member function. + (modref_access_node::search): Remove. + (modref_access_node::insert): Be smarter about subaccesses. + +2021-08-23 Thomas Schwinge <thomas@codesourcery.com> + + * config/i386/i386-options.c (ix86_omp_device_kind_arch_isa) + <omp_device_arch> [ACCEL_COMPILER]: Match "intel_mic". + * config/i386/t-omp-device (omp-device-properties-i386) <arch>: + Add "intel_mic". + +2021-08-23 Jeff Law <jlaw@localhost.localdomain> + + * config/h8300/h8300-protos.h (h8300_expand_epilogue): Add new + argument. + * config/h8300/jumpcall.md (call, call_value): Restrict to + !SIBLING_CALL_P cases. + (subcall, sibcall_value): New patterns & expanders. + * config/h8300/proepi.md (epilogue): Pass new argument to + h8300_expand_epilogue. + (sibcall_epilogue): New expander. + * config/h8300/h8300.c (h8300_expand_epilogue): Handle sibcall + epilogues too. + (h8300_ok_for_sibcall_p): New function. + (TARGET_FUNCTION_OK_FOR_SIBCALL): define. + +2021-08-23 Roger Sayle <roger@nextmovesoftware.com> + + * simplify-rtx.c (simplify_unary_operation_1): [TRUNCATE]: + Handle case where the operand is already the desired mode. + +2021-08-23 Richard Biener <rguenther@suse.de> + + PR ipa/97565 + * tree-ssa-structalias.c (ipa_pta_execute): Check in_other_partition + in addition to has_gimple_body. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + PR middle-end/101949 + * ipa-modref.c (analyze_ssa_name_flags): Fix merging of + EAF_NOCLOBBER + +2021-08-23 Martin Liska <mliska@suse.cz> + + * doc/invoke.texi: Put the option out of -mxl-mode-app-model + table. + +2021-08-23 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.c (vect_compute_single_scalar_iteration_cost): + Properly scale the inner loop cost only once. + +2021-08-23 Roger Sayle <roger@nextmovesoftware.com> + + * tree-ssa-ccp.c (bit_value_binop) [TRUNC_MOD_EXPR, TRUNC_DIV_EXPR]: + Provide bounds for unsigned (and signed with non-negative operands) + division and modulus. + +2021-08-23 Roger Sayle <roger@nextmovesoftware.com> + + * simplify-rtx.c (simplify_truncation): Generalize simplification + of (truncate:A (subreg:B X)). + (simplify_unary_operation_1) [FLOAT_TRUNCATE, FLOAT_EXTEND, + SIGN_EXTEND, ZERO_EXTEND]: Handle cases where the operand + already has the desired machine mode. + (test_scalar_int_ops): Add tests that useless extensions and + truncations are optimized away. + (test_scalar_int_ext_ops): New self-test function to confirm + that truncations of extensions are correctly simplified. + (test_scalar_int_ext_ops2): New self-test function to check + truncations of truncations, extensions of extensions, and + truncations of extensions. + (test_scalar_ops): Call the above two functions with a + representative sampling of integer machine modes. + +2021-08-23 Roger Sayle <roger@nextmovesoftware.com> + + * match.pd (shift transformations): Change the sign of an + LSHIFT_EXPR if it reduces the number of explicit conversions. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/86723 + * gimple-ssa-store-merging.c (find_bswap_or_nop_finalize): Add + cast64_to_32 argument, set *cast64_to_32 to false, unless n is + non-memory permutation of 64-bit src which only has bytes of + 0 or [5..8] and n->range is 4. + (find_bswap_or_nop): Add cast64_to_32 and mask arguments, adjust + find_bswap_or_nop_finalize caller, support bswap with some bytes + zeroed, as long as at least two bytes are not zeroed. + (bswap_replace): Add mask argument and handle masking of bswap + result. + (maybe_optimize_vector_constructor): Adjust find_bswap_or_nop + caller, punt if cast64_to_32 or mask is not all ones. + (pass_optimize_bswap::execute): Adjust find_bswap_or_nop_finalize + caller, for now punt if cast64_to_32. + +2021-08-23 Richard Biener <rguenther@suse.de> + + PR tree-optimization/79334 + * tree-ssa-sccvn.c (copy_reference_ops_from_ref): Record + a type also for COMPONENT_REFs. + (vn_reference_may_trap): Check ARRAY_REF with constant index + against the array domain. + +2021-08-23 liuhongt <hongtao.liu@intel.com> + + PR target/102016 + * config/i386/sse.md (*avx512f_pshufb_truncv8hiv8qi_1): Add + TARGET_AVX512BW to condition. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + PR debug/101905 + * dwarf2out.c (gen_variable_die): Add DW_AT_location for global + register variables already during early_dwarf if possible. + +2021-08-23 Christophe Lyon <christophe.lyon@foss.st.com> + + * config/arm/arm_mve.h: Fix __arm_vctp16q return type. + +2021-08-23 Christophe Lyon <christophe.lyon@foss.st.com> + + PR target/100856 + * config/arm/arm.opt: Fix typo. + * config/arm/t-rmprofile: Fix typo. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + * tree.h (OMP_CLAUSE_GRAINSIZE_STRICT): Define. + (OMP_CLAUSE_NUM_TASKS_STRICT): Define. + * tree-pretty-print.c (dump_omp_clause) <case OMP_CLAUSE_GRAINSIZE, + case OMP_CLAUSE_NUM_TASKS>: Print strict: modifier. + * omp-expand.c (expand_task_call): Use GOMP_TASK_FLAG_STRICT in iflags + if either grainsize or num_tasks clause has the strict modifier. + +2021-08-23 Martin Liska <mliska@suse.cz> + + * dbgcnt.def (DEBUG_COUNTER): New counter. + * gimple.c (gimple_call_arg_flags): Use it in IPA PTA. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + * ipa-modref.c (analyze_ssa_name_flags): Improve handling of return slot. + +2021-08-23 Xi Ruoyao <xry111@mengyan1223.wang> + + PR target/101922 + * config/mips/mips-protos.h (mips_msa_output_shift_immediate): + Declare. + * config/mips/mips.c (mips_msa_output_shift_immediate): New + function. + * config/mips/mips-msa.md (vashl<mode>3, vashr<mode>3, + vlshr<mode>3): Call it. + 2021-08-22 Jan Hubicka <hubicka@ucw.cz> Martin Liska <mliska@suse.cz> diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index d2e7bda..83a5291 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20210823 +20210824 diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index da90011..211f34c 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,184 @@ +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + * analyzer.h (struct rejected_constraint): Convert to... + (class rejected_constraint): ...this. + (class bounded_ranges): New forward decl. + (class bounded_ranges_manager): New forward decl. + * constraint-manager.cc: Include "analyzer/analyzer-logging.h" and + "tree-pretty-print.h". + (can_plus_one_p): New. + (plus_one): New. + (can_minus_one_p): New. + (minus_one): New. + (bounded_range::bounded_range): New. + (dump_cst): New. + (bounded_range::dump_to_pp): New. + (bounded_range::dump): New. + (bounded_range::to_json): New. + (bounded_range::set_json_attr): New. + (bounded_range::contains_p): New. + (bounded_range::intersects_p): New. + (bounded_range::operator==): New. + (bounded_range::cmp): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::bounded_ranges): New. + (bounded_ranges::canonicalize): New. + (bounded_ranges::validate): New. + (bounded_ranges::operator==): New. + (bounded_ranges::dump_to_pp): New. + (bounded_ranges::dump): New. + (bounded_ranges::to_json): New. + (bounded_ranges::eval_condition): New. + (bounded_ranges::contain_p): New. + (bounded_ranges::cmp): New. + (bounded_ranges_manager::~bounded_ranges_manager): New. + (bounded_ranges_manager::get_or_create_empty): New. + (bounded_ranges_manager::get_or_create_point): New. + (bounded_ranges_manager::get_or_create_range): New. + (bounded_ranges_manager::get_or_create_union): New. + (bounded_ranges_manager::get_or_create_intersection): New. + (bounded_ranges_manager::get_or_create_inverse): New. + (bounded_ranges_manager::consolidate): New. + (bounded_ranges_manager::get_or_create_ranges_for_switch): New. + (bounded_ranges_manager::create_ranges_for_switch): New. + (bounded_ranges_manager::make_case_label_ranges): New. + (bounded_ranges_manager::log_stats): New. + (bounded_ranges_constraint::print): New. + (bounded_ranges_constraint::to_json): New. + (bounded_ranges_constraint::operator==): New. + (bounded_ranges_constraint::add_to_hash): New. + (constraint_manager::constraint_manager): Update for new field + m_bounded_ranges_constraints. + (constraint_manager::operator=): Likewise. + (constraint_manager::hash): Likewise. + (constraint_manager::operator==): Likewise. + (constraint_manager::print): Likewise. + (constraint_manager::dump_to_pp): Likewise. + (constraint_manager::to_json): Likewise. + (constraint_manager::add_unknown_constraint): Update the lhs_ec_id + if necessary in existing constraints when combining equivalence + classes. Add similar code for handling + m_bounded_ranges_constraints. + (constraint_manager::add_constraint_internal): Add comment. + (constraint_manager::add_bounded_ranges): New. + (constraint_manager::eval_condition): Use new field + m_bounded_ranges_constraints. + (constraint_manager::purge): Update bounded_ranges_constraint + instances. + (constraint_manager::canonicalize): Update for new field. + (merger_fact_visitor::on_ranges): New. + (constraint_manager::for_each_fact): Use new field + m_bounded_ranges_constraints. + (constraint_manager::validate): Fix off-by-one error needed due + to bug fixed above in add_unknown_constraint. Validate the EC IDs + in m_bounded_ranges_constraints. + (constraint_manager::get_range_manager): New. + (selftest::assert_dump_bounded_range_eq): New. + (ASSERT_DUMP_BOUNDED_RANGE_EQ): New. + (selftest::test_bounded_range): New. + (selftest::assert_dump_bounded_ranges_eq): New. + (ASSERT_DUMP_BOUNDED_RANGES_EQ): New. + (selftest::test_bounded_ranges): New. + (selftest::run_constraint_manager_tests): Call the new selftests. + * constraint-manager.h (struct bounded_range): New. + (struct bounded_ranges): New. + (template <> struct default_hash_traits<bounded_ranges::key_t>): New. + (class bounded_ranges_manager): New. + (fact_visitor::on_ranges): New pure virtual function. + (class bounded_ranges_constraint): New. + (constraint_manager::add_bounded_ranges): New decl. + (constraint_manager::get_range_manager): New decl. + (constraint_manager::m_bounded_ranges_constraints): New field. + * diagnostic-manager.cc (epath_finder::process_worklist_item): + Transfer ownership of rc to add_feasibility_problem. + * engine.cc (feasibility_problem::dump_to_pp): Use get_model. + * feasible-graph.cc (infeasible_node::dump_dot): Update for + conversion of m_rc to a pointer. + (feasible_graph::add_feasibility_problem): Pass RC by pointer and + take ownership. + * feasible-graph.h (infeasible_node::infeasible_node): Pass RC by + pointer and take ownership. + (infeasible_node::~infeasible_node): New. + (infeasible_node::m_rc): Convert to a pointer. + (feasible_graph::add_feasibility_problem): Pass RC by pointer and + take ownership. + * region-model-manager.cc: Include + "analyzer/constraint-manager.h". + (region_model_manager::region_model_manager): Initializer new + field m_range_mgr. + (region_model_manager::~region_model_manager): Delete it. + (region_model_manager::log_stats): Call log_stats on it. + * region-model.cc (region_model::add_constraint): Use new subclass + rejected_op_constraint. + (region_model::apply_constraints_for_gswitch): Reimplement using + bounded_ranges_manager. + (rejected_constraint::dump_to_pp): Convert to... + (rejected_op_constraint::dump_to_pp): ...this. + (rejected_ranges_constraint::dump_to_pp): New. + * region-model.h (struct purge_stats): Add field + m_num_bounded_ranges_constraints. + (region_model_manager::get_range_manager): New. + (region_model_manager::m_range_mgr): New. + (region_model::get_range_manager): New. + (struct rejected_constraint): Split into... + (class rejected_constraint):...this new abstract base class, + and... + (class rejected_op_constraint): ...this new concrete subclass. + (class rejected_ranges_constraint): New. + * supergraph.cc: Include "tree-cfg.h". + (supergraph::supergraph): Drop idx param from add_cfg_edge. + (supergraph::add_cfg_edge): Drop idx param. + (switch_cfg_superedge::switch_cfg_superedge): Move here from + header. Populate m_case_labels with all cases which go to DST. + (switch_cfg_superedge::dump_label_to_pp): Reimplement to use + m_case_labels. + (switch_cfg_superedge::get_case_label): Delete. + * supergraph.h (supergraphadd_cfg_edge): Drop "idx" param. + (switch_cfg_superedge::switch_cfg_superedge): Drop idx param and + move implementation to supergraph.cc. + (switch_cfg_superedge::get_case_label): Delete. + (switch_cfg_superedge::get_case_labels): New. + (switch_cfg_superedge::m_idx): Delete. + (switch_cfg_superedge::m_case_labels): New field. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101875 + * sm-file.cc (file_diagnostic::describe_state_change): Handle + change.m_expr being NULL. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101837 + * analyzer.cc (maybe_reconstruct_from_def_stmt): Bail if fn is + NULL, and assert that it's non-NULL before passing it to + build_call_array_loc. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101962 + * region-model.cc (region_model::eval_condition_without_cm): + Refactor comparison against zero, adding a check for + POINTER_PLUS_EXPR of non-NULL. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + * store.cc (bit_range::intersects_p): New overload. + (bit_range::operator-): New. + (binding_cluster::maybe_get_compound_binding): Handle the partial + overlap case. + (selftest::test_bit_range_intersects_p): Add test coverage for + new overload of bit_range::intersects_p. + * store.h (bit_range::intersects_p): New overload. + (bit_range::operator-): New. + +2021-08-23 Ankur Saini <arsenic@sourceware.org> + + PR analyzer/102020 + * diagnostic-manager.cc + (diagnostic_manager::prune_for_sm_diagnostic)<case EK_CALL_EDGE>: Fix typo. + 2021-08-21 Ankur Saini <arsenic@sourceware.org> PR analyzer/101980 diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc index 5578877..f6e9c9d 100644 --- a/gcc/analyzer/analyzer.cc +++ b/gcc/analyzer/analyzer.cc @@ -145,6 +145,8 @@ maybe_reconstruct_from_def_stmt (tree ssa_name, tree return_type = gimple_call_return_type (call_stmt); tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt), visited); + if (fn == NULL_TREE) + return NULL_TREE; unsigned num_args = gimple_call_num_args (call_stmt); auto_vec<tree> args (num_args); for (unsigned i = 0; i < num_args; i++) @@ -155,6 +157,7 @@ maybe_reconstruct_from_def_stmt (tree ssa_name, return NULL_TREE; args.quick_push (arg); } + gcc_assert (fn); return build_call_array_loc (gimple_location (call_stmt), return_type, fn, num_args, args.address ()); diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h index 896b350..05d4751 100644 --- a/gcc/analyzer/analyzer.h +++ b/gcc/analyzer/analyzer.h @@ -75,10 +75,12 @@ class region_model; class region_model_context; class impl_region_model_context; class call_details; -struct rejected_constraint; +class rejected_constraint; class constraint_manager; class equiv_class; class reachable_regions; +class bounded_ranges; +class bounded_ranges_manager; class pending_diagnostic; class state_change_event; diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index f59929a..dc65c8d 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -42,12 +42,14 @@ along with GCC; see the file COPYING3. If not see #include "sbitmap.h" #include "bitmap.h" #include "tristate.h" +#include "analyzer/analyzer-logging.h" #include "analyzer/call-string.h" #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" #include "analyzer/constraint-manager.h" #include "analyzer/analyzer-selftests.h" +#include "tree-pretty-print.h" #if ENABLE_ANALYZER @@ -65,6 +67,50 @@ compare_constants (tree lhs_const, enum tree_code op, tree rhs_const) return tristate (tristate::TS_UNKNOWN); } +/* Return true iff CST is below the maximum value for its type. */ + +static bool +can_plus_one_p (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + return tree_int_cst_lt (cst, TYPE_MAX_VALUE (TREE_TYPE (cst))); +} + +/* Return (CST + 1). */ + +static tree +plus_one (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + gcc_assert (can_plus_one_p (cst)); + tree result = fold_build2 (PLUS_EXPR, TREE_TYPE (cst), + cst, integer_one_node); + gcc_assert (CONSTANT_CLASS_P (result)); + return result; +} + +/* Return true iff CST is above the minimum value for its type. */ + +static bool +can_minus_one_p (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + return tree_int_cst_lt (TYPE_MIN_VALUE (TREE_TYPE (cst)), cst); +} + +/* Return (CST - 1). */ + +static tree +minus_one (tree cst) +{ + gcc_assert (CONSTANT_CLASS_P (cst)); + gcc_assert (can_minus_one_p (cst)); + tree result = fold_build2 (MINUS_EXPR, TREE_TYPE (cst), + cst, integer_one_node); + gcc_assert (CONSTANT_CLASS_P (result)); + return result; +} + /* struct bound. */ /* Ensure that this bound is closed by converting an open bound to a @@ -255,6 +301,678 @@ range::above_upper_bound (tree rhs_const) const m_upper_bound.m_constant).is_true (); } +/* struct bounded_range. */ + +bounded_range::bounded_range (const_tree lower, const_tree upper) +: m_lower (const_cast<tree> (lower)), + m_upper (const_cast<tree> (upper)) +{ + if (lower && upper) + { + gcc_assert (TREE_CODE (m_lower) == INTEGER_CST); + gcc_assert (TREE_CODE (m_upper) == INTEGER_CST); + /* We should have lower <= upper. */ + gcc_assert (!tree_int_cst_lt (m_upper, m_lower)); + } + else + { + /* Purely for pending on-stack values, for + writing back to. */ + gcc_assert (m_lower == NULL_TREE); + gcc_assert (m_lower == NULL_TREE); + } +} + +static void +dump_cst (pretty_printer *pp, tree cst, bool show_types) +{ + gcc_assert (cst); + if (show_types) + { + pp_character (pp, '('); + dump_generic_node (pp, TREE_TYPE (cst), 0, (dump_flags_t)0, false); + pp_character (pp, ')'); + } + dump_generic_node (pp, cst, 0, (dump_flags_t)0, false); +} + +/* Dump this object to PP. */ + +void +bounded_range::dump_to_pp (pretty_printer *pp, bool show_types) const +{ + if (tree_int_cst_equal (m_lower, m_upper)) + dump_cst (pp, m_lower, show_types); + else + { + pp_character (pp, '['); + dump_cst (pp, m_lower, show_types); + pp_string (pp, ", "); + dump_cst (pp, m_upper, show_types); + pp_character (pp, ']'); + } +} + +/* Dump this object to stderr. */ + +void +bounded_range::dump (bool show_types) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + dump_to_pp (&pp, show_types); + pp_newline (&pp); + pp_flush (&pp); +} + +json::object * +bounded_range::to_json () const +{ + json::object *range_obj = new json::object (); + set_json_attr (range_obj, "lower", m_lower); + set_json_attr (range_obj, "upper", m_upper); + return range_obj; +} + +/* Subroutine of bounded_range::to_json. */ + +void +bounded_range::set_json_attr (json::object *obj, const char *name, tree value) +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_printf (&pp, "%E", value); + obj->set (name, new json::string (pp_formatted_text (&pp))); +} + + +/* Return true iff CST is within this range. */ + +bool +bounded_range::contains_p (tree cst) const +{ + /* Reject if below lower bound. */ + if (tree_int_cst_lt (cst, m_lower)) + return false; + /* Reject if above lower bound. */ + if (tree_int_cst_lt (m_upper, cst)) + return false; + return true; +} + +/* If this range intersects OTHER, return true, writing + the intersection to *OUT if OUT is non-NULL. + Return false if they do not intersect. */ + +bool +bounded_range::intersects_p (const bounded_range &other, + bounded_range *out) const +{ + const tree max_lower + = (tree_int_cst_le (m_lower, other.m_lower) + ? other.m_lower : m_lower); + gcc_assert (TREE_CODE (max_lower) == INTEGER_CST); + const tree min_upper + = (tree_int_cst_le (m_upper, other.m_upper) + ? m_upper : other.m_upper); + gcc_assert (TREE_CODE (min_upper) == INTEGER_CST); + + if (tree_int_cst_le (max_lower, min_upper)) + { + if (out) + *out = bounded_range (max_lower, min_upper); + return true; + } + else + return false; +} + +bool +bounded_range::operator== (const bounded_range &other) const +{ + return (tree_int_cst_equal (m_lower, other.m_lower) + && tree_int_cst_equal (m_upper, other.m_upper)); +} + +int +bounded_range::cmp (const bounded_range &br1, const bounded_range &br2) +{ + if (int cmp_lower = tree_int_cst_compare (br1.m_lower, + br2.m_lower)) + return cmp_lower; + return tree_int_cst_compare (br1.m_upper, br2.m_upper); +} + +/* struct bounded_ranges. */ + +/* Construct a bounded_ranges instance from a single range. */ + +bounded_ranges::bounded_ranges (const bounded_range &range) +: m_ranges (1) +{ + m_ranges.quick_push (range); + canonicalize (); + validate (); +} + +/* Construct a bounded_ranges instance from multiple ranges. */ + +bounded_ranges::bounded_ranges (const vec<bounded_range> &ranges) +: m_ranges (ranges.length ()) +{ + m_ranges.safe_splice (ranges); + canonicalize (); + validate (); +} + +/* Construct a bounded_ranges instance for values of LHS for which + (LHS OP RHS_CONST) is true (e.g. "(LHS > 3)". */ + +bounded_ranges::bounded_ranges (enum tree_code op, tree rhs_const) +: m_ranges () +{ + gcc_assert (TREE_CODE (rhs_const) == INTEGER_CST); + tree type = TREE_TYPE (rhs_const); + switch (op) + { + default: + gcc_unreachable (); + case EQ_EXPR: + m_ranges.safe_push (bounded_range (rhs_const, rhs_const)); + break; + + case GE_EXPR: + m_ranges.safe_push (bounded_range (rhs_const, TYPE_MAX_VALUE (type))); + break; + + case LE_EXPR: + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), rhs_const)); + break; + + case NE_EXPR: + if (tree_int_cst_lt (TYPE_MIN_VALUE (type), rhs_const)) + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), + minus_one (rhs_const))); + if (tree_int_cst_lt (rhs_const, TYPE_MAX_VALUE (type))) + m_ranges.safe_push (bounded_range (plus_one (rhs_const), + TYPE_MAX_VALUE (type))); + break; + case GT_EXPR: + if (tree_int_cst_lt (rhs_const, TYPE_MAX_VALUE (type))) + m_ranges.safe_push (bounded_range (plus_one (rhs_const), + TYPE_MAX_VALUE (type))); + break; + case LT_EXPR: + if (tree_int_cst_lt (TYPE_MIN_VALUE (type), rhs_const)) + m_ranges.safe_push (bounded_range (TYPE_MIN_VALUE (type), + minus_one (rhs_const))); + break; + } + canonicalize (); + validate (); +} + +/* Subroutine of ctors for fixing up m_ranges. + Also, initialize m_hash. */ + +void +bounded_ranges::canonicalize () +{ + /* Sort the ranges. */ + m_ranges.qsort ([](const void *p1, const void *p2) -> int + { + const bounded_range &br1 = *(const bounded_range *)p1; + const bounded_range &br2 = *(const bounded_range *)p2; + return bounded_range::cmp (br1, br2); + }); + + /* Merge ranges that are touching or overlapping. */ + for (unsigned i = 1; i < m_ranges.length (); ) + { + bounded_range *prev = &m_ranges[i - 1]; + const bounded_range *next = &m_ranges[i]; + if (prev->intersects_p (*next, NULL) + || (can_plus_one_p (prev->m_upper) + && tree_int_cst_equal (plus_one (prev->m_upper), + next->m_lower))) + { + prev->m_upper = next->m_upper; + m_ranges.ordered_remove (i); + } + else + i++; + } + + /* Initialize m_hash. */ + inchash::hash hstate (0); + for (const auto &iter : m_ranges) + { + inchash::add_expr (iter.m_lower, hstate); + inchash::add_expr (iter.m_upper, hstate); + } + m_hash = hstate.end (); +} + +/* Assert that this object is valid. */ + +void +bounded_ranges::validate () const +{ + /* Skip this in a release build. */ +#if !CHECKING_P + return; +#endif + + for (unsigned i = 1; i < m_ranges.length (); i++) + { + const bounded_range &prev = m_ranges[i - 1]; + const bounded_range &next = m_ranges[i]; + + /* Give up if we somehow have incompatible different types. */ + if (!types_compatible_p (TREE_TYPE (prev.m_upper), + TREE_TYPE (next.m_lower))) + continue; + + /* Verify sorted. */ + gcc_assert (tree_int_cst_lt (prev.m_upper, next.m_lower)); + + gcc_assert (can_plus_one_p (prev.m_upper)); + /* otherwise there's no room for "next". */ + + /* Verify no ranges touch each other. */ + gcc_assert (tree_int_cst_lt (plus_one (prev.m_upper), next.m_lower)); + } +} + +/* bounded_ranges equality operator. */ + +bool +bounded_ranges::operator== (const bounded_ranges &other) const +{ + if (m_ranges.length () != other.m_ranges.length ()) + return false; + for (unsigned i = 0; i < m_ranges.length (); i++) + { + if (m_ranges[i] != other.m_ranges[i]) + return false; + } + return true; +} + +/* Dump this object to PP. */ + +void +bounded_ranges::dump_to_pp (pretty_printer *pp, bool show_types) const +{ + pp_character (pp, '{'); + for (unsigned i = 0; i < m_ranges.length (); ++i) + { + if (i > 0) + pp_string (pp, ", "); + m_ranges[i].dump_to_pp (pp, show_types); + } + pp_character (pp, '}'); +} + +/* Dump this object to stderr. */ + +DEBUG_FUNCTION void +bounded_ranges::dump (bool show_types) const +{ + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + dump_to_pp (&pp, show_types); + pp_newline (&pp); + pp_flush (&pp); +} + +json::value * +bounded_ranges::to_json () const +{ + json::array *arr_obj = new json::array (); + + for (unsigned i = 0; i < m_ranges.length (); ++i) + arr_obj->append (m_ranges[i].to_json ()); + + return arr_obj; +} + +/* Determine whether (X OP RHS_CONST) is known to be true or false + for all X in the ranges expressed by this object. */ + +tristate +bounded_ranges::eval_condition (enum tree_code op, + tree rhs_const, + bounded_ranges_manager *mgr) const +{ + /* Convert (X OP RHS_CONST) to a bounded_ranges instance and find + the intersection of that with this object. */ + bounded_ranges other (op, rhs_const); + const bounded_ranges *intersection + = mgr->get_or_create_intersection (this, &other); + + if (intersection->m_ranges.length () > 0) + { + /* We can use pointer equality to check for equality, + due to instance consolidation. */ + if (intersection == this) + return tristate (tristate::TS_TRUE); + else + return tristate (tristate::TS_UNKNOWN); + } + else + /* No intersection. */ + return tristate (tristate::TS_FALSE); +} + +/* Return true if CST is within any of the ranges. */ + +bool +bounded_ranges::contain_p (tree cst) const +{ + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + for (const auto &iter : m_ranges) + { + /* TODO: should we optimize this based on sorting? */ + if (iter.contains_p (cst)) + return true; + } + return false; +} + +int +bounded_ranges::cmp (const bounded_ranges *a, const bounded_ranges *b) +{ + if (int cmp_length = ((int)a->m_ranges.length () + - (int)b->m_ranges.length ())) + return cmp_length; + for (unsigned i = 0; i < a->m_ranges.length (); i++) + { + if (int cmp_range = bounded_range::cmp (a->m_ranges[i], b->m_ranges[i])) + return cmp_range; + } + /* They are equal. They ought to have been consolidated, so we should + have two pointers to the same object. */ + gcc_assert (a == b); + return 0; +} + +/* class bounded_ranges_manager. */ + +/* bounded_ranges_manager's dtor. */ + +bounded_ranges_manager::~bounded_ranges_manager () +{ + /* Delete the managed objects. */ + for (const auto &iter : m_map) + delete iter.second; +} + +/* Get the bounded_ranges instance for the empty set, creating it if + necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_empty () +{ + auto_vec<bounded_range> empty_vec; + + return consolidate (new bounded_ranges (empty_vec)); +} + +/* Get the bounded_ranges instance for {CST}, creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_point (const_tree cst) +{ + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + + return get_or_create_range (cst, cst); +} + +/* Get the bounded_ranges instance for {[LOWER_BOUND..UPPER_BOUND]}, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_range (const_tree lower_bound, + const_tree upper_bound) +{ + gcc_assert (TREE_CODE (lower_bound) == INTEGER_CST); + gcc_assert (TREE_CODE (upper_bound) == INTEGER_CST); + + return consolidate + (new bounded_ranges (bounded_range (lower_bound, upper_bound))); +} + +/* Get the bounded_ranges instance for the union of OTHERS, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager:: +get_or_create_union (const vec <const bounded_ranges *> &others) +{ + auto_vec<bounded_range> ranges; + for (const auto &r : others) + ranges.safe_splice (r->m_ranges); + return consolidate (new bounded_ranges (ranges)); +} + +/* Get the bounded_ranges instance for the intersection of A and B, + creating it if necessary. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_intersection (const bounded_ranges *a, + const bounded_ranges *b) +{ + auto_vec<bounded_range> ranges; + unsigned a_idx = 0; + unsigned b_idx = 0; + while (a_idx < a->m_ranges.length () + && b_idx < b->m_ranges.length ()) + { + const bounded_range &r_a = a->m_ranges[a_idx]; + const bounded_range &r_b = b->m_ranges[b_idx]; + + bounded_range intersection (NULL_TREE, NULL_TREE); + if (r_a.intersects_p (r_b, &intersection)) + { + ranges.safe_push (intersection); + } + if (tree_int_cst_lt (r_a.m_lower, r_b.m_lower)) + { + a_idx++; + } + else + { + if (tree_int_cst_lt (r_a.m_upper, r_b.m_upper)) + a_idx++; + else + b_idx++; + } + } + + return consolidate (new bounded_ranges (ranges)); +} + +/* Get the bounded_ranges instance for the inverse of OTHER relative + to TYPE, creating it if necessary. + This is for use when handling "default" in switch statements, where + OTHER represents all the other cases. */ + +const bounded_ranges * +bounded_ranges_manager::get_or_create_inverse (const bounded_ranges *other, + tree type) +{ + tree min_val = TYPE_MIN_VALUE (type); + tree max_val = TYPE_MAX_VALUE (type); + if (other->m_ranges.length () == 0) + return get_or_create_range (min_val, max_val); + auto_vec<bounded_range> ranges; + tree first_lb = other->m_ranges[0].m_lower; + if (tree_int_cst_lt (min_val, first_lb) + && can_minus_one_p (first_lb)) + ranges.safe_push (bounded_range (min_val, + minus_one (first_lb))); + for (unsigned i = 1; i < other->m_ranges.length (); i++) + { + tree prev_ub = other->m_ranges[i - 1].m_upper; + tree iter_lb = other->m_ranges[i].m_lower; + gcc_assert (tree_int_cst_lt (prev_ub, iter_lb)); + if (can_plus_one_p (prev_ub) && can_minus_one_p (iter_lb)) + ranges.safe_push (bounded_range (plus_one (prev_ub), + minus_one (iter_lb))); + } + tree last_ub + = other->m_ranges[other->m_ranges.length () - 1].m_upper; + if (tree_int_cst_lt (last_ub, max_val) + && can_plus_one_p (last_ub)) + ranges.safe_push (bounded_range (plus_one (last_ub), max_val)); + + return consolidate (new bounded_ranges (ranges)); +} + +/* If an object equal to INST is already present, delete INST and + return the existing object. + Otherwise add INST and return it. */ + +const bounded_ranges * +bounded_ranges_manager::consolidate (bounded_ranges *inst) +{ + if (bounded_ranges **slot = m_map.get (inst)) + { + delete inst; + return *slot; + } + m_map.put (inst, inst); + return inst; +} + +/* Get the bounded_ranges instance for EDGE of SWITCH_STMT, + creating it if necessary, and caching it by edge. */ + +const bounded_ranges * +bounded_ranges_manager:: +get_or_create_ranges_for_switch (const switch_cfg_superedge *edge, + const gswitch *switch_stmt) +{ + /* Look in per-edge cache. */ + if (const bounded_ranges ** slot = m_edge_cache.get (edge)) + return *slot; + + /* Not yet in cache. */ + const bounded_ranges *all_cases_ranges + = create_ranges_for_switch (*edge, switch_stmt); + m_edge_cache.put (edge, all_cases_ranges); + return all_cases_ranges; +} + +/* Get the bounded_ranges instance for EDGE of SWITCH_STMT, + creating it if necessary, for edges for which the per-edge + cache has not yet been populated. */ + +const bounded_ranges * +bounded_ranges_manager:: +create_ranges_for_switch (const switch_cfg_superedge &edge, + const gswitch *switch_stmt) +{ + /* Get the ranges for each case label. */ + auto_vec <const bounded_ranges *> case_ranges_vec + (gimple_switch_num_labels (switch_stmt)); + + for (tree case_label : edge.get_case_labels ()) + { + /* Get the ranges for this case label. */ + const bounded_ranges *case_ranges + = make_case_label_ranges (switch_stmt, case_label); + case_ranges_vec.quick_push (case_ranges); + } + + /* Combine all the ranges for each case label into a single collection + of ranges. */ + const bounded_ranges *all_cases_ranges + = get_or_create_union (case_ranges_vec); + return all_cases_ranges; +} + +/* Get the bounded_ranges instance for CASE_LABEL within + SWITCH_STMT. */ + +const bounded_ranges * +bounded_ranges_manager:: +make_case_label_ranges (const gswitch *switch_stmt, + tree case_label) +{ + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + if (upper_bound) + /* Range. */ + return get_or_create_range (lower_bound, upper_bound); + else + /* Single-value. */ + return get_or_create_point (lower_bound); + } + else + { + /* The default case. + Add exclusions based on the other cases. */ + auto_vec <const bounded_ranges *> other_case_ranges + (gimple_switch_num_labels (switch_stmt)); + for (unsigned other_idx = 1; + other_idx < gimple_switch_num_labels (switch_stmt); + other_idx++) + { + tree other_label = gimple_switch_label (switch_stmt, + other_idx); + const bounded_ranges *other_ranges + = make_case_label_ranges (switch_stmt, other_label); + other_case_ranges.quick_push (other_ranges); + } + const bounded_ranges *other_cases_ranges + = get_or_create_union (other_case_ranges); + tree type = TREE_TYPE (gimple_switch_index (switch_stmt)); + return get_or_create_inverse (other_cases_ranges, type); + } +} + +/* Dump the number of objects of each class that were managed by this + manager to LOGGER. + If SHOW_OBJS is true, also dump the objects themselves. */ + +void +bounded_ranges_manager::log_stats (logger *logger, bool show_objs) const +{ + LOG_SCOPE (logger); + logger->log (" # %s: %li", "ranges", m_map.elements ()); + if (!show_objs) + return; + + auto_vec<const bounded_ranges *> vec_objs (m_map.elements ()); + for (const auto &iter : m_map) + vec_objs.quick_push (iter.second); + vec_objs.qsort + ([](const void *p1, const void *p2) -> int + { + const bounded_ranges *br1 = *(const bounded_ranges * const *)p1; + const bounded_ranges *br2 = *(const bounded_ranges * const *)p2; + return bounded_ranges::cmp (br1, br2); + }); + + for (const auto &iter : vec_objs) + { + logger->start_log_line (); + pretty_printer *pp = logger->get_printer (); + pp_string (pp, " "); + iter->dump_to_pp (pp, true); + logger->end_log_line (); + } +} + /* class equiv_class. */ /* equiv_class's default ctor. */ @@ -576,6 +1294,49 @@ constraint::implied_by (const constraint &other, return false; } +/* class bounded_ranges_constraint. */ + +void +bounded_ranges_constraint::print (pretty_printer *pp, + const constraint_manager &cm) const +{ + m_ec_id.print (pp); + pp_string (pp, ": "); + m_ec_id.get_obj (cm).print (pp); + pp_string (pp, ": "); + m_ranges->dump_to_pp (pp, true); +} + +json::object * +bounded_ranges_constraint::to_json () const +{ + json::object *con_obj = new json::object (); + + con_obj->set ("ec", new json::integer_number (m_ec_id.as_int ())); + con_obj->set ("ranges", m_ranges->to_json ()); + + return con_obj; +} + +bool +bounded_ranges_constraint:: +operator== (const bounded_ranges_constraint &other) const +{ + if (m_ec_id != other.m_ec_id) + return false; + + /* We can compare by pointer, since the bounded_ranges_manager + consolidates instances. */ + return m_ranges == other.m_ranges; +} + +void +bounded_ranges_constraint::add_to_hash (inchash::hash *hstate) const +{ + hstate->add_int (m_ec_id.m_idx); + hstate->merge_hash (m_ranges->get_hash ()); +} + /* class equiv_class_id. */ /* Get the underlying equiv_class for this ID from CM. */ @@ -612,6 +1373,7 @@ equiv_class_id::print (pretty_printer *pp) const constraint_manager::constraint_manager (const constraint_manager &other) : m_equiv_classes (other.m_equiv_classes.length ()), m_constraints (other.m_constraints.length ()), + m_bounded_ranges_constraints (other.m_bounded_ranges_constraints.length ()), m_mgr (other.m_mgr) { int i; @@ -621,6 +1383,8 @@ constraint_manager::constraint_manager (const constraint_manager &other) constraint *c; FOR_EACH_VEC_ELT (other.m_constraints, i, c) m_constraints.quick_push (*c); + for (const auto &iter : other.m_bounded_ranges_constraints) + m_bounded_ranges_constraints.quick_push (iter); } /* constraint_manager's assignment operator. */ @@ -630,6 +1394,7 @@ constraint_manager::operator= (const constraint_manager &other) { gcc_assert (m_equiv_classes.length () == 0); gcc_assert (m_constraints.length () == 0); + gcc_assert (m_bounded_ranges_constraints.length () == 0); int i; equiv_class *ec; @@ -640,6 +1405,8 @@ constraint_manager::operator= (const constraint_manager &other) m_constraints.reserve (other.m_constraints.length ()); FOR_EACH_VEC_ELT (other.m_constraints, i, c) m_constraints.quick_push (*c); + for (const auto &iter : other.m_bounded_ranges_constraints) + m_bounded_ranges_constraints.quick_push (iter); return *this; } @@ -658,6 +1425,8 @@ constraint_manager::hash () const hstate.merge_hash (ec->hash ()); FOR_EACH_VEC_ELT (m_constraints, i, c) hstate.merge_hash (c->hash ()); + for (const auto &iter : m_bounded_ranges_constraints) + iter.add_to_hash (&hstate); return hstate.end (); } @@ -670,6 +1439,9 @@ constraint_manager::operator== (const constraint_manager &other) const return false; if (m_constraints.length () != other.m_constraints.length ()) return false; + if (m_bounded_ranges_constraints.length () + != other.m_bounded_ranges_constraints.length ()) + return false; int i; equiv_class *ec; @@ -684,6 +1456,13 @@ constraint_manager::operator== (const constraint_manager &other) const if (!(*c == other.m_constraints[i])) return false; + for (unsigned i = 0; i < m_bounded_ranges_constraints.length (); i++) + { + if (m_bounded_ranges_constraints[i] + != other.m_bounded_ranges_constraints[i]) + return false; + } + return true; } @@ -711,6 +1490,18 @@ constraint_manager::print (pretty_printer *pp) const pp_string (pp, " && "); c->print (pp, *this); } + if (m_bounded_ranges_constraints.length ()) + { + pp_string (pp, " | "); + i = 0; + for (const auto &iter : m_bounded_ranges_constraints) + { + if (i > 0) + pp_string (pp, " && "); + iter.print (pp, *this); + i++; + } + } pp_printf (pp, "}"); } @@ -762,6 +1553,30 @@ constraint_manager::dump_to_pp (pretty_printer *pp, bool multiline) const } if (!multiline) pp_string (pp, "}"); + if (m_bounded_ranges_constraints.length ()) + { + if (multiline) + pp_string (pp, " "); + pp_string (pp, "ranges:"); + if (multiline) + pp_newline (pp); + else + pp_string (pp, "{"); + i = 0; + for (const auto &iter : m_bounded_ranges_constraints) + { + if (multiline) + pp_string (pp, " "); + else if (i > 0) + pp_string (pp, " && "); + iter.print (pp, *this); + if (multiline) + pp_newline (pp); + i++; + } + if (!multiline) + pp_string (pp, "}"); + } } /* Dump a multiline representation of this constraint_manager to FP. */ @@ -818,6 +1633,14 @@ constraint_manager::to_json () const cm_obj->set ("constraints", con_arr); } + /* m_bounded_ranges_constraints. */ + { + json::array *con_arr = new json::array (); + for (const auto &c : m_bounded_ranges_constraints) + con_arr->append (c.to_json ()); + cm_obj->set ("bounded_ranges_constraints", con_arr); + } + return cm_obj; } @@ -936,6 +1759,8 @@ constraint_manager::add_unknown_constraint (equiv_class_id lhs_ec_id, if (final_ec != old_ec) m_equiv_classes[rhs_ec_id.m_idx] = final_ec; delete old_ec; + if (lhs_ec_id == final_ec_id) + lhs_ec_id = rhs_ec_id; /* Update the constraints. */ constraint *c; @@ -955,6 +1780,14 @@ constraint_manager::add_unknown_constraint (equiv_class_id lhs_ec_id, if (c->m_rhs == final_ec_id) c->m_rhs = rhs_ec_id; } + bounded_ranges_constraint *brc; + FOR_EACH_VEC_ELT (m_bounded_ranges_constraints, i, brc) + { + if (brc->m_ec_id == rhs_ec_id) + brc->m_ec_id = lhs_ec_id; + if (brc->m_ec_id == final_ec_id) + brc->m_ec_id = rhs_ec_id; + } /* We may now have self-comparisons due to the merger; these constraints should be removed. */ @@ -1008,6 +1841,8 @@ constraint_manager::add_constraint_internal (equiv_class_id lhs_id, /* Add the constraint. */ m_constraints.safe_push (new_c); + /* We don't yet update m_bounded_ranges_constraints here yet. */ + if (!flag_analyzer_transitivity) return; @@ -1141,6 +1976,80 @@ constraint_manager::add_constraint_internal (equiv_class_id lhs_id, } } +/* Attempt to add the constraint that SVAL is within RANGES to this + constraint_manager. + + Return true if the constraint was successfully added (or is already + known to be true). + Return false if the constraint contradicts existing knowledge. */ + +bool +constraint_manager::add_bounded_ranges (const svalue *sval, + const bounded_ranges *ranges) +{ + sval = sval->unwrap_any_unmergeable (); + + /* Nothing can be known about unknown/poisoned values. */ + if (!sval->can_have_associated_state_p ()) + /* Not a contradiction. */ + return true; + + /* If SVAL is a constant, then we can look at RANGES directly. */ + if (tree cst = sval->maybe_get_constant ()) + { + /* If the ranges contain CST, then it's a successful no-op; + otherwise it's a contradiction. */ + return ranges->contain_p (cst); + } + + equiv_class_id ec_id = get_or_add_equiv_class (sval); + + /* If the EC has a constant, it's either true or false. */ + const equiv_class &ec = ec_id.get_obj (*this); + if (tree ec_cst = ec.get_any_constant ()) + { + if (ranges->contain_p (ec_cst)) + /* We already have SVAL == EC_CST, within RANGES, so + we can discard RANGES and succeed. */ + return true; + else + /* We already have SVAL == EC_CST, not within RANGES, so + we can reject RANGES as a contradiction. */ + return false; + } + + /* We have at most one per ec_id. */ + /* Iterate through each range in RANGES. */ + for (auto iter : m_bounded_ranges_constraints) + { + if (iter.m_ec_id == ec_id) + { + /* Update with intersection, or fail if empty. */ + bounded_ranges_manager *mgr = get_range_manager (); + const bounded_ranges *intersection + = mgr->get_or_create_intersection (iter.m_ranges, ranges); + if (intersection->empty_p ()) + { + /* No intersection; fail. */ + return false; + } + else + { + /* Update with intersection; succeed. */ + iter.m_ranges = intersection; + validate (); + return true; + } + } + } + m_bounded_ranges_constraints.safe_push + (bounded_ranges_constraint (ec_id, ranges)); + + validate (); + + return true; +} + /* Look for SVAL within the equivalence classes of this constraint_manager; if found, return true, writing the id to *OUT if OUT is non-NULL, otherwise return false. */ @@ -1279,6 +2188,8 @@ constraint_manager::eval_condition (equiv_class_id lhs_ec, } } + /* We don't use m_bounded_ranges_constraints here yet. */ + return tristate (tristate::TS_UNKNOWN); } @@ -1404,6 +2315,12 @@ constraint_manager::eval_condition (equiv_class_id lhs_ec, } } } + + bounded_ranges_manager *mgr = get_range_manager (); + for (const auto &iter : m_bounded_ranges_constraints) + if (iter.m_ec_id == lhs_ec) + return iter.m_ranges->eval_condition (op, rhs_const, mgr); + /* Look at existing bounds on LHS_EC. */ range lhs_bounds = get_ec_bounds (lhs_ec); return lhs_bounds.eval_condition (op, rhs_const); @@ -1552,6 +2469,29 @@ constraint_manager::purge (const PurgeCriteria &p, purge_stats *stats) con_idx++; } } + + /* Update bounded_ranges_constraint instances. */ + for (unsigned r_idx = 0; + r_idx < m_bounded_ranges_constraints.length (); ) + { + bounded_ranges_constraint *brc + = &m_bounded_ranges_constraints[r_idx]; + + /* Remove if it refers to the deleted EC. */ + if (brc->m_ec_id == ec_idx) + { + m_bounded_ranges_constraints.ordered_remove (r_idx); + if (stats) + stats->m_num_bounded_ranges_constraints++; + } + else + { + /* Renumber any EC ids that refer to ECs that have + had their idx changed. */ + brc->m_ec_id.update_for_removal (ec_idx); + r_idx++; + } + } } else ec_idx++; @@ -1610,6 +2550,17 @@ constraint_manager::purge (const PurgeCriteria &p, purge_stats *stats) c->m_lhs.update_for_removal (ec_idx); c->m_rhs.update_for_removal (ec_idx); } + + /* Likewise for m_bounded_ranges_constraints. */ + for (unsigned r_idx = 0; + r_idx < m_bounded_ranges_constraints.length (); + r_idx++) + { + bounded_ranges_constraint *brc + = &m_bounded_ranges_constraints[r_idx]; + brc->m_ec_id.update_for_removal (ec_idx); + } + continue; } } @@ -1751,6 +2702,9 @@ constraint_manager::canonicalize () used_ecs.add (m_equiv_classes[c->m_rhs.as_int ()]); } + for (const auto &iter : m_bounded_ranges_constraints) + used_ecs.add (m_equiv_classes[iter.m_ec_id.as_int ()]); + /* Purge unused ECs: those that aren't used by constraints and that effectively have only one svalue (either in m_constant or in m_vars). */ @@ -1791,6 +2745,9 @@ constraint_manager::canonicalize () ec_id_map.update (&c->m_rhs); } + for (auto &iter : m_bounded_ranges_constraints) + ec_id_map.update (&iter.m_ec_id); + /* Finally, sort the constraints. */ m_constraints.qsort (constraint_cmp); } @@ -1835,6 +2792,32 @@ public: } } + void on_ranges (const svalue *lhs_sval, + const bounded_ranges *ranges) FINAL OVERRIDE + { + for (const auto &iter : m_cm_b->m_bounded_ranges_constraints) + { + const equiv_class &ec_rhs = iter.m_ec_id.get_obj (*m_cm_b); + for (unsigned i = 0; i < ec_rhs.m_vars.length (); i++) + { + const svalue *rhs_sval = ec_rhs.m_vars[i]; + if (lhs_sval == rhs_sval) + { + /* Union of the two ranges. */ + auto_vec <const bounded_ranges *> pair (2); + pair.quick_push (ranges); + pair.quick_push (iter.m_ranges); + bounded_ranges_manager *ranges_mgr + = m_cm_b->get_range_manager (); + const bounded_ranges *union_ + = ranges_mgr->get_or_create_union (pair); + bool sat = m_out->add_bounded_ranges (lhs_sval, union_); + gcc_assert (sat); + } + } + } + } + private: const constraint_manager *m_cm_b; constraint_manager *m_out; @@ -1908,6 +2891,16 @@ constraint_manager::for_each_fact (fact_visitor *visitor) const visitor->on_fact (ec_lhs.m_vars[i], code, ec_rhs.m_vars[j]); } } + + for (const auto &iter : m_bounded_ranges_constraints) + { + const equiv_class &ec_lhs = iter.m_ec_id.get_obj (*this); + for (unsigned i = 0; i < ec_lhs.m_vars.length (); i++) + { + const svalue *lhs_sval = ec_lhs.m_vars[i]; + visitor->on_ranges (lhs_sval, iter.m_ranges); + } + } } /* Assert that this object is valid. */ @@ -1945,10 +2938,22 @@ constraint_manager::validate () const FOR_EACH_VEC_ELT (m_constraints, i, c) { gcc_assert (!c->m_lhs.null_p ()); - gcc_assert (c->m_lhs.as_int () <= (int)m_equiv_classes.length ()); + gcc_assert (c->m_lhs.as_int () < (int)m_equiv_classes.length ()); gcc_assert (!c->m_rhs.null_p ()); - gcc_assert (c->m_rhs.as_int () <= (int)m_equiv_classes.length ()); + gcc_assert (c->m_rhs.as_int () < (int)m_equiv_classes.length ()); } + + for (const auto &iter : m_bounded_ranges_constraints) + { + gcc_assert (!iter.m_ec_id.null_p ()); + gcc_assert (iter.m_ec_id.as_int () < (int)m_equiv_classes.length ()); + } +} + +bounded_ranges_manager * +constraint_manager::get_range_manager () const +{ + return m_mgr->get_range_manager (); } #if CHECKING_P @@ -2696,6 +3701,318 @@ test_many_constants () } } +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_range_eq (const location &loc, + const bounded_range &range, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + range.dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Assert that BR.dump (false) is EXPECTED. */ + +#define ASSERT_DUMP_BOUNDED_RANGE_EQ(BR, EXPECTED) \ + SELFTEST_BEGIN_STMT \ + assert_dump_bounded_range_eq ((SELFTEST_LOCATION), (BR), (EXPECTED)); \ + SELFTEST_END_STMT + +/* Verify that bounded_range works as expected. */ + +static void +test_bounded_range () +{ + tree u8_0 = build_int_cst (unsigned_char_type_node, 0); + tree u8_1 = build_int_cst (unsigned_char_type_node, 1); + tree u8_64 = build_int_cst (unsigned_char_type_node, 64); + tree u8_128 = build_int_cst (unsigned_char_type_node, 128); + tree u8_255 = build_int_cst (unsigned_char_type_node, 255); + + tree s8_0 = build_int_cst (signed_char_type_node, 0); + tree s8_1 = build_int_cst (signed_char_type_node, 1); + tree s8_2 = build_int_cst (signed_char_type_node, 2); + + bounded_range br_u8_0 (u8_0, u8_0); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_0, "0"); + ASSERT_TRUE (br_u8_0.contains_p (u8_0)); + ASSERT_FALSE (br_u8_0.contains_p (u8_1)); + ASSERT_TRUE (br_u8_0.contains_p (s8_0)); + ASSERT_FALSE (br_u8_0.contains_p (s8_1)); + + bounded_range br_u8_0_1 (u8_0, u8_1); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_0_1, "[0, 1]"); + + bounded_range tmp (NULL_TREE, NULL_TREE); + ASSERT_TRUE (br_u8_0.intersects_p (br_u8_0_1, &tmp)); + ASSERT_DUMP_BOUNDED_RANGE_EQ (tmp, "0"); + + bounded_range br_u8_64_128 (u8_64, u8_128); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_64_128, "[64, 128]"); + + ASSERT_FALSE (br_u8_0.intersects_p (br_u8_64_128, NULL)); + ASSERT_FALSE (br_u8_64_128.intersects_p (br_u8_0, NULL)); + + bounded_range br_u8_128_255 (u8_128, u8_255); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_u8_128_255, "[128, 255]"); + ASSERT_TRUE (br_u8_128_255.intersects_p (br_u8_64_128, &tmp)); + ASSERT_DUMP_BOUNDED_RANGE_EQ (tmp, "128"); + + bounded_range br_s8_2 (s8_2, s8_2); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_s8_2, "2"); + bounded_range br_s8_2_u8_255 (s8_2, u8_255); + ASSERT_DUMP_BOUNDED_RANGE_EQ (br_s8_2_u8_255, "[2, 255]"); +} + +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_ranges_eq (const location &loc, + const bounded_ranges *ranges, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + ranges->dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Implementation detail of ASSERT_DUMP_BOUNDED_RANGES_EQ. */ + +static void +assert_dump_bounded_ranges_eq (const location &loc, + const bounded_ranges &ranges, + const char *expected) +{ + auto_fix_quotes sentinel; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + ranges.dump_to_pp (&pp, false); + ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected); +} + +/* Assert that BRS.dump (false) is EXPECTED. */ + +#define ASSERT_DUMP_BOUNDED_RANGES_EQ(BRS, EXPECTED) \ + SELFTEST_BEGIN_STMT \ + assert_dump_bounded_ranges_eq ((SELFTEST_LOCATION), (BRS), (EXPECTED)); \ + SELFTEST_END_STMT + +/* Verify that the bounded_ranges class works as expected. */ + +static void +test_bounded_ranges () +{ + bounded_ranges_manager mgr; + + tree ch0 = build_int_cst (unsigned_char_type_node, 0); + tree ch1 = build_int_cst (unsigned_char_type_node, 1); + tree ch2 = build_int_cst (unsigned_char_type_node, 2); + tree ch3 = build_int_cst (unsigned_char_type_node, 3); + tree ch128 = build_int_cst (unsigned_char_type_node, 128); + tree ch129 = build_int_cst (unsigned_char_type_node, 129); + tree ch254 = build_int_cst (unsigned_char_type_node, 254); + tree ch255 = build_int_cst (unsigned_char_type_node, 255); + + const bounded_ranges *empty = mgr.get_or_create_empty (); + ASSERT_DUMP_BOUNDED_RANGES_EQ (empty, "{}"); + + const bounded_ranges *point0 = mgr.get_or_create_point (ch0); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point0, "{0}"); + + const bounded_ranges *point1 = mgr.get_or_create_point (ch1); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point1, "{1}"); + + const bounded_ranges *point2 = mgr.get_or_create_point (ch2); + ASSERT_DUMP_BOUNDED_RANGES_EQ (point2, "{2}"); + + const bounded_ranges *range0_128 = mgr.get_or_create_range (ch0, ch128); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range0_128, "{[0, 128]}"); + + const bounded_ranges *range0_255 = mgr.get_or_create_range (ch0, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range0_255, "{[0, 255]}"); + + ASSERT_FALSE (empty->contain_p (ch0)); + ASSERT_FALSE (empty->contain_p (ch1)); + ASSERT_FALSE (empty->contain_p (ch255)); + + ASSERT_TRUE (point0->contain_p (ch0)); + ASSERT_FALSE (point0->contain_p (ch1)); + ASSERT_FALSE (point0->contain_p (ch255)); + + ASSERT_FALSE (point1->contain_p (ch0)); + ASSERT_TRUE (point1->contain_p (ch1)); + ASSERT_FALSE (point0->contain_p (ch255)); + + ASSERT_TRUE (range0_128->contain_p (ch0)); + ASSERT_TRUE (range0_128->contain_p (ch1)); + ASSERT_TRUE (range0_128->contain_p (ch128)); + ASSERT_FALSE (range0_128->contain_p (ch129)); + ASSERT_FALSE (range0_128->contain_p (ch254)); + ASSERT_FALSE (range0_128->contain_p (ch255)); + + const bounded_ranges *inv0_128 + = mgr.get_or_create_inverse (range0_128, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv0_128, "{[129, 255]}"); + + const bounded_ranges *range128_129 = mgr.get_or_create_range (ch128, ch129); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range128_129, "{[128, 129]}"); + + const bounded_ranges *inv128_129 + = mgr.get_or_create_inverse (range128_129, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv128_129, "{[0, 127], [130, 255]}"); + + /* Intersection. */ + { + /* Intersection of disjoint ranges should be empty set. */ + const bounded_ranges *intersect0_1 + = mgr.get_or_create_intersection (point0, point1); + ASSERT_DUMP_BOUNDED_RANGES_EQ (intersect0_1, "{}"); + } + + /* Various tests of "union of ranges". */ + { + { + /* Touching points should be merged into a range. */ + auto_vec <const bounded_ranges *> v; + v.safe_push (point0); + v.safe_push (point1); + const bounded_ranges *union_0_and_1 = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_0_and_1, "{[0, 1]}"); + } + + { + /* Overlapping and out-of-order. */ + auto_vec <const bounded_ranges *> v; + v.safe_push (inv0_128); // {[129, 255]} + v.safe_push (range128_129); + const bounded_ranges *union_129_255_and_128_129 + = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_129_255_and_128_129, "{[128, 255]}"); + } + + { + /* Union of R and inverse(R) should be full range of type. */ + auto_vec <const bounded_ranges *> v; + v.safe_push (range128_129); + v.safe_push (inv128_129); + const bounded_ranges *union_ = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_, "{[0, 255]}"); + } + + /* Union with an endpoint. */ + { + const bounded_ranges *range2_to_255 + = mgr.get_or_create_range (ch2, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range2_to_255, "{[2, 255]}"); + auto_vec <const bounded_ranges *> v; + v.safe_push (point0); + v.safe_push (point2); + v.safe_push (range2_to_255); + const bounded_ranges *union_ = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (union_, "{0, [2, 255]}"); + } + + /* Construct from vector of bounded_range. */ + { + auto_vec<bounded_range> v; + v.safe_push (bounded_range (ch2, ch2)); + v.safe_push (bounded_range (ch0, ch0)); + v.safe_push (bounded_range (ch2, ch255)); + bounded_ranges br (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (&br, "{0, [2, 255]}"); + } + } + + /* Various tests of "inverse". */ + { + { + const bounded_ranges *range_1_to_3 = mgr.get_or_create_range (ch1, ch3); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_1_to_3, "{[1, 3]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_1_to_3, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{0, [4, 255]}"); + } + { + const bounded_ranges *range_1_to_255 + = mgr.get_or_create_range (ch1, ch255); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_1_to_255, "{[1, 255]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_1_to_255, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{0}"); + } + { + const bounded_ranges *range_0_to_254 + = mgr.get_or_create_range (ch0, ch254); + ASSERT_DUMP_BOUNDED_RANGES_EQ (range_0_to_254, "{[0, 254]}"); + const bounded_ranges *inv + = mgr.get_or_create_inverse (range_0_to_254, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (inv, "{255}"); + } + } + + /* "case 'a'-'z': case 'A-Z':" vs "default:", for ASCII. */ + { + tree ch65 = build_int_cst (unsigned_char_type_node, 65); + tree ch90 = build_int_cst (unsigned_char_type_node, 90); + + tree ch97 = build_int_cst (unsigned_char_type_node, 97); + tree ch122 = build_int_cst (unsigned_char_type_node, 122); + + const bounded_ranges *A_to_Z = mgr.get_or_create_range (ch65, ch90); + ASSERT_DUMP_BOUNDED_RANGES_EQ (A_to_Z, "{[65, 90]}"); + const bounded_ranges *a_to_z = mgr.get_or_create_range (ch97, ch122); + ASSERT_DUMP_BOUNDED_RANGES_EQ (a_to_z, "{[97, 122]}"); + auto_vec <const bounded_ranges *> v; + v.safe_push (A_to_Z); + v.safe_push (a_to_z); + const bounded_ranges *label_ranges = mgr.get_or_create_union (v); + ASSERT_DUMP_BOUNDED_RANGES_EQ (label_ranges, "{[65, 90], [97, 122]}"); + const bounded_ranges *default_ranges + = mgr.get_or_create_inverse (label_ranges, unsigned_char_type_node); + ASSERT_DUMP_BOUNDED_RANGES_EQ (default_ranges, + "{[0, 64], [91, 96], [123, 255]}"); + } + + /* Verify ranges from ops. */ + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (EQ_EXPR, ch128), + "{128}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch128), + "{[0, 127], [129, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LT_EXPR, ch128), + "{[0, 127]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LE_EXPR, ch128), + "{[0, 128]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GE_EXPR, ch128), + "{[128, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GT_EXPR, ch128), + "{[129, 255]}"); + /* Ops at endpoints of type ranges. */ + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LE_EXPR, ch0), + "{0}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (LT_EXPR, ch0), + "{}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch0), + "{[1, 255]}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GE_EXPR, ch255), + "{255}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (GT_EXPR, ch255), + "{}"); + ASSERT_DUMP_BOUNDED_RANGES_EQ (bounded_ranges (NE_EXPR, ch255), + "{[0, 254]}"); + + /* Verify that instances are consolidated by mgr. */ + ASSERT_EQ (mgr.get_or_create_point (ch0), + mgr.get_or_create_point (ch0)); + ASSERT_NE (mgr.get_or_create_point (ch0), + mgr.get_or_create_point (ch1)); +} + /* Run the selftests in this file, temporarily overriding flag_analyzer_transitivity with TRANSITIVITY. */ @@ -2715,6 +4032,8 @@ run_constraint_manager_tests (bool transitivity) test_constraint_impl (); test_equality (); test_many_constants (); + test_bounded_range (); + test_bounded_ranges (); flag_analyzer_transitivity = saved_flag_analyzer_transitivity; } diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 2bb3215..0a430ea 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -64,6 +64,164 @@ struct range bound m_upper_bound; }; +/* A closed range of values with constant integer bounds + e.g. [3, 5] for the set {3, 4, 5}. */ + +struct bounded_range +{ + bounded_range (const_tree lower, const_tree upper); + + void dump_to_pp (pretty_printer *pp, bool show_types) const; + void dump (bool show_types) const; + + json::object *to_json () const; + + bool contains_p (tree cst) const; + + bool intersects_p (const bounded_range &other, + bounded_range *out) const; + + bool operator== (const bounded_range &other) const; + bool operator!= (const bounded_range &other) const + { + return !(*this == other); + } + + static int cmp (const bounded_range &a, const bounded_range &b); + + tree m_lower; + tree m_upper; + +private: + static void set_json_attr (json::object *obj, const char *name, tree value); +}; + +/* A collection of bounded_range instances, suitable + for representing the ranges on a case label within a switch + statement. */ + +struct bounded_ranges +{ +public: + typedef bounded_ranges key_t; + + bounded_ranges (const bounded_range &range); + bounded_ranges (const vec<bounded_range> &ranges); + bounded_ranges (enum tree_code op, tree rhs_const); + + bool operator== (const bounded_ranges &other) const; + + hashval_t get_hash () const { return m_hash; } + + void dump_to_pp (pretty_printer *pp, bool show_types) const; + void dump (bool show_types) const; + + json::value *to_json () const; + + tristate eval_condition (enum tree_code op, + tree rhs_const, + bounded_ranges_manager *mgr) const; + + bool contain_p (tree cst) const; + bool empty_p () const { return m_ranges.length () == 0; } + + static int cmp (const bounded_ranges *a, const bounded_ranges *b); + +private: + void canonicalize (); + void validate () const; + + friend class bounded_ranges_manager; + + auto_vec<bounded_range> m_ranges; + hashval_t m_hash; +}; + +} // namespace ana + +template <> struct default_hash_traits<bounded_ranges::key_t> +: public member_function_hash_traits<bounded_ranges::key_t> +{ + static const bool empty_zero_p = true; +}; + +namespace ana { + +/* An object to own and consolidate bounded_ranges instances. + This also caches the mapping from switch_cfg_superedge + bounded_ranges instances, so that get_or_create_ranges_for_switch is + memoized. */ + +class bounded_ranges_manager +{ +public: + ~bounded_ranges_manager (); + + const bounded_ranges * + get_or_create_ranges_for_switch (const switch_cfg_superedge *edge, + const gswitch *switch_stmt); + + const bounded_ranges *get_or_create_empty (); + const bounded_ranges *get_or_create_point (const_tree value); + const bounded_ranges *get_or_create_range (const_tree lower_bound, + const_tree upper_bound); + const bounded_ranges * + get_or_create_union (const vec <const bounded_ranges *> &others); + const bounded_ranges * + get_or_create_intersection (const bounded_ranges *a, + const bounded_ranges *b); + const bounded_ranges * + get_or_create_inverse (const bounded_ranges *other, tree type); + + void log_stats (logger *logger, bool show_objs) const; + +private: + const bounded_ranges * + create_ranges_for_switch (const switch_cfg_superedge &edge, + const gswitch *switch_stmt); + + const bounded_ranges * + make_case_label_ranges (const gswitch *switch_stmt, + tree case_label); + + const bounded_ranges *consolidate (bounded_ranges *); + + struct hash_traits_t : public typed_noop_remove<bounded_ranges *> + { + typedef bounded_ranges *key_type; + typedef bounded_ranges *value_type; + + static inline bool + equal (const key_type &k1, const key_type &k2) + { + return *k1 == *k2; + } + static inline hashval_t + hash (const key_type &k) + { + return k->get_hash (); + } + static inline bool is_empty (key_type k) { return k == NULL; } + static inline void mark_empty (key_type &k) { k = NULL; } + static inline bool is_deleted (key_type k) + { + return k == reinterpret_cast<key_type> (1); + } + + static const bool empty_zero_p = true; + }; + struct traits_t : public simple_hashmap_traits<hash_traits_t, + bounded_ranges *> + { + }; + typedef hash_map<bounded_ranges *, bounded_ranges *, traits_t> map_t; + map_t m_map; + + typedef hash_map<const switch_cfg_superedge *, + const bounded_ranges *> edge_cache_t; + edge_cache_t m_edge_cache; +}; + /* An equivalence class within a constraint manager: a set of svalues that are known to all be equal to each other, together with an optional tree constant that they are equal to. */ @@ -190,6 +348,33 @@ class fact_visitor virtual void on_fact (const svalue *lhs, enum tree_code, const svalue *rhs) = 0; + virtual void on_ranges (const svalue *lhs, + const bounded_ranges *ranges) = 0; +}; + +class bounded_ranges_constraint +{ +public: + bounded_ranges_constraint (equiv_class_id ec_id, + const bounded_ranges *ranges) + : m_ec_id (ec_id), m_ranges (ranges) + { + } + + void print (pretty_printer *pp, const constraint_manager &cm) const; + + json::object *to_json () const; + + bool operator== (const bounded_ranges_constraint &other) const; + bool operator!= (const bounded_ranges_constraint &other) const + { + return !(*this == other); + } + + void add_to_hash (inchash::hash *hstate) const; + + equiv_class_id m_ec_id; + const bounded_ranges *m_ranges; }; /* A collection of equivalence classes and constraints on them. @@ -248,6 +433,9 @@ public: enum tree_code op, equiv_class_id rhs_ec_id); + bool add_bounded_ranges (const svalue *sval, + const bounded_ranges *ranges); + bool get_equiv_class_by_svalue (const svalue *sval, equiv_class_id *out) const; equiv_class_id get_or_add_equiv_class (const svalue *sval); @@ -281,8 +469,11 @@ public: void validate () const; + bounded_ranges_manager *get_range_manager () const; + auto_delete_vec<equiv_class> m_equiv_classes; auto_vec<constraint> m_constraints; + auto_vec<bounded_ranges_constraint> m_bounded_ranges_constraints; private: void add_constraint_internal (equiv_class_id lhs_id, diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 77dda4d..7ffe000 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -520,8 +520,7 @@ epath_finder::process_worklist_item (feasible_worklist *worklist, gcc_assert (rc); fg->add_feasibility_problem (fnode, succ_eedge, - *rc); - delete rc; + rc); /* Give up if there have been too many infeasible edges. */ if (fg->get_num_infeasible () diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index e66ca4e..4ee9279 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -3842,7 +3842,7 @@ feasibility_problem::dump_to_pp (pretty_printer *pp) const pp_string (pp, "; rejected constraint: "); m_rc->dump_to_pp (pp); pp_string (pp, "; rmodel: "); - m_rc->m_model.dump_to_pp (pp, true, false); + m_rc->get_model ().dump_to_pp (pp, true, false); } } diff --git a/gcc/analyzer/feasible-graph.cc b/gcc/analyzer/feasible-graph.cc index 675bda9..3b85896 100644 --- a/gcc/analyzer/feasible-graph.cc +++ b/gcc/analyzer/feasible-graph.cc @@ -129,7 +129,7 @@ infeasible_node::dump_dot (graphviz_out *gv, pp_string (pp, "rejected constraint:"); pp_newline (pp); - m_rc.dump_to_pp (pp); + m_rc->dump_to_pp (pp); pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true); @@ -178,12 +178,13 @@ feasible_graph::add_node (const exploded_node *enode, } /* Add an infeasible_node to this graph and an infeasible_edge connecting - to it from SRC_FNODE, capturing a failure of RC along EEDGE. */ + to it from SRC_FNODE, capturing a failure of RC along EEDGE. + Takes ownership of RC. */ void feasible_graph::add_feasibility_problem (feasible_node *src_fnode, const exploded_edge *eedge, - const rejected_constraint &rc) + rejected_constraint *rc) { infeasible_node *dst_fnode = new infeasible_node (eedge->m_dest, m_nodes.length (), rc); diff --git a/gcc/analyzer/feasible-graph.h b/gcc/analyzer/feasible-graph.h index 5a580f4..07696fa 100644 --- a/gcc/analyzer/feasible-graph.h +++ b/gcc/analyzer/feasible-graph.h @@ -115,17 +115,18 @@ class infeasible_node : public base_feasible_node { public: infeasible_node (const exploded_node *inner_node, unsigned index, - const rejected_constraint &rc) + rejected_constraint *rc) : base_feasible_node (inner_node, index), m_rc (rc) { } + ~infeasible_node () { delete m_rc; } void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE; private: - rejected_constraint m_rc; + rejected_constraint *m_rc; }; /* Base class of edge within a feasible_graph. */ @@ -192,7 +193,7 @@ class feasible_graph : public digraph <fg_traits> void add_feasibility_problem (feasible_node *src_fnode, const exploded_edge *eedge, - const rejected_constraint &rc); + rejected_constraint *rc); exploded_path *make_epath (feasible_node *fnode) const; diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index 9e4644f..1cdec1b 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/program-point.h" #include "analyzer/store.h" #include "analyzer/region-model.h" +#include "analyzer/constraint-manager.h" #if ENABLE_ANALYZER @@ -77,7 +78,8 @@ region_model_manager::region_model_manager () m_fndecls_map (), m_labels_map (), m_globals_region (alloc_region_id (), &m_root_region), m_globals_map (), - m_store_mgr (this) + m_store_mgr (this), + m_range_mgr (new bounded_ranges_manager ()) { } @@ -142,6 +144,8 @@ region_model_manager::~region_model_manager () for (string_map_t::iterator iter = m_string_map.begin (); iter != m_string_map.end (); ++iter) delete (*iter).second; + + delete m_range_mgr; } /* Return true if C exceeds the complexity limit for svalues. */ @@ -1574,6 +1578,7 @@ region_model_manager::log_stats (logger *logger, bool show_objs) const logger->log (" # managed dynamic regions: %i", m_managed_dynamic_regions.length ()); m_store_mgr.log_stats (logger, show_objs); + m_range_mgr->log_stats (logger, show_objs); } /* Dump the number of objects of each class that were managed by this diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 9870007..787f2ed 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2488,34 +2488,51 @@ region_model::eval_condition_without_cm (const svalue *lhs, if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) return constant_svalue::eval_condition (cst_lhs, op, cst_rhs); - /* Handle comparison of a region_svalue against zero. */ - - if (const region_svalue *ptr = lhs->dyn_cast_region_svalue ()) - if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) - if (zerop (cst_rhs->get_constant ())) - { - /* A region_svalue is a non-NULL pointer, except in certain - special cases (see the comment for region::non_null_p. */ - const region *pointee = ptr->get_pointee (); - if (pointee->non_null_p ()) - { - switch (op) - { - default: - gcc_unreachable (); - - case EQ_EXPR: - case GE_EXPR: - case LE_EXPR: - return tristate::TS_FALSE; - - case NE_EXPR: - case GT_EXPR: - case LT_EXPR: - return tristate::TS_TRUE; - } - } - } + /* Handle comparison against zero. */ + if (const constant_svalue *cst_rhs = rhs->dyn_cast_constant_svalue ()) + if (zerop (cst_rhs->get_constant ())) + { + if (const region_svalue *ptr = lhs->dyn_cast_region_svalue ()) + { + /* A region_svalue is a non-NULL pointer, except in certain + special cases (see the comment for region::non_null_p). */ + const region *pointee = ptr->get_pointee (); + if (pointee->non_null_p ()) + { + switch (op) + { + default: + gcc_unreachable (); + + case EQ_EXPR: + case GE_EXPR: + case LE_EXPR: + return tristate::TS_FALSE; + + case NE_EXPR: + case GT_EXPR: + case LT_EXPR: + return tristate::TS_TRUE; + } + } + } + else if (const binop_svalue *binop = lhs->dyn_cast_binop_svalue ()) + { + /* Treat offsets from a non-NULL pointer as being non-NULL. This + isn't strictly true, in that eventually ptr++ will wrap + around and be NULL, but it won't occur in practise and thus + can be used to suppress effectively false positives that we + shouldn't warn for. */ + if (binop->get_op () == POINTER_PLUS_EXPR) + { + tristate lhs_ts + = eval_condition_without_cm (binop->get_arg0 (), + op, rhs); + if (lhs_ts.is_known ()) + return lhs_ts; + } + } + } /* Handle rejection of equality for comparisons of the initial values of "external" values (such as params) with the address of locals. */ @@ -2756,7 +2773,7 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs, { bool sat = add_constraint (lhs, op, rhs, ctxt); if (!sat && out) - *out = new rejected_constraint (*this, lhs, op, rhs); + *out = new rejected_op_constraint (*this, lhs, op, rhs); return sat; } @@ -3312,56 +3329,15 @@ region_model::apply_constraints_for_gswitch (const switch_cfg_superedge &edge, region_model_context *ctxt, rejected_constraint **out) { + bounded_ranges_manager *ranges_mgr = get_range_manager (); + const bounded_ranges *all_cases_ranges + = ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt); tree index = gimple_switch_index (switch_stmt); - tree case_label = edge.get_case_label (); - gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); - tree lower_bound = CASE_LOW (case_label); - tree upper_bound = CASE_HIGH (case_label); - if (lower_bound) - { - if (upper_bound) - { - /* Range. */ - if (!add_constraint (index, GE_EXPR, lower_bound, ctxt, out)) - return false; - return add_constraint (index, LE_EXPR, upper_bound, ctxt, out); - } - else - /* Single-value. */ - return add_constraint (index, EQ_EXPR, lower_bound, ctxt, out); - } - else - { - /* The default case. - Add exclusions based on the other cases. */ - for (unsigned other_idx = 1; - other_idx < gimple_switch_num_labels (switch_stmt); - other_idx++) - { - tree other_label = gimple_switch_label (switch_stmt, - other_idx); - tree other_lower_bound = CASE_LOW (other_label); - tree other_upper_bound = CASE_HIGH (other_label); - gcc_assert (other_lower_bound); - if (other_upper_bound) - { - /* Exclude this range-valued case. - For now, we just exclude the boundary values. - TODO: exclude the values within the region. */ - if (!add_constraint (index, NE_EXPR, other_lower_bound, - ctxt, out)) - return false; - if (!add_constraint (index, NE_EXPR, other_upper_bound, - ctxt, out)) - return false; - } - else - /* Exclude this single-valued case. */ - if (!add_constraint (index, NE_EXPR, other_lower_bound, ctxt, out)) - return false; - } - return true; - } + const svalue *index_sval = get_rvalue (index, ctxt); + bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges); + if (!sat && out) + *out = new rejected_ranges_constraint (*this, index, all_cases_ranges); + return sat; } /* Apply any constraints due to an exception being thrown at LAST_STMT. @@ -3843,10 +3819,10 @@ debug (const region_model &rmodel) rmodel.dump (false); } -/* struct rejected_constraint. */ +/* class rejected_op_constraint : public rejected_constraint. */ void -rejected_constraint::dump_to_pp (pretty_printer *pp) const +rejected_op_constraint::dump_to_pp (pretty_printer *pp) const { region_model m (m_model); const svalue *lhs_sval = m.get_rvalue (m_lhs, NULL); @@ -3856,6 +3832,18 @@ rejected_constraint::dump_to_pp (pretty_printer *pp) const rhs_sval->dump_to_pp (pp, true); } +/* class rejected_ranges_constraint : public rejected_constraint. */ + +void +rejected_ranges_constraint::dump_to_pp (pretty_printer *pp) const +{ + region_model m (m_model); + const svalue *sval = m.get_rvalue (m_expr, NULL); + sval->dump_to_pp (pp, true); + pp_string (pp, " in "); + m_ranges->dump_to_pp (pp, true); +} + /* class engine. */ /* Dump the managed objects by class to LOGGER, and the per-class totals. */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index a734f9f..f2c82b0 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -189,6 +189,7 @@ struct purge_stats m_num_regions (0), m_num_equiv_classes (0), m_num_constraints (0), + m_num_bounded_ranges_constraints (0), m_num_client_items (0) {} @@ -196,6 +197,7 @@ struct purge_stats int m_num_regions; int m_num_equiv_classes; int m_num_constraints; + int m_num_bounded_ranges_constraints; int m_num_client_items; }; @@ -320,6 +322,7 @@ public: unsigned alloc_region_id () { return m_next_region_id++; } store_manager *get_store_manager () { return &m_store_mgr; } + bounded_ranges_manager *get_range_manager () const { return m_range_mgr; } /* Dynamically-allocated region instances. The number of these within the analysis can grow arbitrarily. @@ -456,6 +459,8 @@ private: store_manager m_store_mgr; + bounded_ranges_manager *m_range_mgr; + /* "Dynamically-allocated" region instances. The number of these within the analysis can grow arbitrarily. They are still owned by the manager. */ @@ -698,6 +703,10 @@ class region_model void unset_dynamic_extents (const region *reg); region_model_manager *get_manager () const { return m_mgr; } + bounded_ranges_manager *get_range_manager () const + { + return m_mgr->get_range_manager (); + } void unbind_region_and_descendents (const region *reg, enum poison_kind pkind); @@ -945,21 +954,54 @@ struct model_merger /* A record that can (optionally) be written out when region_model::add_constraint fails. */ -struct rejected_constraint +class rejected_constraint { - rejected_constraint (const region_model &model, - tree lhs, enum tree_code op, tree rhs) - : m_model (model), m_lhs (lhs), m_op (op), m_rhs (rhs) - {} +public: + virtual ~rejected_constraint () {} + virtual void dump_to_pp (pretty_printer *pp) const = 0; - void dump_to_pp (pretty_printer *pp) const; + const region_model &get_model () const { return m_model; } + +protected: + rejected_constraint (const region_model &model) + : m_model (model) + {} region_model m_model; +}; + +class rejected_op_constraint : public rejected_constraint +{ +public: + rejected_op_constraint (const region_model &model, + tree lhs, enum tree_code op, tree rhs) + : rejected_constraint (model), + m_lhs (lhs), m_op (op), m_rhs (rhs) + {} + + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + tree m_lhs; enum tree_code m_op; tree m_rhs; }; +class rejected_ranges_constraint : public rejected_constraint +{ +public: + rejected_ranges_constraint (const region_model &model, + tree expr, const bounded_ranges *ranges) + : rejected_constraint (model), + m_expr (expr), m_ranges (ranges) + {} + + void dump_to_pp (pretty_printer *pp) const FINAL OVERRIDE; + +private: + tree m_expr; + const bounded_ranges *m_ranges; +}; + /* A bundle of state. */ class engine diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index 6a17019..0c8cdf0 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -125,11 +125,21 @@ public: return label_text::borrow ("opened here"); if (change.m_old_state == m_sm.m_unchecked && change.m_new_state == m_sm.m_nonnull) - return change.formatted_print ("assuming %qE is non-NULL", - change.m_expr); + { + if (change.m_expr) + return change.formatted_print ("assuming %qE is non-NULL", + change.m_expr); + else + return change.formatted_print ("assuming FILE * is non-NULL"); + } if (change.m_new_state == m_sm.m_null) - return change.formatted_print ("assuming %qE is NULL", - change.m_expr); + { + if (change.m_expr) + return change.formatted_print ("assuming %qE is NULL", + change.m_expr); + else + return change.formatted_print ("assuming FILE * is NULL"); + } return label_text (); } diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index eac1295..3760858 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -253,6 +253,35 @@ bit_range::contains_p (const bit_range &other, bit_range *out) const return false; } +/* If OTHER intersects this, return true and write + the relative range of OTHER within THIS to *OUT_THIS, + and the relative range of THIS within OTHER to *OUT_OTHER. + Otherwise return false. */ + +bool +bit_range::intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const +{ + if (get_start_bit_offset () < other.get_next_bit_offset () + && other.get_start_bit_offset () < get_next_bit_offset ()) + { + bit_offset_t overlap_start + = MAX (get_start_bit_offset (), + other.get_start_bit_offset ()); + bit_offset_t overlap_next + = MIN (get_next_bit_offset (), + other.get_next_bit_offset ()); + gcc_assert (overlap_next > overlap_start); + bit_range abs_overlap_bits (overlap_start, overlap_next - overlap_start); + *out_this = abs_overlap_bits - get_start_bit_offset (); + *out_other = abs_overlap_bits - other.get_start_bit_offset (); + return true; + } + else + return false; +} + int bit_range::cmp (const bit_range &br1, const bit_range &br2) { @@ -263,6 +292,14 @@ bit_range::cmp (const bit_range &br1, const bit_range &br2) return wi::cmpu (br1.m_size_in_bits, br2.m_size_in_bits); } +/* Offset this range by OFFSET. */ + +bit_range +bit_range::operator- (bit_offset_t offset) const +{ + return bit_range (m_start_bit_offset - offset, m_size_in_bits); +} + /* If MASK is a contiguous range of set bits, write them to *OUT and return true. Otherwise return false. */ @@ -1570,9 +1607,29 @@ binding_cluster::maybe_get_compound_binding (store_manager *mgr, } else { - /* REG and the bound range partially overlap. - We don't handle this case yet. */ - return NULL; + /* REG and the bound range partially overlap. */ + bit_range reg_subrange (0, 0); + bit_range bound_subrange (0, 0); + reg_range.intersects_p (bound_range, + ®_subrange, &bound_subrange); + + /* Get the bits from the bound value for the bits at the + intersection (relative to the bound value). */ + const svalue *overlap_sval + = sval->extract_bit_range (NULL_TREE, + bound_subrange, + mgr->get_svalue_manager ()); + + /* Get key for overlap, relative to the REG. */ + const concrete_binding *overlap_concrete_key + = mgr->get_concrete_binding (reg_subrange); + result_map.put (overlap_concrete_key, overlap_sval); + + /* Clobber default_map, removing/trimming/spliting where + it overlaps with overlap_concrete_key. */ + default_map.remove_overlapping_bindings (mgr, + overlap_concrete_key, + NULL); } } else @@ -2905,6 +2962,20 @@ test_bit_range_intersects_p () ASSERT_FALSE (b3_to_5.intersects_p (b6_to_7)); ASSERT_FALSE (b6_to_7.intersects_p (b3_to_5)); + + bit_range r1 (0,0); + bit_range r2 (0,0); + ASSERT_TRUE (b1_to_6.intersects_p (b0_to_7, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 0); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 1); + ASSERT_EQ (r2.m_size_in_bits, 6); + + ASSERT_TRUE (b0_to_7.intersects_p (b1_to_6, &r1, &r2)); + ASSERT_EQ (r1.get_start_bit_offset (), 1); + ASSERT_EQ (r1.m_size_in_bits, 6); + ASSERT_EQ (r2.get_start_bit_offset (), 0); + ASSERT_EQ (r2.m_size_in_bits, 6); } /* Implementation detail of ASSERT_BIT_RANGE_FROM_MASK_EQ. */ diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index b75691e..da82bd1 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -269,9 +269,14 @@ struct bit_range return (get_start_bit_offset () < other.get_next_bit_offset () && other.get_start_bit_offset () < get_next_bit_offset ()); } + bool intersects_p (const bit_range &other, + bit_range *out_this, + bit_range *out_other) const; static int cmp (const bit_range &br1, const bit_range &br2); + bit_range operator- (bit_offset_t offset) const; + static bool from_mask (unsigned HOST_WIDE_INT mask, bit_range *out); bool as_byte_range (byte_range *out) const; diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index 66ef765..85acf44 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "cfg.h" #include "digraph.h" +#include "tree-cfg.h" #include "analyzer/supergraph.h" #include "analyzer/analyzer-logging.h" @@ -246,7 +247,7 @@ supergraph::supergraph (logger *logger) supernode *dest_supernode = *m_bb_to_initial_node.get (dest_cfg_block); cfg_superedge *cfg_sedge - = add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx); + = add_cfg_edge (src_supernode, dest_supernode, cfg_edge); m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge); } } @@ -505,17 +506,16 @@ supergraph::add_node (function *fun, basic_block bb, gcall *returning_call, adding it to this supergraph. If the edge is for a switch statement, create a switch_cfg_superedge - subclass using IDX (the index of E within the out-edges from SRC's - underlying basic block). */ + subclass. */ cfg_superedge * -supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx) +supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e) { /* Special-case switch edges. */ gimple *stmt = src->get_last_stmt (); cfg_superedge *new_edge; if (stmt && stmt->code == GIMPLE_SWITCH) - new_edge = new switch_cfg_superedge (src, dest, e, idx); + new_edge = new switch_cfg_superedge (src, dest, e); else new_edge = new cfg_superedge (src, dest, e); add_edge (new_edge); @@ -1072,6 +1072,23 @@ cfg_superedge::get_phi_arg (const gphi *phi) const return gimple_phi_arg_def (phi, index); } +switch_cfg_superedge::switch_cfg_superedge (supernode *src, + supernode *dst, + ::edge e) +: cfg_superedge (src, dst, e) +{ + /* Populate m_case_labels with all cases which go to DST. */ + const gswitch *gswitch = get_switch_stmt (); + for (unsigned i = 0; i < gimple_switch_num_labels (gswitch); i++) + { + tree case_ = gimple_switch_label (gswitch, i); + basic_block bb = label_to_block (src->get_function (), + CASE_LABEL (case_)); + if (bb == dst->m_bb) + m_case_labels.safe_push (case_); + } +} + /* Implementation of superedge::dump_label_to_pp for CFG superedges for "switch" statements. @@ -1081,31 +1098,63 @@ void switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp, bool user_facing ATTRIBUTE_UNUSED) const { - tree case_label = get_case_label (); - gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); - tree lower_bound = CASE_LOW (case_label); - tree upper_bound = CASE_HIGH (case_label); - if (lower_bound) + if (user_facing) { - pp_printf (pp, "case "); - dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); - if (upper_bound) + for (unsigned i = 0; i < m_case_labels.length (); ++i) { - pp_printf (pp, " ... "); - dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, false); + if (i > 0) + pp_string (pp, ", "); + tree case_label = m_case_labels[i]; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + pp_printf (pp, "case "); + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); + if (upper_bound) + { + pp_printf (pp, " ... "); + dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, + false); + } + pp_printf (pp, ":"); + } + else + pp_printf (pp, "default:"); } - pp_printf (pp, ":"); } else - pp_printf (pp, "default:"); -} - -/* Get the case label for this "switch" superedge. */ - -tree -switch_cfg_superedge::get_case_label () const -{ - return gimple_switch_label (get_switch_stmt (), m_idx); + { + pp_character (pp, '{'); + for (unsigned i = 0; i < m_case_labels.length (); ++i) + { + if (i > 0) + pp_string (pp, ", "); + tree case_label = m_case_labels[i]; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + tree lower_bound = CASE_LOW (case_label); + tree upper_bound = CASE_HIGH (case_label); + if (lower_bound) + { + if (upper_bound) + { + pp_character (pp, '['); + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, + false); + pp_string (pp, ", "); + dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, + false); + pp_character (pp, ']'); + } + else + dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); + } + else + pp_printf (pp, "default"); + } + pp_character (pp, '}'); + } } /* Implementation of superedge::dump_label_to_pp for interprocedural diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index 335f513..09a12be 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -181,7 +181,7 @@ public: private: supernode *add_node (function *fun, basic_block bb, gcall *returning_call, gimple_seq phi_nodes); - cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx); + cfg_superedge *add_cfg_edge (supernode *src, supernode *dest, ::edge e); call_superedge *add_call_superedge (supernode *src, supernode *dest, cgraph_edge *cedge); return_superedge *add_return_superedge (supernode *src, supernode *dest, @@ -539,15 +539,12 @@ is_a_helper <const cfg_superedge *>::test (const superedge *sedge) namespace ana { /* A subclass for edges from switch statements, retaining enough - information to identify the pertinent case, and for adding labels + information to identify the pertinent cases, and for adding labels when rendering via graphviz. */ class switch_cfg_superedge : public cfg_superedge { public: - switch_cfg_superedge (supernode *src, supernode *dst, ::edge e, int idx) - : cfg_superedge (src, dst, e), - m_idx (idx) - {} + switch_cfg_superedge (supernode *src, supernode *dst, ::edge e); const switch_cfg_superedge *dyn_cast_switch_cfg_superedge () const FINAL OVERRIDE @@ -563,10 +560,10 @@ class switch_cfg_superedge : public cfg_superedge { return as_a <gswitch *> (m_src->get_last_stmt ()); } - tree get_case_label () const; + const vec<tree> &get_case_labels () const { return m_case_labels; } - private: - const int m_idx; +private: + auto_vec<tree> m_case_labels; }; } // namespace ana diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 6ca524b..27ccc32 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,8 @@ +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + * c-parser.c (c_parser_omp_clause_num_tasks, + c_parser_omp_clause_grainsize): Parse the optional strict: modifier. + 2021-08-22 Martin Uecker <muecker@gwdg.de> PR c/98397 diff --git a/gcc/config.gcc b/gcc/config.gcc index 08e6c67..94199d7 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -476,6 +476,7 @@ powerpc*-*-*) cpu_type=rs6000 extra_objs="rs6000-string.o rs6000-p8swap.o rs6000-logue.o" extra_objs="${extra_objs} rs6000-call.o rs6000-pcrel-opt.o" + extra_objs="${extra_objs} rs6000-builtins.o" extra_headers="ppc-asm.h altivec.h htmintrin.h htmxlintrin.h" extra_headers="${extra_headers} bmi2intrin.h bmiintrin.h" extra_headers="${extra_headers} xmmintrin.h mm_malloc.h emmintrin.h" @@ -491,6 +492,7 @@ powerpc*-*-*) extra_options="${extra_options} g.opt fused-madd.opt rs6000/rs6000-tables.opt" target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-logue.c \$(srcdir)/config/rs6000/rs6000-call.c" target_gtfiles="$target_gtfiles \$(srcdir)/config/rs6000/rs6000-pcrel-opt.c" + target_gtfiles="$target_gtfiles ./rs6000-builtins.h" ;; pru-*-*) cpu_type=pru diff --git a/gcc/config/h8300/h8300-protos.h b/gcc/config/h8300/h8300-protos.h index 744337d..3d34401 100644 --- a/gcc/config/h8300/h8300-protos.h +++ b/gcc/config/h8300/h8300-protos.h @@ -94,7 +94,7 @@ extern int h8300_tiny_data_p (tree); extern int h8300_can_use_return_insn_p (void); extern void h8300_expand_prologue (void); -extern void h8300_expand_epilogue (void); +extern void h8300_expand_epilogue (bool); extern int h8300_current_function_interrupt_function_p (void); extern int h8300_current_function_monitor_function_p (void); extern int h8300_initial_elimination_offset (int, int); diff --git a/gcc/config/h8300/h8300.c b/gcc/config/h8300/h8300.c index 8ccacec..5f7251a 100644 --- a/gcc/config/h8300/h8300.c +++ b/gcc/config/h8300/h8300.c @@ -874,7 +874,7 @@ h8300_can_use_return_insn_p (void) /* Generate RTL code for the function epilogue. */ void -h8300_expand_epilogue (void) +h8300_expand_epilogue (bool sibcall_p) { int regno; int saved_regs; @@ -919,6 +919,7 @@ h8300_expand_epilogue (void) /* See if this pop would be the last insn before the return. If so, use rte/l or rts/l instead of pop or ldm.l. */ if (TARGET_H8300SX + && !sibcall_p && !frame_pointer_needed && frame_size == 0 && (saved_regs & ((1 << (regno - n_regs + 1)) - 1)) == 0) @@ -931,12 +932,12 @@ h8300_expand_epilogue (void) /* Pop frame pointer if we had one. */ if (frame_pointer_needed) { - if (TARGET_H8300SX) + if (TARGET_H8300SX && !sibcall_p) returned_p = true; h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, true, returned_p); } - if (!returned_p) + if (!returned_p && !sibcall_p) emit_jump_insn (ret_rtx); } @@ -5533,6 +5534,25 @@ h8300_push_rounding (poly_int64 bytes) { return ((bytes + PARM_BOUNDARY / 8 - 1) & (-PARM_BOUNDARY / 8)); } + +static bool +h8300_ok_for_sibcall_p (tree fndecl, tree) +{ + /* If either the caller or target are special, then assume sibling + calls are not OK. */ + if (!fndecl + || h8300_os_task_function_p (fndecl) + || h8300_monitor_function_p (fndecl) + || h8300_interrupt_function_p (fndecl) + || h8300_saveall_function_p (fndecl) + || h8300_os_task_function_p (current_function_decl) + || h8300_monitor_function_p (current_function_decl) + || h8300_interrupt_function_p (current_function_decl) + || h8300_saveall_function_p (current_function_decl)) + return false; + + return 1; +} /* Initialize the GCC target structure. */ #undef TARGET_ATTRIBUTE_TABLE @@ -5628,4 +5648,7 @@ h8300_push_rounding (poly_int64 bytes) #undef TARGET_FLAGS_REGNUM #define TARGET_FLAGS_REGNUM 12 +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL h8300_ok_for_sibcall_p + struct gcc_target targetm = TARGET_INITIALIZER; diff --git a/gcc/config/h8300/jumpcall.md b/gcc/config/h8300/jumpcall.md index 3e59fee..b596399 100644 --- a/gcc/config/h8300/jumpcall.md +++ b/gcc/config/h8300/jumpcall.md @@ -290,7 +290,7 @@ (define_insn "call_insn_<mode>" [(call (mem:QI (match_operand 0 "call_insn_operand" "Cr")) (match_operand:P 1 "general_operand" "g"))] - "" + "!SIBLING_CALL_P (insn)" { rtx xoperands[1]; xoperands[0] = gen_rtx_MEM (QImode, operands[0]); @@ -328,7 +328,7 @@ [(set (match_operand 0 "" "=r") (call (mem:QI (match_operand 1 "call_insn_operand" "Cr")) (match_operand:P 2 "general_operand" "g")))] - "" + "!SIBLING_CALL_P (insn)" { rtx xoperands[2]; gcc_assert (GET_MODE (operands[1]) == Pmode); @@ -347,3 +347,73 @@ (const_int 2) (const_int 4)))]) +(define_expand "sibcall" + [(call (match_operand:QI 0 "call_expander_operand" "") + (match_operand 1 "general_operand" ""))] + "" + { + if (!register_operand (XEXP (operands[0], 0), Pmode) + && GET_CODE (XEXP (operands[0], 0)) != SYMBOL_REF) + XEXP (operands[0], 0) = force_reg (Pmode, XEXP (operands[0], 0)); + }) + +(define_insn "sibcall_insn_<mode>" + [(call (mem:QI (match_operand 0 "call_insn_operand" "Cr")) + (match_operand:P 1 "general_operand" "g"))] + "SIBLING_CALL_P (insn)" +{ + rtx xoperands[1]; + xoperands[0] = gen_rtx_MEM (QImode, operands[0]); + gcc_assert (GET_MODE (operands[0]) == Pmode); + if (GET_CODE (XEXP (xoperands[0], 0)) == SYMBOL_REF + && (SYMBOL_REF_FLAGS (XEXP (xoperands[0], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION)) + output_asm_insn ("jmp\\t@%0:8", xoperands); + else + output_asm_insn ("jmp\\t%0", xoperands); + return ""; +} + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (match_operand:QI 0 "small_call_insn_operand" "") + (const_int 2) + (const_int 4)))]) + +;; Call subroutine, returning value in operand 0 +;; (which must be a hard register). + +;; ??? Even though we use HImode here, this works on the H8/300H and H8S. + +(define_expand "sibcall_value" + [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "call_expander_operand" "") + (match_operand 2 "general_operand" "")))] + "" + { + if (!register_operand (XEXP (operands[1], 0), Pmode) + && GET_CODE (XEXP (operands[1], 0)) != SYMBOL_REF) + XEXP (operands[1], 0) = force_reg (Pmode, XEXP (operands[1], 0)); + }) + +(define_insn "sibcall_value_insn_<mode>" + [(set (match_operand 0 "" "=r") + (call (mem:QI (match_operand 1 "call_insn_operand" "Cr")) + (match_operand:P 2 "general_operand" "g")))] + "SIBLING_CALL_P (insn)" +{ + rtx xoperands[2]; + gcc_assert (GET_MODE (operands[1]) == Pmode); + xoperands[0] = operands[0]; + xoperands[1] = gen_rtx_MEM (QImode, operands[1]); + if (GET_CODE (XEXP (xoperands[1], 0)) == SYMBOL_REF + && (SYMBOL_REF_FLAGS (XEXP (xoperands[1], 0)) & SYMBOL_FLAG_FUNCVEC_FUNCTION)) + output_asm_insn ("jmp\\t@%1:8", xoperands); + else + output_asm_insn ("jmp\\t%1", xoperands); + return ""; +} + [(set_attr "type" "call") + (set (attr "length") + (if_then_else (match_operand:QI 0 "small_call_insn_operand" "") + (const_int 2) + (const_int 4)))]) + diff --git a/gcc/config/h8300/proepi.md b/gcc/config/h8300/proepi.md index 44d5968..ab58d02 100644 --- a/gcc/config/h8300/proepi.md +++ b/gcc/config/h8300/proepi.md @@ -98,7 +98,7 @@ [(return)] "" { - h8300_expand_epilogue (); + h8300_expand_epilogue (false); DONE; }) @@ -121,3 +121,11 @@ gcc_unreachable (); } [(set_attr "length" "20")]) + +(define_expand "sibcall_epilogue" + [(const_int 0)] + "" + { + h8300_expand_epilogue (true); + DONE; + }) diff --git a/gcc/config/i386/i386-features.c b/gcc/config/i386/i386-features.c index d9c6652..5a99ea7 100644 --- a/gcc/config/i386/i386-features.c +++ b/gcc/config/i386/i386-features.c @@ -610,12 +610,40 @@ general_scalar_chain::compute_convert_gain () case CONST_INT: if (REG_P (dst)) - /* DImode can be immediate for TARGET_64BIT and SImode always. */ - igain += m * COSTS_N_INSNS (1); + { + if (optimize_insn_for_size_p ()) + { + /* xor (2 bytes) vs. xorps (3 bytes). */ + if (src == const0_rtx) + igain -= COSTS_N_BYTES (1); + /* movdi_internal vs. movv2di_internal. */ + /* => mov (5 bytes) vs. movaps (7 bytes). */ + else if (x86_64_immediate_operand (src, SImode)) + igain -= COSTS_N_BYTES (2); + else + /* ??? Larger immediate constants are placed in the + constant pool, where the size benefit/impact of + STV conversion is affected by whether and how + often each constant pool entry is shared/reused. + The value below is empirically derived from the + CSiBE benchmark (and the optimal value may drift + over time). */ + igain += COSTS_N_BYTES (0); + } + else + { + /* DImode can be immediate for TARGET_64BIT + and SImode always. */ + igain += m * COSTS_N_INSNS (1); + igain -= vector_const_cost (src); + } + } else if (MEM_P (dst)) - igain += (m * ix86_cost->int_store[2] - - ix86_cost->sse_store[sse_cost_idx]); - igain -= vector_const_cost (src); + { + igain += (m * ix86_cost->int_store[2] + - ix86_cost->sse_store[sse_cost_idx]); + igain -= vector_const_cost (src); + } break; default: diff --git a/gcc/config/i386/i386-options.c b/gcc/config/i386/i386-options.c index 6b78998..fee5a48 100644 --- a/gcc/config/i386/i386-options.c +++ b/gcc/config/i386/i386-options.c @@ -304,6 +304,10 @@ ix86_omp_device_kind_arch_isa (enum omp_device_kind_arch_isa trait, case omp_device_kind: return strcmp (name, "cpu") == 0; case omp_device_arch: +#ifdef ACCEL_COMPILER + if (strcmp (name, "intel_mic") == 0) + return 1; +#endif if (strcmp (name, "x86") == 0) return 1; if (TARGET_64BIT) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 46844fa..5bff131 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -19982,8 +19982,6 @@ ix86_division_cost (const struct processor_costs *cost, return cost->divide[MODE_INDEX (mode)]; } -#define COSTS_N_BYTES(N) ((N) * 2) - /* Return cost of shift in MODE. If CONSTANT_OP1 is true, the op1 value is known and set in OP1_VAL. AND_IN_OP1 specify in op1 is result of and and SHIFT_AND_TRUNCATE diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 8aba86d..11ac8d0 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -88,6 +88,11 @@ struct stringop_algs } size [MAX_STRINGOP_ALGS]; }; +/* Analog of COSTS_N_INSNS when optimizing for size. */ +#ifndef COSTS_N_BYTES +#define COSTS_N_BYTES(N) ((N) * 2) +#endif + /* Define the specific costs for a given cpu. NB: hard_register is used by TARGET_REGISTER_MOVE_COST and TARGET_MEMORY_MOVE_COST to compute hard register move costs by register allocator. Relative costs of diff --git a/gcc/config/i386/t-omp-device b/gcc/config/i386/t-omp-device index 037ae5e..29350a1 100644 --- a/gcc/config/i386/t-omp-device +++ b/gcc/config/i386/t-omp-device @@ -1,6 +1,6 @@ omp-device-properties-i386: $(srcdir)/config/i386/i386-options.c echo kind: cpu > $@ - echo arch: x86 x86_64 i386 i486 i586 i686 ia32 >> $@ + echo arch: intel_mic x86 x86_64 i386 i486 i586 i686 ia32 >> $@ echo isa: sse4 `sed -n '/^static struct ix86_target_opts isa2\?_opts\[\] =/,/^};/p' \ $(srcdir)/config/i386/i386-options.c | \ sed -n 's/",.*$$//;s/^ { "-m//p'` >> $@ diff --git a/gcc/config/rs6000/rs6000-gen-builtins.c b/gcc/config/rs6000/rs6000-gen-builtins.c index e5d3b71..000e5f9 100644 --- a/gcc/config/rs6000/rs6000-gen-builtins.c +++ b/gcc/config/rs6000/rs6000-gen-builtins.c @@ -597,6 +597,13 @@ consume_whitespace (void) { while (pos < LINELEN && isspace(linebuf[pos]) && linebuf[pos] != '\n') pos++; + + if (pos >= LINELEN) + { + diag ("line length overrun at %d.\n", pos); + exit (1); + } + return; } @@ -623,7 +630,7 @@ advance_line (FILE *file) static inline void safe_inc_pos (void) { - if (pos++ >= LINELEN) + if (++pos >= LINELEN) { (*diag) ("line length overrun.\n"); exit (1); @@ -636,9 +643,16 @@ static char * match_identifier (void) { int lastpos = pos - 1; - while (isalnum (linebuf[lastpos + 1]) || linebuf[lastpos + 1] == '_') + while (lastpos < LINELEN - 1 + && (isalnum (linebuf[lastpos + 1]) || linebuf[lastpos + 1] == '_')) ++lastpos; + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return 0; @@ -660,9 +674,15 @@ match_integer (void) safe_inc_pos (); int lastpos = pos - 1; - while (isdigit (linebuf[lastpos + 1])) + while (lastpos < LINELEN - 1 && isdigit (linebuf[lastpos + 1])) ++lastpos; + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return NULL; @@ -680,7 +700,7 @@ static const char * match_to_right_bracket (void) { int lastpos = pos - 1; - while (linebuf[lastpos + 1] != ']') + while (lastpos < LINELEN - 1 && linebuf[lastpos + 1] != ']') { if (linebuf[lastpos + 1] == '\n') { @@ -690,6 +710,12 @@ match_to_right_bracket (void) ++lastpos; } + if (lastpos >= LINELEN - 1) + { + diag ("line length overrun at %d.\n", lastpos); + exit (1); + } + if (lastpos < pos) return 0; @@ -1768,8 +1794,9 @@ parse_bif_entry (void) /* Append a number representing the order in which this function was encountered to its name, and save in another lookup structure. */ - char *buf; - asprintf (&buf, "%s:%05d", bifs[curr_bif].idname, curr_bif); + int orig_len = strlen (bifs[curr_bif].idname); + char *buf = (char *) malloc (orig_len + 7); + sprintf (buf, "%s:%05d", bifs[curr_bif].idname, curr_bif); if (!rbt_insert (&bifo_rbt, buf)) { @@ -2979,9 +3006,11 @@ main (int argc, const char **argv) exit (1); } + /* Always close init_file last. This avoids race conditions in the + build machinery. See comments in t-rs6000. */ fclose (header_file); - fclose (init_file); fclose (defines_file); + fclose (init_file); return 0; } diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000 index 44f7ffb..92766d8 100644 --- a/gcc/config/rs6000/t-rs6000 +++ b/gcc/config/rs6000/t-rs6000 @@ -22,15 +22,12 @@ TM_H += $(srcdir)/config/rs6000/rs6000-builtin.def TM_H += $(srcdir)/config/rs6000/rs6000-cpus.def TM_H += $(srcdir)/config/rs6000/rs6000-modes.h PASSES_EXTRA += $(srcdir)/config/rs6000/rs6000-passes.def +EXTRA_GTYPE_DEPS += $(srcdir)/config/rs6000/rs6000-builtin-new.def rs6000-pcrel-opt.o: $(srcdir)/config/rs6000/rs6000-pcrel-opt.c $(COMPILE) $< $(POSTCOMPILE) -rs6000-c.o: $(srcdir)/config/rs6000/rs6000-c.c - $(COMPILE) $< - $(POSTCOMPILE) - rs6000-string.o: $(srcdir)/config/rs6000/rs6000-string.c $(COMPILE) $< $(POSTCOMPILE) @@ -47,7 +44,47 @@ rs6000-logue.o: $(srcdir)/config/rs6000/rs6000-logue.c $(COMPILE) $< $(POSTCOMPILE) -rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call.c +rs6000-gen-builtins.o: $(srcdir)/config/rs6000/rs6000-gen-builtins.c + $(COMPILE) $< + $(POSTCOMPILE) + +rbtree.o: $(srcdir)/config/rs6000/rbtree.c + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-gen-builtins: rs6000-gen-builtins.o rbtree.o + $(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \ + $(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS) + +# TODO: Whenever GNU make 4.3 is the minimum required, we should use +# grouped targets on this: +# rs6000-builtins.c rs6000-builtins.h rs6000-vecdefines.h &: <deps> +# <recipe> +# For now, the header files depend on rs6000-builtins.c, which avoids +# races because the .c file is closed last in rs6000-gen-builtins.c. +rs6000-builtins.c: rs6000-gen-builtins \ + $(srcdir)/config/rs6000/rs6000-builtin-new.def \ + $(srcdir)/config/rs6000/rs6000-overload.def + ./rs6000-gen-builtins $(srcdir)/config/rs6000/rs6000-builtin-new.def \ + $(srcdir)/config/rs6000/rs6000-overload.def rs6000-builtins.h \ + rs6000-builtins.c rs6000-vecdefines.h + +rs6000-builtins.h: rs6000-builtins.c + +rs6000.o: rs6000-builtins.h + +EXTRA_HEADERS += rs6000-vecdefines.h +rs6000-vecdefines.h: rs6000-builtins.c + +rs6000-builtins.o: rs6000-builtins.c + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-call.o: $(srcdir)/config/rs6000/rs6000-call.c rs6000-builtins.h + $(COMPILE) $< + $(POSTCOMPILE) + +rs6000-c.o: $(srcdir)/config/rs6000/rs6000-c.c rs6000-builtins.h $(COMPILE) $< $(POSTCOMPILE) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d496202..ddea2a2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + * parser.c (cp_parser_omp_clause_num_tasks, + cp_parser_omp_clause_grainsize): Parse the optional strict: modifier. + 2021-08-20 Jakub Jelinek <jakub@redhat.com> * parser.c (cp_parser_handle_statement_omp_attributes): Determine if diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 7da56e7..307886d 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,21 @@ +2021-08-23 Tobias Burnus <tobias@codesourcery.com> + + * openmp.c (gfc_match_dupl_check, gfc_match_dupl_memorder, + gfc_match_dupl_atomic): New. + (gfc_match_omp_clauses): Use them; remove duplicate + 'release'/'relaxed' clause matching; improve error dignostic + for 'default'. + +2021-08-23 Tobias Burnus <tobias@codesourcery.com> + + * dump-parse-tree.c (show_omp_clauses): Handle 'strict' modifier + on grainsize/num_tasks + * gfortran.h (gfc_omp_clauses): Add grainsize_strict + and num_tasks_strict. + * trans-openmp.c (gfc_trans_omp_clauses, gfc_split_omp_clauses): + Handle 'strict' modifier on grainsize/num_tasks. + * openmp.c (gfc_match_omp_clauses): Likewise. + 2021-08-20 Tobias Burnus <tobias@codesourcery.com> * error.c diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h index d36c28c..2e26b75 100644 --- a/gcc/ipa-modref-tree.h +++ b/gcc/ipa-modref-tree.h @@ -66,7 +66,10 @@ struct GTY(()) modref_access_node /* Return true if range info is useful. */ bool range_info_useful_p () const { - return parm_index != -1 && parm_offset_known; + return parm_index != -1 && parm_offset_known + && (known_size_p (size) + || known_size_p (max_size) + || known_ge (offset, 0)); } /* Return true if both accesses are the same. */ bool operator == (modref_access_node &a) const @@ -88,6 +91,35 @@ struct GTY(()) modref_access_node return false; return true; } + /* Return true A is a subaccess. */ + bool contains (modref_access_node &a) const + { + if (parm_index != a.parm_index) + return false; + if (parm_index >= 0) + { + if (parm_offset_known + && (!a.parm_offset_known + || !known_eq (parm_offset, a.parm_offset))) + return false; + } + if (range_info_useful_p ()) + { + if (!a.range_info_useful_p ()) + return false; + /* Sizes of stores are used to check that object is big enough + to fit the store, so smaller or unknown sotre is more general + than large store. */ + if (known_size_p (size) + && !known_le (size, a.size)) + return false; + if (known_size_p (max_size)) + return known_subrange_p (a.offset, a.max_size, offset, max_size); + else + return known_le (offset, a.offset); + } + return true; + } }; /* Access node specifying no useful info. */ @@ -107,17 +139,6 @@ struct GTY((user)) modref_ref_node accesses (NULL) {} - /* Search REF; return NULL if failed. */ - modref_access_node *search (modref_access_node access) - { - size_t i; - modref_access_node *a; - FOR_EACH_VEC_SAFE_ELT (accesses, i, a) - if (*a == access) - return a; - return NULL; - } - /* Collapse the tree. */ void collapse () { @@ -136,16 +157,36 @@ struct GTY((user)) modref_ref_node return false; /* Otherwise, insert a node for the ref of the access under the base. */ - modref_access_node *access_node = search (a); - if (access_node) - return false; + size_t i; + modref_access_node *a2; + + if (!a.useful_p ()) + { + if (!every_access) + { + collapse (); + return true; + } + return false; + } + + FOR_EACH_VEC_SAFE_ELT (accesses, i, a2) + { + if (a2->contains (a)) + return false; + if (a.contains (*a2)) + { + *a2 = a; + return true; + } + gcc_checking_assert (!(a == *a2)); + } /* If this base->ref pair has too many accesses stored, we will clear all accesses and bail out. */ - if ((accesses && accesses->length () >= max_accesses) - || !a.useful_p ()) + if (accesses && accesses->length () >= max_accesses) { - if (dump_file && a.useful_p ()) + if (dump_file) fprintf (dump_file, "--param param=modref-max-accesses limit reached\n"); collapse (); diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c index cb0a314..6ab687a 100644 --- a/gcc/ipa-modref.c +++ b/gcc/ipa-modref.c @@ -1707,7 +1707,7 @@ analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth, is on since that would allow propagation of this from -fno-ipa-pta to -fipa-pta functions. */ if (gimple_call_fn (use_stmt) == name) - lattice[index].merge (~EAF_NOCLOBBER); + lattice[index].merge (~(EAF_NOCLOBBER | EAF_UNUSED)); /* Recursion would require bit of propagation; give up for now. */ if (callee && !ipa && recursive_call_p (current_function_decl, diff --git a/gcc/match.pd b/gcc/match.pd index 978a1b0..e5bbb12 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -3389,7 +3389,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) the form that minimizes the number of conversions. */ (simplify (convert (lshift:s@0 (convert:s@1 @2) INTEGER_CST@3)) - (if (tree_nop_conversion_p (type, TREE_TYPE (@0)) + (if (INTEGRAL_TYPE_P (type) + && !POINTER_TYPE_P (type) + && tree_nop_conversion_p (type, TREE_TYPE (@0)) && INTEGRAL_TYPE_P (TREE_TYPE (@2)) && TYPE_PRECISION (TREE_TYPE (@2)) <= TYPE_PRECISION (type)) (lshift (convert @2) @3))) diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index f3df614..8eea9fb 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -1268,6 +1268,9 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, return temp; } + /* Check for useless truncation. */ + if (GET_MODE (op) == mode) + return op; break; case FLOAT_TRUNCATE: diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index fa50fe5..5c49bd3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,129 @@ +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + * gcc.dg/analyzer/switch.c: Remove xfail. Add various tests. + * gcc.dg/analyzer/torture/switch-2.c: New test. + * gcc.dg/analyzer/torture/switch-3.c: New test. + * gcc.dg/analyzer/torture/switch-4.c: New test. + * gcc.dg/analyzer/torture/switch-5.c: New test. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101875 + * gcc.dg/analyzer/pr101875.c: New test. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101837 + * gcc.dg/analyzer/pr101837.c: New test. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101962 + * gcc.dg/analyzer/data-model-23.c: New test. + * gcc.dg/analyzer/pr101962.c: New test. + +2021-08-23 David Malcolm <dmalcolm@redhat.com> + + * gcc.dg/analyzer/data-model-22.c: New test. + * gcc.dg/analyzer/uninit-6.c: New test. + * gcc.dg/analyzer/uninit-6b.c: New test. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + * gcc.dg/tree-ssa/modref-7.c: New test. + +2021-08-23 Richard Biener <rguenther@suse.de> + + PR ipa/97565 + * g++.dg/lto/pr97565_0.C: New testcase. + * g++.dg/lto/pr97565_1.C: Likewise. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + * g++.dg/tree-ssa/modref-1.C: Fix template. + +2021-08-23 Tobias Burnus <tobias@codesourcery.com> + + * gfortran.dg/goacc/asyncwait-1.f95: Update dg-error. + * gfortran.dg/goacc/default-2.f: Update dg-error. + * gfortran.dg/goacc/enter-exit-data.f95: Update dg-error. + * gfortran.dg/goacc/if.f95: Update dg-error. + * gfortran.dg/goacc/parallel-kernels-clauses.f95: Update dg-error. + * gfortran.dg/goacc/routine-6.f90: Update dg-error. + * gfortran.dg/goacc/sie.f95: Update dg-error. + * gfortran.dg/goacc/update-if_present-2.f90: Update dg-error. + * gfortran.dg/gomp/cancel-2.f90: Update dg-error. + * gfortran.dg/gomp/declare-simd-1.f90: Update dg-error. + * gfortran.dg/gomp/error-3.f90: Update dg-error. + * gfortran.dg/gomp/loop-2.f90: Update dg-error. + * gfortran.dg/gomp/masked-2.f90: Update dg-error. + +2021-08-23 Ankur Saini <arsenic@sourceware.org> + + PR analyzer/102020 + * gcc.dg/analyzer/malloc-callbacks.c : Fix faulty test. + +2021-08-23 Roger Sayle <roger@nextmovesoftware.com> + + * gcc.dg/fold-convlshift-1.c: New test case. + * gcc.dg/fold-convlshift-2.c: New test case. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/86723 + * gcc.dg/pr86723.c: New test. + * gcc.target/i386/pr86723.c: New test. + * gcc.dg/optimize-bswapdi-1.c: Use -fdump-tree-optimized instead of + -fdump-tree-bswap and scan for number of __builtin_bswap64 calls. + * gcc.dg/optimize-bswapdi-2.c: Likewise. + * gcc.dg/optimize-bswapsi-1.c: Use -fdump-tree-optimized instead of + -fdump-tree-bswap and scan for number of __builtin_bswap32 calls. + * gcc.dg/optimize-bswapsi-5.c: Likewise. + * gcc.dg/optimize-bswapsi-3.c: Likewise. Expect one __builtin_bswap32 + call instead of zero. + +2021-08-23 Richard Biener <rguenther@suse.de> + + PR tree-optimization/79334 + * gcc.dg/torture/pr79334-0.c: New testcase. + * gcc.dg/torture/pr79334-1.c: Likewise. + +2021-08-23 liuhongt <hongtao.liu@intel.com> + + PR target/102016 + * gcc.target/i386/pr102016.c: New test. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + PR debug/101905 + * gcc.dg/guality/pr101905.c: New test. + +2021-08-23 Christophe Lyon <christophe.lyon@foss.st.com> + + PR target/100856 + * gcc.target/arm/acle/pr100856.c: Use arm_v8m_main_cde_multilib + and arm_v8m_main_cde. + * lib/target-supports.exp: Add + check_effective_target_FUNC_multilib for ARM CDE. + +2021-08-23 Jan Hubicka <hubicka@ucw.cz> + + * g++.dg/tree-ssa/modref-1.C: New test. + +2021-08-23 Xi Ruoyao <xry111@mengyan1223.wang> + + PR target/101922 + * gcc.target/mips/pr101922.c: New test. + +2021-08-23 Jonathan Yong <10walls@gmail.com> + + * gcc.c-torture/execute/gcc_tmpnam.h: Fix tmpnam case on Windows + where it can return a filename with "\" to indicate current + directory. + * gcc.c-torture/execute/fprintf-2.c: Use wrapper. + * gcc.c-torture/execute/printf-2.c: Use wrapper. + * gcc.c-torture/execute/user-printf.c: Use wrapper. + 2021-08-22 Martin Uecker <muecker@gwdg.de> PR c/98397 diff --git a/gcc/testsuite/g++.dg/lto/pr97565_0.C b/gcc/testsuite/g++.dg/lto/pr97565_0.C new file mode 100644 index 0000000..f4572e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr97565_0.C @@ -0,0 +1,7 @@ +// { dg-lto-do link } +// { dg-lto-options { "-O -flto -fipa-pta" } } + +extern "C" void abort(void) +{ + abort(); +} diff --git a/gcc/testsuite/g++.dg/lto/pr97565_1.C b/gcc/testsuite/g++.dg/lto/pr97565_1.C new file mode 100644 index 0000000..ff7b638 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr97565_1.C @@ -0,0 +1,6 @@ +extern "C" void abort(void); + +int main(int argc, char * argv[]) +{ + abort(); +} diff --git a/gcc/testsuite/g++.dg/tree-ssa/modref-1.C b/gcc/testsuite/g++.dg/tree-ssa/modref-1.C index c742dfe..b9b9f6c 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/modref-1.C +++ b/gcc/testsuite/g++.dg/tree-ssa/modref-1.C @@ -29,4 +29,4 @@ int test2() return b; } // ipa-modref should analyze parameter B of test as noescape. -// { dg-final { scan-tree-dump "return 1234" } } +// { dg-final { scan-tree-dump "return 1234" "optimized" } } diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-22.c b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c new file mode 100644 index 0000000..8429b2f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-22.c @@ -0,0 +1,101 @@ +#include <string.h> +#include "analyzer-decls.h" + +extern void check_init_char (char v); +extern void check_init_int (int v); + +void test_1 (void) +{ + union + { + char c[16]; + int i[4]; + } v; + memset (&v, 0, sizeof (v)); + v.c[5] = 42; + check_init_int (v.c[0]); + check_init_int (v.c[4]); + check_init_int (v.c[6]); + check_init_int (v.i[1]); +} + +void test_2 (void) +{ + /* Intersection of byte ranges within "v". */ + union + { + struct { + int a; + char b; + char c; + } __attribute__((packed)) icc; + struct { + char a; + int b; + char c; + } __attribute__((packed)) cic; + struct { + char a; + char b; + int c; + } __attribute__((packed)) cci; + } v; + + v.icc.a = 1066; + v.icc.b = 42; + v.icc.c = 17; + + __analyzer_eval (v.icc.a == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.b == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.icc.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cic.a = 42; + v.cic.b = 1066; + v.cic.c = 17; + + __analyzer_eval (v.cic.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.b == 1066); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cic.c == 17); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + + v.cci.a = 42; + v.cci.b = 17; + v.cci.c = 1066; + + __analyzer_eval (v.cci.a == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.b == 17); /* { dg-warning "TRUE" } */ + __analyzer_eval (v.cci.c == 1066); /* { dg-warning "TRUE" } */ + check_init_int (v.icc.a); + check_init_char (v.icc.b); + check_init_char (v.icc.c); + + check_init_char (v.cic.a); + check_init_int (v.cic.b); + check_init_char (v.cic.c); + + check_init_char (v.cci.a); + check_init_char (v.cci.b); + check_init_int (v.cci.c); + +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-23.c b/gcc/testsuite/gcc.dg/analyzer/data-model-23.c new file mode 100644 index 0000000..c76dd4e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-23.c @@ -0,0 +1,26 @@ +#include "analyzer-decls.h" + +#define NULL ((void *)0) + +void * __attribute__((noinline)) +hide (void *ptr) +{ + return ptr; +} + +void test_1 (void) +{ + int a; + __analyzer_eval (hide (&a) == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (&a) + 1 != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (&a) + 1 == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (&a) - 1 != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (&a) - 1 == NULL); /* { dg-warning "FALSE" } */ +} + +void test_2 (void) +{ + __analyzer_eval (hide (NULL) == NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (hide (NULL) - 1 == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (hide (NULL) + 1 == NULL); /* { dg-warning "FALSE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101837.c b/gcc/testsuite/gcc.dg/analyzer/pr101837.c new file mode 100644 index 0000000..f99374d --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101837.c @@ -0,0 +1,10 @@ +/* { dg-additional-options "-O3 -fsanitize=undefined" } */ + +void memory_exhausted(); +void memcheck(void *ptr) { + if (ptr) /* { dg-warning "leak" } */ + memory_exhausted(); +} + +int emalloc(int size) { memcheck(__builtin_malloc(size)); } /* { dg-message "allocated here" } */ +int main() { int max_envvar_len = emalloc(max_envvar_len + 1); } /* { dg-message "use of uninitialized value 'max_envvar_len'" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101875.c b/gcc/testsuite/gcc.dg/analyzer/pr101875.c new file mode 100644 index 0000000..5988b8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101875.c @@ -0,0 +1,16 @@ +char * +fopen (const char *restrict, const char *restrict); + +void +err (void); + +void +k2 (void) +{ + char *setfiles[1]; + int i; + + setfiles[i] = fopen("", ""); /* { dg-warning "use of uninitialized value 'i'" } */ + if (!setfiles[i]) /* { dg-warning "use of uninitialized value 'i'" } */ + err (); +} /* { dg-warning "leak of FILE" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101962.c b/gcc/testsuite/gcc.dg/analyzer/pr101962.c new file mode 100644 index 0000000..7b83d03 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101962.c @@ -0,0 +1,51 @@ +#include "analyzer-decls.h" + +#define NULL ((void *)0) + +/* Verify that the analyzer makes the simplifying assumption that we don't + hit NULL when incrementing pointers to non-NULL memory regions. */ + +static int * __attribute__((noinline)) +maybe_inc_int_ptr (int *ptr) +{ + if (!ptr) + return NULL; + return ++ptr; +} + +int +test_1 (void) +{ + int stack; + int *a = &stack; + a = maybe_inc_int_ptr (a); + a = maybe_inc_int_ptr (a); + __analyzer_eval (a == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (a != NULL); /* { dg-warning "TRUE" } */ + return *a; /* { dg-warning "use of uninitialized value '\\*a'" } */ + /* TODO: a complaint about out-of-bounds would be a better warning. */ +} + +static const char * __attribute__((noinline)) +maybe_inc_char_ptr (const char *ptr) +{ + if (!ptr) + return NULL; + return ++ptr; +} + +char +test_s (void) +{ + const char *msg = "hello world"; + const char *a = msg; + __analyzer_eval (*a == 'h'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'e'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'l'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'l'); /* { dg-warning "TRUE" } */ + a = maybe_inc_char_ptr (a); + __analyzer_eval (*a == 'o'); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c index 870b00f..0b9e7e3 100644 --- a/gcc/testsuite/gcc.dg/analyzer/switch.c +++ b/gcc/testsuite/gcc.dg/analyzer/switch.c @@ -8,23 +8,156 @@ void test (int i) { case 0: __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != -1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (i != 1); /* { dg-warning "TRUE" } */ break; case 3 ... 5: + __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (i >= 2); /* { dg-warning "TRUE" } */ __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i < 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i < 7); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 6); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i >= 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i >= 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i <= 3); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i <= 4); /* { dg-warning "UNKNOWN" } */ break; default: + __analyzer_eval (i == -1); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 0); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */ __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */ - __analyzer_eval (i == 4); /* { dg-warning "FALSE" "desired" { xfail *-*-* } } */ - /* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */ - /* TODO(xfail^^^): we're only checking against endpoints of case - ranges, not the insides. */ + __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */ __analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i != 6); /* { dg-warning "UNKNOWN" } */ break; } } + +/* Verify that the analyzer follows the correct paths on a + switch statement guarded by an if, using noinline to defeat + optimizations. */ + +static void __attribute__((noinline)) +__analyzer_called_by_test_2 (int y) +{ + switch (y) + { + case 0: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + case 1: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 2: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + default: + __analyzer_dump_path (); /* { dg-bogus "path" } */ + break; + } +} + +void test_2 (int x) +{ + if (x == 1) + __analyzer_called_by_test_2 (x); +} + +void test_3 (int x, int y) +{ + if (y == 3) + switch (x) + { + case 0 ... 9: + case 20 ... 29: + if (x == y) + __analyzer_dump_path (); /* { dg-message "path" } */ + else + __analyzer_dump_path (); /* { dg-message "path" } */ + } +} + +struct s4 +{ + unsigned char level:3; + unsigned char key_id_mode:2; + unsigned char reserved:3; +}; + +void test_4 (struct s4 *p) +{ + switch (p->key_id_mode) + { + case 0: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 1: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 2: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + case 3: + __analyzer_dump_path (); /* { dg-message "path" } */ + break; + } + __analyzer_dump_path (); /* { dg-message "path" } */ +} + +int test_5 (unsigned v) +{ + switch (v) + { + case 0: + return 7; + break; + case 1: + return 23; + break; + default: + return v * 2; + } +} + +int test_6 (unsigned v) +{ + switch (v) + { + case 0: + return 3; + case -1: + return 22; + } + return -3; +} + +int g7 = -1; +int test_7 () +{ + switch (g7++) { + case 0: + return 32; + + case 100: + return 42; + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c new file mode 100644 index 0000000..3da2e30 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c @@ -0,0 +1,42 @@ +struct s +{ + int f0; + int f1; +}; + +int test (int cmd) +{ + int err = 0; + struct s foo; + struct s bar; + + switch (cmd) + { + case 0: + foo.f0 = 0; + break; + case 1: + foo.f0 = 1; + break; + case 30 ... 50: + case 70 ... 80: + __builtin_memset (&bar, 0, sizeof (bar)); + break; + } + + switch (cmd) + { + default: + return -1; + case 0 ... 1: + return foo.f0; + break; + case 42: + return bar.f1; + break; + case 65: + return bar.f1; + break; + } + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c new file mode 100644 index 0000000..57b8acd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c @@ -0,0 +1,158 @@ +typedef unsigned int __u32; +__extension__ typedef unsigned long long __u64; + +extern unsigned long +copy_from_user(void *to, const void *from, unsigned long n); + +extern unsigned long +copy_to_user(void *to, const void *from, unsigned long n); + +struct mtrr_sentry { + __u64 base; + __u32 size; + __u32 type; +}; + +struct mtrr_gentry { + __u64 base; + __u32 size; + __u32 regnum; + __u32 type; + __u32 _pad; +}; + +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +#define _IOC_TYPECHECK(t) (sizeof(t)) + +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) + +#define MTRR_IOCTL_BASE 'M' + +#define EFAULT 14 +#define EINVAL 22 +#define ENOTTY 25 + +#define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) +#define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) +#define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) +#define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry) +#define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry) +#define MTRRIOC_ADD_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_sentry) +#define MTRRIOC_SET_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 6, struct mtrr_sentry) +#define MTRRIOC_DEL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 7, struct mtrr_sentry) +#define MTRRIOC_GET_PAGE_ENTRY _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry) +#define MTRRIOC_KILL_PAGE_ENTRY _IOW(MTRR_IOCTL_BASE, 9, struct mtrr_sentry) + +extern void check_init_u64 (__u64 v); +extern void check_init_u32 (__u32 v); + +/* Adapted/reduced from arch/x86/kernel/cpu/mtrr/if.c: mtrr_ioctl, + which is GPL-2.0 */ + +long mtrr_ioctl(unsigned int cmd, unsigned long __arg) { + int err = 0; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + void *arg = (void *)__arg; + + __builtin_memset(&gentry, 0, sizeof(gentry)); + + switch (cmd) { + case MTRRIOC_ADD_ENTRY: + case MTRRIOC_SET_ENTRY: + case MTRRIOC_DEL_ENTRY: + case MTRRIOC_KILL_ENTRY: + case MTRRIOC_ADD_PAGE_ENTRY: + case MTRRIOC_SET_PAGE_ENTRY: + case MTRRIOC_DEL_PAGE_ENTRY: + case MTRRIOC_KILL_PAGE_ENTRY: + if (copy_from_user(&sentry, arg, sizeof(sentry))) + return -EFAULT; + break; + case MTRRIOC_GET_ENTRY: + case MTRRIOC_GET_PAGE_ENTRY: + if (copy_from_user(&gentry, arg, sizeof(gentry))) + return -EFAULT; + break; + } + + switch (cmd) { + default: + return -ENOTTY; + case MTRRIOC_ADD_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_SET_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_DEL_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_KILL_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_GET_ENTRY: + check_init_u64 (gentry.base); + check_init_u32 (gentry.size); + check_init_u32 (gentry.regnum); + check_init_u32 (gentry.type); + check_init_u32 (gentry._pad); + break; + case MTRRIOC_ADD_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_SET_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_DEL_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_KILL_PAGE_ENTRY: + check_init_u64 (sentry.base); + check_init_u32 (sentry.size); + check_init_u32 (sentry.type); + break; + case MTRRIOC_GET_PAGE_ENTRY: + check_init_u64 (gentry.base); + check_init_u32 (gentry.size); + check_init_u32 (gentry.regnum); + check_init_u32 (gentry.type); + check_init_u32 (gentry._pad); + break; + } + + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c new file mode 100644 index 0000000..f5cdb5c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c @@ -0,0 +1,27 @@ +struct snd_ac97 { + // snip + unsigned int id; + // snip +}; + +int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg) { + + switch (ac97->id) { + case 0x53544d02: + if (reg == 0x22 || reg == 0x7a) + return 1; + __attribute__((__fallthrough__)); + case 0x414b4d00: + return 0; + } + return 1; +} + +int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg) { + if (ac97->id == 0x414c4781) + { + if (!snd_ac97_valid_reg(ac97, reg)) + return -22; + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c new file mode 100644 index 0000000..10b2f29 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c @@ -0,0 +1,68 @@ +/* { dg-additional-options "-fno-analyzer-call-summaries" } */ + +typedef unsigned char u8; +typedef signed int s32; +typedef unsigned int u32; + +enum v4l2_mpeg_video_hevc_profile { + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN = 0, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE = 1, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 = 2 +}; +enum v4l2_buf_type { + V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, + V4L2_BUF_TYPE_VIDEO_OUTPUT = 2 +}; +struct v4l2_fmtdesc { + u32 index; + u32 type; +}; +struct v4l2_ctrl; +s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); +struct create_channel_param { + u8 profile; +}; + +u8 +hevc_profile_to_mcu_profile(enum v4l2_mpeg_video_hevc_profile profile) { + switch (profile) { + default: + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: + return 1; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: + return 2; + case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: + return 3; + } +} + +int fill_create_channel_param(struct v4l2_ctrl *ctrl, + struct create_channel_param *param) { + enum v4l2_mpeg_video_hevc_profile profile; + profile = v4l2_ctrl_g_ctrl(ctrl); + param->profile = hevc_profile_to_mcu_profile(profile); + return 0; +} + +int allegro_enum_fmt_vid(struct v4l2_fmtdesc *f) { + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (f->index >= 1) + return -22; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (f->index >= 2) + return -22; + break; + default: + return -22; + } + return 0; +} + +int allegro_ioctl_streamon(struct v4l2_ctrl *ctrl, + struct create_channel_param *param) { + fill_create_channel_param(ctrl, param); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c new file mode 100644 index 0000000..75a99ad --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + struct ethtool_link_settings base; + u32 lanes; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c new file mode 100644 index 0000000..32ba30f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/uninit-6b.c @@ -0,0 +1,29 @@ +/* Reduced from uninit false positive seen on Linux kernel with + net/ethtool/ioctl.c */ + +typedef signed char s8; +typedef unsigned int u32; +typedef __SIZE_TYPE__ size_t; + +void *memset(void *s, int c, size_t n); + +struct ethtool_link_settings { + u32 cmd; + s8 link_mode_masks_nwords; +}; + +struct ethtool_link_ksettings { + u32 lanes; + struct ethtool_link_settings base; +}; + +struct ethtool_link_settings +ethtool_get_link_ksettings(void) { + struct ethtool_link_ksettings link_ksettings; + + memset(&link_ksettings, 0, sizeof(link_ksettings)); + link_ksettings.base.cmd = 0x0000004c; + link_ksettings.base.link_mode_masks_nwords = -3; + + return link_ksettings.base; +} diff --git a/gcc/testsuite/gcc.dg/fold-convlshift-3.c b/gcc/testsuite/gcc.dg/fold-convlshift-3.c new file mode 100644 index 0000000..8d01191 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-convlshift-3.c @@ -0,0 +1,8 @@ +/* PR middle-end/102029 */ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ +int * +foo (const __PTRDIFF_TYPE__ l) +{ + return (int *) (l << 2); +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c new file mode 100644 index 0000000..53ffa1c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-7.c @@ -0,0 +1,13 @@ +/* { dg-options "-O2 --param modref-max-accesses=1 -fdump-tree-modref1" } */ +/* { dg-do compile } */ +struct a { + int array[10]; + int tail; +}; +int test(struct a *a, int p) +{ + a->array[p] = 0; + a->array[0] = 1; +} +/* All three accesses combine to one bigger access. */ +/* { dg-final { scan-tree-dump-not "param=modref-max-accesses" "modref1" } } */ diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index fb0e429..c430855 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -8220,10 +8220,12 @@ ipa_pta_execute (void) FOR_EACH_DEFINED_FUNCTION (node) { varinfo_t vi; - /* Nodes without a body are not interesting. Especially do not - visit clones at this point for now - we get duplicate decls - there for inline clones at least. */ - if (!node->has_gimple_body_p () || node->inlined_to) + /* Nodes without a body in this partition are not interesting. + Especially do not visit clones at this point for now - we + get duplicate decls there for inline clones at least. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->inlined_to) continue; node->get_body (); @@ -8301,8 +8303,10 @@ ipa_pta_execute (void) struct function *func; basic_block bb; - /* Nodes without a body are not interesting. */ - if (!node->has_gimple_body_p () || node->clone_of) + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) continue; if (dump_file) @@ -8431,8 +8435,10 @@ ipa_pta_execute (void) unsigned i; basic_block bb; - /* Nodes without a body are not interesting. */ - if (!node->has_gimple_body_p () || node->clone_of) + /* Nodes without a body in this partition are not interesting. */ + if (!node->has_gimple_body_p () + || node->in_other_partition + || node->clone_of) continue; fn = DECL_STRUCT_FUNCTION (node->decl); diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c index b9709a6..813f468 100644 --- a/gcc/tree-vectorizer.c +++ b/gcc/tree-vectorizer.c @@ -1033,7 +1033,10 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab, only non-if-converted parts took part in BB vectorization. */ if (flag_tree_slp_vectorize != 0 && loop_vectorized_call - && ! loop->inner) + && ! loop->inner + /* This would purely be a workaround and should be removed + once PR100089 is fixed. */ + && flag_vect_cost_model != VECT_COST_MODEL_VERY_CHEAP) { basic_block bb = loop->header; bool require_loop_vectorize = false; diff --git a/include/ChangeLog b/include/ChangeLog index 6584611..23e0fa2 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + * gomp-constants.h (GOMP_TASK_FLAG_STRICT): Define. + 2021-07-24 Marek Polacek <polacek@redhat.com> * ansidecl.h: Check if __cplusplus is defined before checking diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog index aa9e83b..4686bf5 100644 --- a/libgomp/ChangeLog +++ b/libgomp/ChangeLog @@ -1,3 +1,32 @@ +2021-08-23 Thomas Schwinge <thomas@codesourcery.com> + Jakub Jelinek <jakub@redhat.com> + + * testsuite/libgomp.c/address-space-1.c: New file. + +2021-08-23 Thomas Schwinge <thomas@codesourcery.com> + + * testsuite/lib/libgomp.exp + (check_effective_target_offload_target_intelmic): Remove 'proc'. + (check_effective_target_offload_device_intel_mic): New 'proc'. + * testsuite/libgomp.c-c++-common/on_device_arch.h + (device_arch_intel_mic, on_device_arch_intel_mic): New. + * testsuite/libgomp.c-c++-common/target-45.c: Use that for + 'dg-xfail-run-if'. + * testsuite/libgomp.fortran/target10.f90: Likewise. + +2021-08-23 Tobias Burnus <tobias@codesourcery.com> + + * testsuite/libgomp.fortran/taskloop-4-a.f90: New test. + * testsuite/libgomp.fortran/taskloop-4.f90: New test. + * testsuite/libgomp.fortran/taskloop-5-a.f90: New test. + * testsuite/libgomp.fortran/taskloop-5.f90: New test. + +2021-08-23 Jakub Jelinek <jakub@redhat.com> + + * taskloop.c (GOMP_taskloop): Handle GOMP_TASK_FLAG_STRICT. + * testsuite/libgomp.c-c++-common/taskloop-4.c (main): Fix up comment. + * testsuite/libgomp.c-c++-common/taskloop-5.c: New test. + 2021-08-22 Thomas Schwinge <thomas@codesourcery.com> * config/nvptx/error.c (fwrite, exit): Override, too. diff --git a/libgomp/testsuite/lib/libgomp.exp b/libgomp/testsuite/lib/libgomp.exp index ba8a732..57fb6b0 100644 --- a/libgomp/testsuite/lib/libgomp.exp +++ b/libgomp/testsuite/lib/libgomp.exp @@ -374,11 +374,6 @@ proc check_effective_target_offload_target_amdgcn { } { return [libgomp_check_effective_target_offload_target "amdgcn"] } -# Return 1 if compiling for offload target intelmic -proc check_effective_target_offload_target_intelmic { } { - return [libgomp_check_effective_target_offload_target "*-intelmic"] -} - # Return 1 if offload device is available. proc check_effective_target_offload_device { } { return [check_runtime_nocache offload_device_available_ { @@ -453,6 +448,18 @@ proc check_effective_target_openacc_nvidia_accel_selected { } { return [string match "nvidia" $openacc_device_type] } +# Return 1 if using Intel MIC offload device. +proc check_effective_target_offload_device_intel_mic { } { + return [check_runtime_nocache offload_device_intel_mic { + #include <omp.h> + #include "testsuite/libgomp.c-c++-common/on_device_arch.h" + int main () + { + return !on_device_arch_intel_mic (); + } + } ] +} + # Return 1 if the OpenACC 'host' device type is selected. proc check_effective_target_openacc_host_selected { } { diff --git a/libgomp/testsuite/libgomp.c-c++-common/on_device_arch.h b/libgomp/testsuite/libgomp.c-c++-common/on_device_arch.h index 1c0753c..ee541dd 100644 --- a/libgomp/testsuite/libgomp.c-c++-common/on_device_arch.h +++ b/libgomp/testsuite/libgomp.c-c++-common/on_device_arch.h @@ -6,7 +6,14 @@ device_arch_nvptx (void) return GOMP_DEVICE_NVIDIA_PTX; } +/* static */ int +device_arch_intel_mic (void) +{ + return GOMP_DEVICE_INTEL_MIC; +} + #pragma omp declare variant (device_arch_nvptx) match(construct={target},device={arch(nvptx)}) +#pragma omp declare variant (device_arch_intel_mic) match(construct={target},device={arch(intel_mic)}) /* static */ int device_arch (void) { @@ -28,3 +35,9 @@ on_device_arch_nvptx () { return on_device_arch (GOMP_DEVICE_NVIDIA_PTX); } + +int +on_device_arch_intel_mic () +{ + return on_device_arch (GOMP_DEVICE_INTEL_MIC); +} diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-45.c b/libgomp/testsuite/libgomp.c-c++-common/target-45.c index ec0d202..81acee8 100644 --- a/libgomp/testsuite/libgomp.c-c++-common/target-45.c +++ b/libgomp/testsuite/libgomp.c-c++-common/target-45.c @@ -1,4 +1,4 @@ -/* { dg-do run { target { ! offload_target_intelmic } } } */ +/* { dg-xfail-run-if TODO { offload_device_intel_mic } } */ #include <omp.h> #include <stdlib.h> diff --git a/libgomp/testsuite/libgomp.c/address-space-1.c b/libgomp/testsuite/libgomp.c/address-space-1.c new file mode 100644 index 0000000..6ad57de --- /dev/null +++ b/libgomp/testsuite/libgomp.c/address-space-1.c @@ -0,0 +1,28 @@ +/* Verify OMP instances of variables with address space. */ + +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-require-effective-target offload_device_nonshared_as } */ + +/* With Intel MIC (emulated) offloading: + offload error: process on the device 0 unexpectedly exited with code 0 + { dg-xfail-run-if TODO { offload_device_intel_mic } } */ + +#include <assert.h> + +int __seg_fs a; + +int +main (void) +{ + // a = 123; // SIGSEGV + int b; +#pragma omp target map(alloc: a) map(from: b) + { + a = 321; // no SIGSEGV (given 'offload_device_nonshared_as') + asm volatile ("" : : "g" (&a) : "memory"); + b = a; + } + assert (b == 321); + + return 0; +} diff --git a/libgomp/testsuite/libgomp.fortran/target10.f90 b/libgomp/testsuite/libgomp.fortran/target10.f90 index 0b939ad..f41a726 100644 --- a/libgomp/testsuite/libgomp.fortran/target10.f90 +++ b/libgomp/testsuite/libgomp.fortran/target10.f90 @@ -1,4 +1,5 @@ -! { dg-do run { target { ! offload_target_intelmic } } } +! { dg-do run } +! { dg-xfail-run-if TODO { offload_device_intel_mic } } program main use omp_lib diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog index f6ffd16..acfe208 100644 --- a/libiberty/ChangeLog +++ b/libiberty/ChangeLog @@ -1,3 +1,8 @@ +2021-08-23 Iain Sandoe <iain@sandoe.co.uk> + + * simple-object-mach-o.c (simple_object_mach_o_write_segment): + Cast the first argument to set_32 as needed. + 2021-08-18 Iain Sandoe <iain@sandoe.co.uk> * simple-object-mach-o.c (simple_object_mach_o_write_segment): diff --git a/libiberty/simple-object-mach-o.c b/libiberty/simple-object-mach-o.c index 72b69d1..a8869e7 100644 --- a/libiberty/simple-object-mach-o.c +++ b/libiberty/simple-object-mach-o.c @@ -1228,7 +1228,7 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, /* Swap the indices, if required. */ for (i = 0; i < (nsects_in * 4); ++i) - set_32 (&index[i], index[i]); + set_32 ((unsigned char *) &index[i], index[i]); sechdr_offset += sechdrsize; diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 7ec0fbd..e5e7daa 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,20 @@ +2021-08-23 Jonathan Wakely <jwakely@redhat.com> + + * include/std/ranges (basic_istream_view): Add default template + argument. + * testsuite/std/ranges/istream_view.cc: Check it. + +2021-08-23 Jonathan Wakely <jwakely@redhat.com> + + * libsupc++/dyncast.cc (__dynamic_cast): Add __builtin_expect to + precondition check. + +2021-08-23 Jonathan Wakely <jwakely@redhat.com> + + PR libstdc++/90787 + * testsuite/util/testsuite_fs.h (permissions_are_testable): + Define as inline. + 2021-08-20 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/90787 diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 3d49a26..b373e4f 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -679,7 +679,8 @@ namespace views = requires(basic_istream<_CharT, _Traits>& is, _Val& t) { is >> t; }; } // namespace __detail - template<movable _Val, typename _CharT, typename _Traits> + template<movable _Val, typename _CharT, + typename _Traits = char_traits<_CharT>> requires default_initializable<_Val> && __detail::__stream_extractable<_Val, _CharT, _Traits> class basic_istream_view diff --git a/libstdc++-v3/libsupc++/dyncast.cc b/libstdc++-v3/libsupc++/dyncast.cc index f8f707e..a1138d0 100644 --- a/libstdc++-v3/libsupc++/dyncast.cc +++ b/libstdc++-v3/libsupc++/dyncast.cc @@ -47,9 +47,9 @@ __dynamic_cast (const void *src_ptr, // object started from const __class_type_info *dst_type, // desired target type ptrdiff_t src2dst) // how src and dst are related { - if (!src_ptr) - /* Handle precondition violations gracefully. */ - return NULL; + if (__builtin_expect(!src_ptr, 0)) + return NULL; // Handle precondition violations gracefully. + const void *vtable = *static_cast <const void *const *> (src_ptr); const vtable_prefix *prefix = (adjust_pointer <vtable_prefix> @@ -70,7 +70,7 @@ __dynamic_cast (const void *src_ptr, // object started from (whole_vtable, -ptrdiff_t (offsetof (vtable_prefix, origin)))); if (whole_prefix->whole_type != whole_type) return NULL; - + whole_type->__do_dyncast (src2dst, __class_type_info::__contained_public, dst_type, whole_ptr, src_type, src_ptr, result); if (!result.dst_ptr) diff --git a/libstdc++-v3/testsuite/std/ranges/istream_view.cc b/libstdc++-v3/testsuite/std/ranges/istream_view.cc index 2f15f78..f5c0c2a 100644 --- a/libstdc++-v3/testsuite/std/ranges/istream_view.cc +++ b/libstdc++-v3/testsuite/std/ranges/istream_view.cc @@ -94,6 +94,15 @@ test05() ; } +void +test06() +{ + // Default template argument + using V = std::ranges::basic_istream_view<int, char>; + using W = std::ranges::basic_istream_view<int, char, std::char_traits<char>>; + static_assert( std::is_same_v<V, W> ); +} + int main() { @@ -102,4 +111,5 @@ main() test03(); test04(); test05(); + test06(); } diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h index 674b60b..0d32a61 100644 --- a/libstdc++-v3/testsuite/util/testsuite_fs.h +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h @@ -160,7 +160,7 @@ namespace __gnu_test path_type path; }; - bool + inline bool permissions_are_testable(bool print_msg = true) { bool testable = false; |