aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2021-08-24 09:29:48 +0200
committerMartin Liska <mliska@suse.cz>2021-08-24 09:29:48 +0200
commiteb2de151c582a38efc53ce57416f7bd7a3a9c0eb (patch)
tree653a76a3d1f126ac3775912c560df0c8fe9e5e8d
parent041709a62f1e184cf6d8fefa486f67ca7e6b784c (diff)
parentf8977166135de09fe36a3b57cc11daa67587604e (diff)
downloadgcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.zip
gcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.tar.gz
gcc-eb2de151c582a38efc53ce57416f7bd7a3a9c0eb.tar.bz2
Merge branch 'master' into devel/sphinx
-rw-r--r--gcc/ChangeLog197
-rw-r--r--gcc/DATESTAMP2
-rw-r--r--gcc/analyzer/ChangeLog181
-rw-r--r--gcc/analyzer/analyzer.cc3
-rw-r--r--gcc/analyzer/analyzer.h4
-rw-r--r--gcc/analyzer/constraint-manager.cc1323
-rw-r--r--gcc/analyzer/constraint-manager.h191
-rw-r--r--gcc/analyzer/diagnostic-manager.cc3
-rw-r--r--gcc/analyzer/engine.cc2
-rw-r--r--gcc/analyzer/feasible-graph.cc7
-rw-r--r--gcc/analyzer/feasible-graph.h7
-rw-r--r--gcc/analyzer/region-model-manager.cc7
-rw-r--r--gcc/analyzer/region-model.cc148
-rw-r--r--gcc/analyzer/region-model.h54
-rw-r--r--gcc/analyzer/sm-file.cc18
-rw-r--r--gcc/analyzer/store.cc77
-rw-r--r--gcc/analyzer/store.h5
-rw-r--r--gcc/analyzer/supergraph.cc99
-rw-r--r--gcc/analyzer/supergraph.h15
-rw-r--r--gcc/c/ChangeLog5
-rw-r--r--gcc/config.gcc2
-rw-r--r--gcc/config/h8300/h8300-protos.h2
-rw-r--r--gcc/config/h8300/h8300.c29
-rw-r--r--gcc/config/h8300/jumpcall.md74
-rw-r--r--gcc/config/h8300/proepi.md10
-rw-r--r--gcc/config/i386/i386-features.c38
-rw-r--r--gcc/config/i386/i386-options.c4
-rw-r--r--gcc/config/i386/i386.c2
-rw-r--r--gcc/config/i386/i386.h5
-rw-r--r--gcc/config/i386/t-omp-device2
-rw-r--r--gcc/config/rs6000/rs6000-gen-builtins.c43
-rw-r--r--gcc/config/rs6000/t-rs600047
-rw-r--r--gcc/cp/ChangeLog5
-rw-r--r--gcc/fortran/ChangeLog18
-rw-r--r--gcc/ipa-modref-tree.h77
-rw-r--r--gcc/ipa-modref.c2
-rw-r--r--gcc/match.pd4
-rw-r--r--gcc/simplify-rtx.c3
-rw-r--r--gcc/testsuite/ChangeLog126
-rw-r--r--gcc/testsuite/g++.dg/lto/pr97565_0.C7
-rw-r--r--gcc/testsuite/g++.dg/lto/pr97565_1.C6
-rw-r--r--gcc/testsuite/g++.dg/tree-ssa/modref-1.C2
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-22.c101
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/data-model-23.c26
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr101837.c10
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr101875.c16
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/pr101962.c51
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/switch.c141
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/switch-2.c42
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/switch-3.c158
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/switch-4.c27
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/torture/switch-5.c68
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/uninit-6.c29
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/uninit-6b.c29
-rw-r--r--gcc/testsuite/gcc.dg/fold-convlshift-3.c8
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/modref-7.c13
-rw-r--r--gcc/tree-ssa-structalias.c22
-rw-r--r--gcc/tree-vectorizer.c5
-rw-r--r--include/ChangeLog4
-rw-r--r--libgomp/ChangeLog29
-rw-r--r--libgomp/testsuite/lib/libgomp.exp17
-rw-r--r--libgomp/testsuite/libgomp.c-c++-common/on_device_arch.h13
-rw-r--r--libgomp/testsuite/libgomp.c-c++-common/target-45.c2
-rw-r--r--libgomp/testsuite/libgomp.c/address-space-1.c28
-rw-r--r--libgomp/testsuite/libgomp.fortran/target10.f903
-rw-r--r--libiberty/ChangeLog5
-rw-r--r--libiberty/simple-object-mach-o.c2
-rw-r--r--libstdc++-v3/ChangeLog17
-rw-r--r--libstdc++-v3/include/std/ranges3
-rw-r--r--libstdc++-v3/libsupc++/dyncast.cc8
-rw-r--r--libstdc++-v3/testsuite/std/ranges/istream_view.cc10
-rw-r--r--libstdc++-v3/testsuite/util/testsuite_fs.h2
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,
+ &reg_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;