diff options
author | Martin Liska <mliska@suse.cz> | 2021-06-23 10:20:15 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2021-06-23 10:20:15 +0200 |
commit | 0c6508fe976763cf4fe57c3cb6954b7ab7d55619 (patch) | |
tree | 371963812b9df9853216acf70d031962332785e3 | |
parent | 272625aab59a0ff3eddf1da2afc6416703eaf24d (diff) | |
parent | c2124b51a9b83c76400ebb1862b26f61410e77db (diff) | |
download | gcc-0c6508fe976763cf4fe57c3cb6954b7ab7d55619.zip gcc-0c6508fe976763cf4fe57c3cb6954b7ab7d55619.tar.gz gcc-0c6508fe976763cf4fe57c3cb6954b7ab7d55619.tar.bz2 |
Merge branch 'master' into devel/sphinx
69 files changed, 3259 insertions, 363 deletions
@@ -1,3 +1,7 @@ +2021-06-22 liuhongt <hongtao.liu@intel.com> + + * MAINTAINERS: Remove my Write After Approval entry. + 2021-06-21 liuhongt <hongtao.liu@intel.com> * MAINTAINERS: Add myself as maintainer of the i386 vector diff --git a/MAINTAINERS b/MAINTAINERS index 4ac4fc5..b4c50a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -472,7 +472,6 @@ Nicolas Koenig <koenigni@student.ethz.ch> Boris Kolpackov <boris@codesynthesis.com> Dave Korn <dave.korn.cygwin@gmail.com> Julia Koval <julia.koval@intel.com> -Hongtao Liu <hongtao.liu@intel.com> Matt Kraai <kraai@ftbfs.org> Jan Kratochvil <jan.kratochvil@redhat.com> Louis Krupp <louis.krupp@zoho.com> diff --git a/contrib/ChangeLog b/contrib/ChangeLog index 9b31022..b46a215 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,7 @@ +2021-06-22 Martin Liska <mliska@suse.cz> + + * mklog.py: Fix flake8 issue. + 2021-06-21 Tobias Burnus <tobias@codesourcery.com> Martin Sebor <msebor@redhat.com> diff --git a/contrib/gcc-git-customization.sh b/contrib/gcc-git-customization.sh index e7e6662..6f8f23d 100755 --- a/contrib/gcc-git-customization.sh +++ b/contrib/gcc-git-customization.sh @@ -28,7 +28,7 @@ git config alias.gcc-undescr \!"f() { o=\$(git config --get gcc-config.upstream) git config alias.gcc-verify '!f() { "`git rev-parse --show-toplevel`/contrib/gcc-changelog/git_check_commit.py" $@; } ; f' git config alias.gcc-backport '!f() { "`git rev-parse --show-toplevel`/contrib/git-backport.py" $@; } ; f' git config alias.gcc-mklog '!f() { "`git rev-parse --show-toplevel`/contrib/mklog.py" $@; } ; f' -git config alias.gcc-commit-mklog '!f() { GCC_FORCE_MKLOG=1 git commit "$@"; }; f' +git config alias.gcc-commit-mklog '!f() { "`git rev-parse --show-toplevel`/contrib/git-commit-mklog.py" $@; }; f' # Make diff on MD files use "(define" as a function marker. # Use this in conjunction with a .gitattributes file containing diff --git a/contrib/git-commit-mklog.py b/contrib/git-commit-mklog.py new file mode 100755 index 0000000..9c59fb9 --- /dev/null +++ b/contrib/git-commit-mklog.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING. If not, write to +# the Free Software Foundation, 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# The script is wrapper for git commit-mklog alias where it parses +# -b/--pr-numbers argument and passes it via environment variable +# to mklog.py script. + +import argparse +import os +import subprocess + +if __name__ == '__main__': + children_args = [] + myenv = os.environ.copy() + + parser = argparse.ArgumentParser(description='git-commit-mklog wrapped') + parser.add_argument('-b', '--pr-numbers', action='store', + type=lambda arg: arg.split(','), nargs='?', + help='Add the specified PRs (comma separated)') + parser.add_argument('-p', '--fill-up-bug-titles', action='store_true', + help='Download title of mentioned PRs') + args, unknown_args = parser.parse_known_args() + + myenv['GCC_FORCE_MKLOG'] = '1' + mklog_args = [] + if args.pr_numbers: + mklog_args.append(f'-b {",".join(args.pr_numbers)}') + if args.fill_up_bug_titles: + mklog_args.append('-p') + + if mklog_args: + myenv['GCC_MKLOG_ARGS'] = ' '.join(mklog_args) + + commit_args = ' '.join(unknown_args) + subprocess.run(f'git commit {commit_args}', shell=True, env=myenv) diff --git a/contrib/mklog.py b/contrib/mklog.py index 0b434f6..674c1dc 100755 --- a/contrib/mklog.py +++ b/contrib/mklog.py @@ -304,7 +304,7 @@ if __name__ == '__main__': parser.add_argument('input', nargs='?', help='Patch file (or missing, read standard input)') parser.add_argument('-b', '--pr-numbers', action='store', - type=lambda arg: arg.split(','), nargs="?", + type=lambda arg: arg.split(','), nargs='?', help='Add the specified PRs (comma separated)') parser.add_argument('-s', '--no-functions', action='store_true', help='Do not generate function names in ChangeLogs') diff --git a/contrib/prepare-commit-msg b/contrib/prepare-commit-msg index 969847d..5da8784 100755 --- a/contrib/prepare-commit-msg +++ b/contrib/prepare-commit-msg @@ -78,4 +78,4 @@ else tee="cat" fi -git $cmd | $tee | git gcc-mklog -c "$COMMIT_MSG_FILE" +git $cmd | $tee | git gcc-mklog $GCC_MKLOG_ARGS -c "$COMMIT_MSG_FILE" diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5e73922..502a814 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,196 @@ +2021-06-22 Sergei Trofimovich <siarheit@google.com> + + * doc/rtl.texi: drop unbalanced parenthesis. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR middle-end/101156 + * gimplify.c (gimplify_expr): Remove premature incorrect + optimization. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/101159 + * tree-vect-patterns.c (vect_recog_popcount_pattern): Fix some + comment typos. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/101160 + * function.c (assign_parms): For decl_result with TYPE_EMPTY_P type + clear crtl->return_rtx instead of keeping it referencing a pseudo. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + Andrew Pinski <apinski@marvell.com> + + PR tree-optimization/101162 + * fold-const.c (range_check_type): Handle OFFSET_TYPE like pointer + types. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * range-op.cc (range_relational_tests): New. + (range_op_tests): Call range_relational_tests. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * range-op.cc (operator_cast::lhs_op1_relation): New. + (operator_identity::lhs_op1_relation): Mew. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * range-op.cc (operator_minus::op1_op2_relation_effect): New. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * range-op.cc (operator_plus::lhs_op1_relation): New. + (operator_plus::lhs_op2_relation): New. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * gimple-range-cache.cc (ranger_cache::ranger_cache): Create a + relation_oracle if dominators exist. + (ranger_cache::~ranger_cache): Dispose of oracle. + (ranger_cache::dump_bb): Dump oracle. + * gimple-range.cc (fur_source::fur_source): New. + (fur_source::get_operand): Use mmeber query. + (fur_source::get_phi_operand): Use member_query. + (fur_source::query_relation): New. + (fur_source::register_dependency): Delete. + (fur_source::register_relation): New. + (fur_edge::fur_edge): Adjust. + (fur_edge::get_phi_operand): Fix comment. + (fur_edge::query): Delete. + (fur_stmt::fur_stmt): Adjust. + (fur_stmt::query): Delete. + (fur_depend::fur_depend): Adjust. + (fur_depend::register_relation): New. + (fur_depend::register_relation): New. + (fur_list::fur_list): Adjust. + (fur_list::get_operand): Use member query. + (fold_using_range::range_of_range_op): Process and query relations. + (fold_using_range::range_of_address): Adjust dependency call. + (fold_using_range::range_of_phi): Ditto. + (gimple_ranger::gimple_ranger): New. Use ranger_ache oracle. + (fold_using_range::relation_fold_and_or): New. + (fold_using_range::postfold_gcond_edges): New. + * gimple-range.h (class gimple_ranger): Adjust. + (class fur_source): Adjust members. + (class fur_stmt): Ditto. + (class fold_using_range): Ditto. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * range-op.cc (range_operator::wi_fold): Apply relation effect. + (range_operator::fold_range): Adjust and apply relation effect. + (*::fold_range): Add relation parameters. + (*::op1_range): Ditto. + (*::op2_range): Ditto. + (range_operator::lhs_op1_relation): New. + (range_operator::lhs_op2_relation): New. + (range_operator::op1_op2_relation): New. + (range_operator::op1_op2_relation_effect): New. + (relop_early_resolve): New. + (operator_equal::op1_op2_relation): New. + (operator_equal::fold_range): Call relop_early_resolve. + (operator_not_equal::op1_op2_relation): New. + (operator_not_equal::fold_range): Call relop_early_resolve. + (operator_lt::op1_op2_relation): New. + (operator_lt::fold_range): Call relop_early_resolve. + (operator_le::op1_op2_relation): New. + (operator_le::fold_range): Call relop_early_resolve. + (operator_gt::op1_op2_relation): New. + (operator_gt::fold_range): Call relop_early_resolve. + (operator_ge::op1_op2_relation): New. + (operator_ge::fold_range): Call relop_early_resolve. + * range-op.h (class range_operator): Adjust parameters and methods. + +2021-06-22 Andrew MacLeod <amacleod@redhat.com> + + * Makefile.in (OBJS): Add value-relation.o. + * gimple-range.h: Adjust include files. + * tree-data-ref.c: Adjust include file order. + * value-query.cc (range_query::get_value_range): Default to no oracle. + (range_query::query_relation): New. + (range_query::query_relation): New. + * value-query.h (class range_query): Adjust. + * value-relation.cc: New. + * value-relation.h: New. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101151 + * tree-ssa-sink.c (statement_sink_location): Expand irreducible + region check. + +2021-06-22 Jojo R <rjiejie@linux.alibaba.com> + + * config/riscv/riscv.c (thead_c906_tune_info): New. + (riscv_tune_info_table): Use new tune. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101158 + * tree-vect-slp.c (vect_build_slp_tree_1): Move same operand + checking after checking for matching operation. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101159 + * tree-vect-patterns.c (vect_recog_popcount_pattern): Add + missing NULL vectype check. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101154 + * tree-vect-slp.c (vect_build_slp_tree_2): Fix out-of-bound access. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR target/11877 + * config/i386/i386-protos.h (ix86_last_zero_store_uid): Declare. + * config/i386/i386-expand.c (ix86_last_zero_store_uid): New variable. + * config/i386/i386.c (ix86_expand_prologue): Clear it. + * config/i386/i386.md (peephole2s for 1/2/4 stores of const0_rtx): + Remove "" from match_operand. Emit new insns using emit_move_insn and + set ix86_last_zero_store_uid to INSN_UID of the last store. + Add peephole2s for 1/2/4 stores of const0_rtx following previous + successful peep2s. + +2021-06-22 Martin Liska <mliska@suse.cz> + + * auto-profile.c (AUTO_PROFILE_VERSION): Bump as string format + was changed. + +2021-06-22 Martin Liska <mliska@suse.cz> + + * gcov-io.h: Remove padding entries. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR tree-optimization/97770 + * tree-vect-patterns.c (vect_recog_popcount_pattern): + New. + (vect_recog_func vect_vect_recog_func_ptrs): Add new pattern. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR target/100267 + * config/i386/i386-builtin.def (BDESC): Adjust builtin name. + * config/i386/sse.md (<avx512>_expand<mode>_mask): Rename to .. + (expand<mode>_mask): this .. + (*expand<mode>_mask): New pre_reload splitter to transform + v{,p}expand* to vmov* when mask is zero, all ones, or has all + ones in it's lower part, otherwise still generate + v{,p}expand*. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR target/100310 + * config/i386/i386-expand.c + (ix86_expand_special_args_builtin): Keep constm1_operand only + if it satisfies insn's operand predicate. + 2021-06-21 Jason Merrill <jason@redhat.com> PR target/88529 diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index bb14796..e8c8a9f 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20210622 +20210623 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 4cb2966..ebf2644 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1688,6 +1688,7 @@ OBJS = \ value-query.o \ value-range.o \ value-range-equiv.o \ + value-relation.o \ value-prof.o \ var-tracking.o \ varasm.o \ diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 84fa208..3630b2e 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,11 @@ +2021-06-22 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101143 + * region-model.cc (compat_types_p): New function. + (region_model::create_region_for_heap_alloc): Convert assertion to + an error check. + (region_model::create_region_for_alloca): Likewise. + 2021-06-18 David Malcolm <dmalcolm@redhat.com> * store.cc (binding_cluster::get_any_binding): Make symbolic reads diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 462fe6d..ee11e82 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1443,6 +1443,17 @@ assert_compat_types (tree src_type, tree dst_type) } } +/* Return true if SRC_TYPE can be converted to DST_TYPE as a no-op. */ + +static bool +compat_types_p (tree src_type, tree dst_type) +{ + if (src_type && dst_type && !VOID_TYPE_P (dst_type)) + if (!(useless_type_conversion_p (src_type, dst_type))) + return false; + return true; +} + /* Get the region for PV within this region_model, emitting any diagnostics to CTXT. */ @@ -3402,8 +3413,8 @@ const region * region_model::create_region_for_heap_alloc (const svalue *size_in_bytes) { const region *reg = m_mgr->create_region_for_heap_alloc (); - assert_compat_types (size_in_bytes->get_type (), size_type_node); - set_dynamic_extents (reg, size_in_bytes); + if (compat_types_p (size_in_bytes->get_type (), size_type_node)) + set_dynamic_extents (reg, size_in_bytes); return reg; } @@ -3414,8 +3425,8 @@ const region * region_model::create_region_for_alloca (const svalue *size_in_bytes) { const region *reg = m_mgr->create_region_for_alloca (m_current_frame); - assert_compat_types (size_in_bytes->get_type (), size_type_node); - set_dynamic_extents (reg, size_in_bytes); + if (compat_types_p (size_in_bytes->get_type (), size_type_node)) + set_dynamic_extents (reg, size_in_bytes); return reg; } diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c index cc2eaee..2986b49 100644 --- a/gcc/config/i386/i386-expand.c +++ b/gcc/config/i386/i386-expand.c @@ -1316,6 +1316,9 @@ find_nearest_reg_def (rtx_insn *insn, int regno1, int regno2) return false; } +/* INSN_UID of the last insn emitted by zero store peephole2s. */ +int ix86_last_zero_store_uid; + /* Split lea instructions into a sequence of instructions which are executed on ALU to avoid AGU stalls. It is assumed that it is allowed to clobber flags register diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index e6ac939..1d05206 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -111,6 +111,7 @@ extern bool ix86_use_lea_for_mov (rtx_insn *, rtx[]); extern bool ix86_avoid_lea_for_addr (rtx_insn *, rtx[]); extern void ix86_split_lea_for_addr (rtx_insn *, rtx[], machine_mode); extern bool ix86_lea_for_add_ok (rtx_insn *, rtx[]); +extern int ix86_last_zero_store_uid; extern bool ix86_vec_interleave_v2df_operator_ok (rtx operands[3], bool high); extern bool ix86_dep_by_shift_count (const_rtx set_insn, const_rtx use_insn); extern bool ix86_agi_dependent (rtx_insn *set_insn, rtx_insn *use_insn); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index c3740ff..3d5883b 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -8196,6 +8196,7 @@ ix86_expand_prologue (void) bool save_stub_call_needed; rtx static_chain = NULL_RTX; + ix86_last_zero_store_uid = 0; if (ix86_function_naked (current_function_decl)) { if (flag_stack_usage_info) diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 9116828..700c158 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -19360,37 +19360,96 @@ ;; When optimizing for size, zeroing memory should use a register. (define_peephole2 [(match_scratch:SWI48 0 "r") - (set (match_operand:SWI48 1 "memory_operand" "") (const_int 0)) - (set (match_operand:SWI48 2 "memory_operand" "") (const_int 0)) - (set (match_operand:SWI48 3 "memory_operand" "") (const_int 0)) - (set (match_operand:SWI48 4 "memory_operand" "") (const_int 0))] + (set (match_operand:SWI48 1 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 2 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 3 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 4 "memory_operand") (const_int 0))] "optimize_insn_for_size_p () && peep2_regno_dead_p (0, FLAGS_REG)" - [(set (match_dup 1) (match_dup 0)) - (set (match_dup 2) (match_dup 0)) - (set (match_dup 3) (match_dup 0)) - (set (match_dup 4) (match_dup 0))] + [(const_int 0)] { ix86_expand_clear (operands[0]); + emit_move_insn (operands[1], operands[0]); + emit_move_insn (operands[2], operands[0]); + emit_move_insn (operands[3], operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[4], operands[0])); + DONE; }) (define_peephole2 [(match_scratch:SWI48 0 "r") - (set (match_operand:SWI48 1 "memory_operand" "") (const_int 0)) - (set (match_operand:SWI48 2 "memory_operand" "") (const_int 0))] + (set (match_operand:SWI48 1 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 2 "memory_operand") (const_int 0))] "optimize_insn_for_size_p () && peep2_regno_dead_p (0, FLAGS_REG)" - [(set (match_dup 1) (match_dup 0)) - (set (match_dup 2) (match_dup 0))] + [(const_int 0)] { ix86_expand_clear (operands[0]); + emit_move_insn (operands[1], operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[2], operands[0])); + DONE; }) (define_peephole2 [(match_scratch:SWI48 0 "r") - (set (match_operand:SWI48 1 "memory_operand" "") (const_int 0))] + (set (match_operand:SWI48 1 "memory_operand") (const_int 0))] "optimize_insn_for_size_p () && peep2_regno_dead_p (0, FLAGS_REG)" - [(set (match_dup 1) (match_dup 0))] + [(const_int 0)] { ix86_expand_clear (operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[1], operands[0])); + DONE; +}) + +(define_peephole2 + [(set (match_operand:SWI48 5 "memory_operand") + (match_operand:SWI48 0 "general_reg_operand")) + (set (match_operand:SWI48 1 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 2 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 3 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 4 "memory_operand") (const_int 0))] + "optimize_insn_for_size_p () + && INSN_UID (peep2_next_insn (0)) == ix86_last_zero_store_uid" + [(const_int 0)] +{ + emit_move_insn (operands[5], operands[0]); + emit_move_insn (operands[1], operands[0]); + emit_move_insn (operands[2], operands[0]); + emit_move_insn (operands[3], operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[4], operands[0])); + DONE; +}) + +(define_peephole2 + [(set (match_operand:SWI48 3 "memory_operand") + (match_operand:SWI48 0 "general_reg_operand")) + (set (match_operand:SWI48 1 "memory_operand") (const_int 0)) + (set (match_operand:SWI48 2 "memory_operand") (const_int 0))] + "optimize_insn_for_size_p () + && INSN_UID (peep2_next_insn (0)) == ix86_last_zero_store_uid" + [(const_int 0)] +{ + emit_move_insn (operands[3], operands[0]); + emit_move_insn (operands[1], operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[2], operands[0])); + DONE; +}) + +(define_peephole2 + [(set (match_operand:SWI48 2 "memory_operand") + (match_operand:SWI48 0 "general_reg_operand")) + (set (match_operand:SWI48 1 "memory_operand") (const_int 0))] + "optimize_insn_for_size_p () + && INSN_UID (peep2_next_insn (0)) == ix86_last_zero_store_uid" + [(const_int 0)] +{ + emit_move_insn (operands[2], operands[0]); + ix86_last_zero_store_uid + = INSN_UID (emit_move_insn (operands[1], operands[0])); + DONE; }) ;; Reload dislikes loading constants directly into class_likely_spilled diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c index 1baa299..576960b 100644 --- a/gcc/config/riscv/riscv.c +++ b/gcc/config/riscv/riscv.c @@ -300,6 +300,19 @@ static const struct riscv_tune_param sifive_7_tune_info = { true, /* slow_unaligned_access */ }; +/* Costs to use when optimizing for T-HEAD c906. */ +static const struct riscv_tune_param thead_c906_tune_info = { + {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_add */ + {COSTS_N_INSNS (4), COSTS_N_INSNS (5)}, /* fp_mul */ + {COSTS_N_INSNS (20), COSTS_N_INSNS (20)}, /* fp_div */ + {COSTS_N_INSNS (4), COSTS_N_INSNS (4)}, /* int_mul */ + {COSTS_N_INSNS (6), COSTS_N_INSNS (6)}, /* int_div */ + 1, /* issue_rate */ + 3, /* branch_cost */ + 5, /* memory_cost */ + false, /* slow_unaligned_access */ +}; + /* Costs to use when optimizing for size. */ static const struct riscv_tune_param optimize_size_tune_info = { {COSTS_N_INSNS (1), COSTS_N_INSNS (1)}, /* fp_add */ @@ -348,6 +361,7 @@ static const struct riscv_tune_info riscv_tune_info_table[] = { { "sifive-3-series", generic, &rocket_tune_info }, { "sifive-5-series", generic, &rocket_tune_info }, { "sifive-7-series", sifive_7, &sifive_7_tune_info }, + { "thead-c906", generic, &thead_c906_tune_info }, { "size", generic, &optimize_size_tune_info }, }; diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi index 5af7113..e1e76a9 100644 --- a/gcc/doc/rtl.texi +++ b/gcc/doc/rtl.texi @@ -144,7 +144,7 @@ Currently, @file{rtl.def} defines these classes: @item RTX_OBJ An RTX code that represents an actual object, such as a register (@code{REG}) or a memory location (@code{MEM}, @code{SYMBOL_REF}). -@code{LO_SUM}) is also included; instead, @code{SUBREG} and +@code{LO_SUM} is also included; instead, @code{SUBREG} and @code{STRICT_LOW_PART} are not in this class, but in class @code{RTX_EXTRA}. diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 95673d2..0b33ee9 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -5360,7 +5360,7 @@ range_check_type (tree etype) else return NULL_TREE; } - else if (POINTER_TYPE_P (etype)) + else if (POINTER_TYPE_P (etype) || TREE_CODE (etype) == OFFSET_TYPE) etype = unsigned_type_for (etype); return etype; } diff --git a/gcc/function.c b/gcc/function.c index 6abaf3d..00b2fe7 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -3821,17 +3821,22 @@ assign_parms (tree fndecl) tree decl_result = DECL_RESULT (fndecl); rtx decl_rtl = DECL_RTL (decl_result); - if ((REG_P (decl_rtl) - ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER - : DECL_REGISTER (decl_result)) - /* Unless the psABI says not to. */ - && !TYPE_EMPTY_P (TREE_TYPE (decl_result))) + if (REG_P (decl_rtl) + ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER + : DECL_REGISTER (decl_result)) { rtx real_decl_rtl; - real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result), - fndecl, true); - REG_FUNCTION_VALUE_P (real_decl_rtl) = 1; + /* Unless the psABI says not to. */ + if (TYPE_EMPTY_P (TREE_TYPE (decl_result))) + real_decl_rtl = NULL_RTX; + else + { + real_decl_rtl + = targetm.calls.function_value (TREE_TYPE (decl_result), + fndecl, true); + REG_FUNCTION_VALUE_P (real_decl_rtl) = 1; + } /* The delay slot scheduler assumes that crtl->return_rtx holds the hard register containing the return value, not a temporary pseudo. */ diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index def604dc..4347485 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -714,6 +714,12 @@ ranger_cache::ranger_cache () m_update_list.safe_grow_cleared (last_basic_block_for_fn (cfun)); m_update_list.truncate (0); m_temporal = new temporal_cache; + // If DOM info is available, spawn an oracle as well. + if (dom_info_available_p (CDI_DOMINATORS)) + m_oracle = new relation_oracle (); + else + m_oracle = NULL; + unsigned x, lim = last_basic_block_for_fn (cfun); // Calculate outgoing range info upfront. This will fully populate the // m_maybe_variant bitmap which will help eliminate processing of names @@ -728,6 +734,8 @@ ranger_cache::ranger_cache () ranger_cache::~ranger_cache () { + if (m_oracle) + delete m_oracle; delete m_temporal; m_workback.release (); m_update_list.release (); @@ -750,6 +758,8 @@ ranger_cache::dump_bb (FILE *f, basic_block bb) { m_gori.gori_map::dump (f, bb, false); m_on_entry.dump (f, bb); + if (m_oracle) + m_oracle->dump (f, bb); } // Get the global range for NAME, and return in R. Return false if the diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index 0a2c72b..385cecf 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -47,14 +47,25 @@ along with GCC; see the file COPYING3. If not see #include "vr-values.h" #include "gimple-range.h" -// Evaluate expression EXPR using the source information the class was -// instantiated with. Place the result in R, and return TRUE. If a range -// cannot be calculated, return FALSE. +// Construct a fur_source, and set the m_query field. + +fur_source::fur_source (range_query *q) +{ + if (q) + m_query = q; + else if (cfun) + m_query = get_range_query (cfun); + else + m_query = get_global_range_query (); + m_gori = NULL; +} + +// Invoke range_of_expr on EXPR. bool fur_source::get_operand (irange &r, tree expr) { - return get_range_query (cfun)->range_of_expr (r, expr); + return m_query->range_of_expr (r, expr); } // Evaluate EXPR for this stmt as a PHI argument on edge E. Use the current @@ -63,23 +74,36 @@ fur_source::get_operand (irange &r, tree expr) bool fur_source::get_phi_operand (irange &r, tree expr, edge e) { - return get_range_query (cfun)->range_on_edge (r, e, expr); + return m_query->range_on_edge (r, e, expr); +} + +// Default is no relation. + +relation_kind +fur_source::query_relation (tree op1 ATTRIBUTE_UNUSED, + tree op2 ATTRIBUTE_UNUSED) +{ + return VREL_NONE; } -// Default is to not register any dependencies from fold_using_range. +// Default registers nothing. void -fur_source::register_dependency (tree lhs ATTRIBUTE_UNUSED, - tree rhs ATTRIBUTE_UNUSED) +fur_source::register_relation (gimple *s ATTRIBUTE_UNUSED, + relation_kind k ATTRIBUTE_UNUSED, + tree op1 ATTRIBUTE_UNUSED, + tree op2 ATTRIBUTE_UNUSED) { } -// Default object is the current range query. +// Default registers nothing. -range_query * -fur_source::query () +void +fur_source::register_relation (edge e ATTRIBUTE_UNUSED, + relation_kind k ATTRIBUTE_UNUSED, + tree op1 ATTRIBUTE_UNUSED, + tree op2 ATTRIBUTE_UNUSED) { - return get_range_query (cfun); } // This version of fur_source will pick a range up off an edge. @@ -90,22 +114,16 @@ public: fur_edge (edge e, range_query *q = NULL); virtual bool get_operand (irange &r, tree expr) OVERRIDE; virtual bool get_phi_operand (irange &r, tree expr, edge e) OVERRIDE; - virtual range_query *query () OVERRIDE; private: - range_query *m_query; edge m_edge; }; // Instantiate an edge based fur_source. inline -fur_edge::fur_edge (edge e, range_query *q) +fur_edge::fur_edge (edge e, range_query *q) : fur_source (q) { m_edge = e; - if (q) - m_query = q; - else - m_query = get_range_query (cfun); } // Get the value of EXPR on edge m_edge. @@ -122,31 +140,19 @@ fur_edge::get_operand (irange &r, tree expr) bool fur_edge::get_phi_operand (irange &r, tree expr, edge e) { - // edge to edge recalculations not supoprted yet, until we sort it out. + // Edge to edge recalculations not supoprted yet, until we sort it out. gcc_checking_assert (e == m_edge); return m_query->range_on_edge (r, e, expr); } -// Return the current range_query object. - -range_query * -fur_edge::query () -{ - return m_query; -} - // Instantiate a stmt based fur_source. -fur_stmt::fur_stmt (gimple *s, range_query *q) +fur_stmt::fur_stmt (gimple *s, range_query *q) : fur_source (q) { m_stmt = s; - if (q) - m_query = q; - else - m_query = get_global_range_query (); } -// Retrieve range of EXPR as it occurs as a use on stmt M_STMT. +// Retreive range of EXPR as it occurs as a use on stmt M_STMT. bool fur_stmt::get_operand (irange &r, tree expr) @@ -165,12 +171,12 @@ fur_stmt::get_phi_operand (irange &r, tree expr, edge e) return e_src.get_operand (r, expr); } -// Return the current range_query object. +// Return relation based from m_stmt. -range_query * -fur_stmt::query () +relation_kind +fur_stmt::query_relation (tree op1, tree op2) { - return m_query; + return m_query->query_relation (m_stmt, op1, op2); } // This version of fur_source will pick a range from a stmt, and also register @@ -180,12 +186,15 @@ class fur_depend : public fur_stmt { public: fur_depend (gimple *s, gori_compute *gori, range_query *q = NULL); - virtual void register_dependency (tree lhs, tree rhs) OVERRIDE; + virtual void register_relation (gimple *stmt, relation_kind k, tree op1, + tree op2) OVERRIDE; + virtual void register_relation (edge e, relation_kind k, tree op1, + tree op2) OVERRIDE; private: - gori_compute *m_gori; + relation_oracle *m_oracle; }; -// Instantiate a stmt based fur_source with a GORI object +// Instantiate a stmt based fur_source with a GORI object. inline fur_depend::fur_depend (gimple *s, gori_compute *gori, range_query *q) @@ -193,14 +202,28 @@ fur_depend::fur_depend (gimple *s, gori_compute *gori, range_query *q) { gcc_checking_assert (gori); m_gori = gori; + // Set relations if there is an oracle in the range_query. + // This will enable registering of relationships as they are discovered. + m_oracle = q->oracle (); + +} + +// Register a relation on a stmt if there is an oracle. + +void +fur_depend::register_relation (gimple *s, relation_kind k, tree op1, tree op2) +{ + if (m_oracle) + m_oracle->register_relation (s, k, op1, op2); } -// find and add any dependnecy between LHS and RHS +// Register a relation on an edge if there is an oracle. void -fur_depend::register_dependency (tree lhs, tree rhs) +fur_depend::register_relation (edge e, relation_kind k, tree op1, tree op2) { - m_gori->register_dependency (lhs, rhs); + if (m_oracle) + m_oracle->register_relation (e, k, op1, op2); } // This version of fur_source will pick a range up from a list of ranges @@ -223,7 +246,7 @@ private: // One range supplied for unary operations. -fur_list::fur_list (irange &r1) +fur_list::fur_list (irange &r1) : fur_source (NULL) { m_list = m_local; m_index = 0; @@ -233,7 +256,7 @@ fur_list::fur_list (irange &r1) // Two ranges supplied for binary operations. -fur_list::fur_list (irange &r1, irange &r2) +fur_list::fur_list (irange &r1, irange &r2) : fur_source (NULL) { m_list = m_local; m_index = 0; @@ -244,7 +267,7 @@ fur_list::fur_list (irange &r1, irange &r2) // Arbitrary number of ranges in a vector. -fur_list::fur_list (unsigned num, irange *list) +fur_list::fur_list (unsigned num, irange *list) : fur_source (NULL) { m_list = list; m_index = 0; @@ -257,7 +280,7 @@ bool fur_list::get_operand (irange &r, tree expr) { if (m_index >= m_limit) - return get_range_query (cfun)->range_of_expr (r, expr); + return m_query->range_of_expr (r, expr); r = m_list[m_index++]; gcc_checking_assert (range_compatible_p (TREE_TYPE (expr), r.type ())); return true; @@ -290,7 +313,6 @@ fold_range (irange &r, gimple *s, irange &r1, irange &r2) return f.fold_stmt (r, s, src); } - // Fold stmt S into range R using NUM_ELEMENTS from VECTOR as the initial // operands encountered. @@ -636,18 +658,50 @@ fold_using_range::range_of_range_op (irange &r, gimple *s, fur_source &src) // Fold range, and register any dependency if available. int_range<2> r2 (type); handler->fold_range (r, type, range1, r2); - if (lhs) - src.register_dependency (lhs, op1); + if (lhs && gimple_range_ssa_p (op1)) + { + if (src.gori ()) + src.gori ()->register_dependency (lhs, op1); + relation_kind rel; + rel = handler->lhs_op1_relation (r, range1, range1); + if (rel != VREL_NONE) + src.register_relation (s, rel, lhs, op1); + } } else if (src.get_operand (range2, op2)) { + relation_kind rel = src.query_relation (op1, op2); + if (dump_file && (dump_flags & TDF_DETAILS) && rel != VREL_NONE) + { + fprintf (dump_file, " folding with relation "); + print_relation (dump_file, rel); + fputc ('\n', dump_file); + } // Fold range, and register any dependency if available. - handler->fold_range (r, type, range1, range2); + handler->fold_range (r, type, range1, range2, rel); + relation_fold_and_or (r, s, src); if (lhs) { - src.register_dependency (lhs, op1); - src.register_dependency (lhs, op2); + if (src.gori ()) + { + src.gori ()->register_dependency (lhs, op1); + src.gori ()->register_dependency (lhs, op2); + } + if (gimple_range_ssa_p (op1)) + { + rel = handler->lhs_op1_relation (r, range1, range2); + if (rel != VREL_NONE) + src.register_relation (s, rel, lhs, op1); + } + if (gimple_range_ssa_p (op2)) + { + rel= handler->lhs_op2_relation (r, range1, range2); + if (rel != VREL_NONE) + src.register_relation (s, rel, lhs, op2); + } } + else if (is_a<gcond *> (s)) + postfold_gcond_edges (as_a<gcond *> (s), src); } else r.set_varying (type); @@ -686,8 +740,8 @@ fold_using_range::range_of_address (irange &r, gimple *stmt, fur_source &src) { tree ssa = TREE_OPERAND (base, 0); tree lhs = gimple_get_lhs (stmt); - if (lhs && gimple_range_ssa_p (ssa)) - src.register_dependency (lhs, ssa); + if (lhs && gimple_range_ssa_p (ssa) && src.gori ()) + src.gori ()->register_dependency (lhs, ssa); gcc_checking_assert (irange::supports_type_p (TREE_TYPE (ssa))); src.get_operand (r, ssa); range_cast (r, TREE_TYPE (gimple_assign_rhs1 (stmt))); @@ -764,8 +818,8 @@ fold_using_range::range_of_phi (irange &r, gphi *phi, fur_source &src) edge e = gimple_phi_arg_edge (phi, x); // Register potential dependencies for stale value tracking. - if (gimple_range_ssa_p (arg)) - src.register_dependency (phi_def, arg); + if (gimple_range_ssa_p (arg) && src.gori ()) + src.gori ()->register_dependency (phi_def, arg); // Get the range of the argument on its edge. src.get_phi_operand (arg_range, arg, e); @@ -1149,6 +1203,12 @@ fold_using_range::range_of_cond_expr (irange &r, gassign *s, fur_source &src) return true; } +gimple_ranger::gimple_ranger () +{ + // If the cache has a relation oracle, use it. + m_oracle = m_cache.oracle (); +} + bool gimple_ranger::range_of_expr (irange &r, tree expr, gimple *stmt) { @@ -1464,6 +1524,185 @@ fold_using_range::range_of_ssa_name_with_loop_info (irange &r, tree name, r.set_varying (type); } +// ----------------------------------------------------------------------- + +// Check if an && or || expression can be folded based on relations. ie +// c_2 = a_6 > b_7 +// c_3 = a_6 < b_7 +// c_4 = c_2 && c_3 +// c_2 and c_3 can never be true at the same time, +// Therefore c_4 can always resolve to false based purely on the relations. + +void +fold_using_range::relation_fold_and_or (irange& lhs_range, gimple *s, + fur_source &src) +{ + // No queries or already folded. + if (!src.gori () || !src.query ()->oracle () || lhs_range.singleton_p ()) + return; + + // Only care about AND and OR expressions. + enum tree_code code = gimple_expr_code (s); + bool is_and = false; + if (code == BIT_AND_EXPR || code == TRUTH_AND_EXPR) + is_and = true; + else if (code != BIT_IOR_EXPR && code != TRUTH_OR_EXPR) + return; + + tree lhs = gimple_get_lhs (s); + tree ssa1 = gimple_range_ssa_p (gimple_range_operand1 (s)); + tree ssa2 = gimple_range_ssa_p (gimple_range_operand2 (s)); + + // Deal with || and && only when there is a full set of symbolics. + if (!lhs || !ssa1 || !ssa2 + || (TREE_CODE (TREE_TYPE (lhs)) != BOOLEAN_TYPE) + || (TREE_CODE (TREE_TYPE (ssa1)) != BOOLEAN_TYPE) + || (TREE_CODE (TREE_TYPE (ssa2)) != BOOLEAN_TYPE)) + return; + + // Now we know its a boolean AND or OR expression with boolean operands. + // Ideally we search dependencies for common names, and see what pops out. + // until then, simply try to resolve direct dependencies. + + // Both names will need to have 2 direct dependencies. + tree ssa1_dep2 = src.gori ()->depend2 (ssa1); + tree ssa2_dep2 = src.gori ()->depend2 (ssa2); + if (!ssa1_dep2 || !ssa2_dep2) + return; + + tree ssa1_dep1 = src.gori ()->depend1 (ssa1); + tree ssa2_dep1 = src.gori ()->depend1 (ssa2); + // Make sure they are the same dependencies, and detect the order of the + // relationship. + bool reverse_op2 = true; + if (ssa1_dep1 == ssa2_dep1 && ssa1_dep2 == ssa2_dep2) + reverse_op2 = false; + else if (ssa1_dep1 != ssa2_dep2 || ssa1_dep2 != ssa2_dep1) + return; + + range_operator *handler1 = gimple_range_handler (SSA_NAME_DEF_STMT (ssa1)); + range_operator *handler2 = gimple_range_handler (SSA_NAME_DEF_STMT (ssa2)); + + int_range<2> bool_one (boolean_true_node, boolean_true_node); + + relation_kind relation1 = handler1->op1_op2_relation (bool_one); + relation_kind relation2 = handler2->op1_op2_relation (bool_one); + if (relation1 == VREL_NONE || relation2 == VREL_NONE) + return; + + if (reverse_op2) + relation2 = relation_negate (relation2); + + // x && y is false if the relation intersection of the true cases is NULL. + if (is_and && relation_intersect (relation1, relation2) == VREL_EMPTY) + lhs_range = int_range<2> (boolean_false_node, boolean_false_node); + // x || y is true if the union of the true cases is NO-RELATION.. + // ie, one or the other being true covers the full range of possibilties. + else if (!is_and && relation_union (relation1, relation2) == VREL_NONE) + lhs_range = bool_one; + else + return; + + range_cast (lhs_range, TREE_TYPE (lhs)); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Relation adjustment: "); + print_generic_expr (dump_file, ssa1, TDF_SLIM); + fprintf (dump_file, " and "); + print_generic_expr (dump_file, ssa2, TDF_SLIM); + fprintf (dump_file, " combine to produce "); + lhs_range.dump (dump_file); + fputc ('\n', dump_file); + } + + return; +} + +// Register any outgoing edge relations from a conditional branch. + +void +fold_using_range::postfold_gcond_edges (gcond *s, fur_source &src) +{ + int_range_max r; + tree name; + range_operator *handler; + basic_block bb = gimple_bb (s); + + edge e0 = EDGE_SUCC (bb, 0); + if (!single_pred_p (e0->dest)) + e0 = NULL; + + edge e1 = EDGE_SUCC (bb, 1); + if (!single_pred_p (e1->dest)) + e1 = NULL; + + // At least one edge needs to be single pred. + if (!e0 && !e1) + return; + + // First, register the gcond itself. This will catch statements like + // if (a_2 < b_5) + tree ssa1 = gimple_range_ssa_p (gimple_range_operand1 (s)); + tree ssa2 = gimple_range_ssa_p (gimple_range_operand2 (s)); + if (ssa1 && ssa2) + { + handler = gimple_range_handler (s); + gcc_checking_assert (handler); + if (e0) + { + gcond_edge_range (r, e0); + relation_kind relation = handler->op1_op2_relation (r); + if (relation != VREL_NONE) + src.register_relation (e0, relation, ssa1, ssa2); + } + if (e1) + { + gcond_edge_range (r, e1); + relation_kind relation = handler->op1_op2_relation (r); + if (relation != VREL_NONE) + src.register_relation (e1, relation, ssa1, ssa2); + } + } + + // Outgoing relations of GORI exports require a gori engine. + if (!src.gori ()) + return; + + range_query *q = src.query (); + // Now look for other relations in the exports. This will find stmts + // leading to the condition such as: + // c_2 = a_4 < b_7 + // if (c_2) + + FOR_EACH_GORI_EXPORT_NAME (*(src.gori ()), bb, name) + { + if (TREE_CODE (TREE_TYPE (name)) != BOOLEAN_TYPE) + continue; + gimple *stmt = SSA_NAME_DEF_STMT (name); + handler = gimple_range_handler (stmt); + if (!handler) + continue; + tree ssa1 = gimple_range_ssa_p (gimple_range_operand1 (stmt)); + tree ssa2 = gimple_range_ssa_p (gimple_range_operand2 (stmt)); + if (ssa1 && ssa2) + { + if (e0 && src.gori ()->outgoing_edge_range_p (r, e0, name, *q) + && r.singleton_p ()) + { + relation_kind relation = handler->op1_op2_relation (r); + if (relation != VREL_NONE) + src.register_relation (e0, relation, ssa1, ssa2); + } + if (e1 && src.gori ()->outgoing_edge_range_p (r, e1, name, *q) + && r.singleton_p ()) + { + relation_kind relation = handler->op1_op2_relation (r); + if (relation != VREL_NONE) + src.register_relation (e1, relation, ssa1, ssa2); + } + } + } +} // -------------------------------------------------------------------------- // trace_ranger implementation. diff --git a/gcc/gimple-range.h b/gcc/gimple-range.h index fc28123..87911b9 100644 --- a/gcc/gimple-range.h +++ b/gcc/gimple-range.h @@ -24,8 +24,8 @@ along with GCC; see the file COPYING3. If not see #include "range.h" -#include "range-op.h" #include "value-query.h" +#include "range-op.h" #include "gimple-range-edge.h" #include "gimple-range-gori.h" #include "gimple-range-cache.h" @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see class gimple_ranger : public range_query { public: + gimple_ranger (); virtual bool range_of_stmt (irange &r, gimple *, tree name = NULL) OVERRIDE; virtual bool range_of_expr (irange &r, tree name, gimple * = NULL) OVERRIDE; virtual bool range_on_edge (irange &r, edge e, tree name) OVERRIDE; @@ -74,15 +75,25 @@ protected: // Source of all operands for fold_using_range and gori_compute. // It abstracts out the source of an operand so it can come from a stmt or -// and edge or anywhere a derived classof fur_source wants. +// and edge or anywhere a derived class of fur_source wants. +// THe default simply picks up ranges from the current range_query. class fur_source { public: + fur_source (range_query *q = NULL); + inline range_query *query () { return m_query; } + inline class gori_compute *gori () { return m_gori; }; virtual bool get_operand (irange &r, tree expr); virtual bool get_phi_operand (irange &r, tree expr, edge e); - virtual void register_dependency (tree lhs, tree rhs); - virtual range_query *query (); + virtual relation_kind query_relation (tree op1, tree op2); + virtual void register_relation (gimple *stmt, relation_kind k, tree op1, + tree op2); + virtual void register_relation (edge e, relation_kind k, tree op1, + tree op2); +protected: + range_query *m_query; + gori_compute *m_gori; }; // fur_stmt is the specification for drawing an operand from range_query Q @@ -94,9 +105,8 @@ public: fur_stmt (gimple *s, range_query *q = NULL); virtual bool get_operand (irange &r, tree expr) OVERRIDE; virtual bool get_phi_operand (irange &r, tree expr, edge e) OVERRIDE; - virtual range_query *query () OVERRIDE; + virtual relation_kind query_relation (tree op1, tree op2) OVERRIDE; private: - range_query *m_query; gimple *m_stmt; }; @@ -132,6 +142,8 @@ protected: bool range_of_phi (irange &r, gphi *phi, fur_source &src); void range_of_ssa_name_with_loop_info (irange &, tree, class loop *, gphi *, fur_source &src); + void relation_fold_and_or (irange& lhs_range, gimple *s, fur_source &src); + void postfold_gcond_edges (gcond *s, fur_source &src); }; diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 41bae9c..21e7a6c 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -15128,24 +15128,11 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, bool (*gimple_test_f) (tree), fallback_t fallback, bool allow_ssa) { - bool was_ssa_name_p = TREE_CODE (*expr_p) == SSA_NAME; enum gimplify_status ret = gimplify_expr (expr_p, pre_p, post_p, gimple_test_f, fallback); if (! allow_ssa && TREE_CODE (*expr_p) == SSA_NAME) - { - tree name = *expr_p; - if (was_ssa_name_p) - *expr_p = get_initialized_tmp_var (*expr_p, pre_p, NULL, false); - else - { - /* Avoid the extra copy if possible. */ - *expr_p = create_tmp_reg (TREE_TYPE (name)); - if (!gimple_nop_p (SSA_NAME_DEF_STMT (name))) - gimple_set_lhs (SSA_NAME_DEF_STMT (name), *expr_p); - release_ssa_name (name); - } - } + *expr_p = get_initialized_tmp_var (*expr_p, pre_p, NULL, false); return ret; } diff --git a/gcc/range-op.cc b/gcc/range-op.cc index e805f26..1692a09 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-walk.h" #include "tree-cfg.h" #include "wide-int.h" +#include "value-relation.h" #include "range-op.h" // Return the upper limit for a type. @@ -138,7 +139,8 @@ range_operator::wi_fold (irange &r, tree type, bool range_operator::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel) const { gcc_checking_assert (irange::supports_type_p (type)); if (empty_range_varying (r, type, lh, rh)) @@ -152,6 +154,7 @@ range_operator::fold_range (irange &r, tree type, { wi_fold (r, type, lh.lower_bound (0), lh.upper_bound (0), rh.lower_bound (0), rh.upper_bound (0)); + op1_op2_relation_effect (r, type, lh, rh, rel); return true; } @@ -167,8 +170,12 @@ range_operator::fold_range (irange &r, tree type, wi_fold (tmp, type, lh_lb, lh_ub, rh_lb, rh_ub); r.union_ (tmp); if (r.varying_p ()) - return true; + { + op1_op2_relation_effect (r, type, lh, rh, rel); + return true; + } } + op1_op2_relation_effect (r, type, lh, rh, rel); return true; } @@ -178,7 +185,8 @@ bool range_operator::op1_range (irange &r ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, const irange &lhs ATTRIBUTE_UNUSED, - const irange &op2 ATTRIBUTE_UNUSED) const + const irange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { return false; } @@ -189,11 +197,47 @@ bool range_operator::op2_range (irange &r ATTRIBUTE_UNUSED, tree type ATTRIBUTE_UNUSED, const irange &lhs ATTRIBUTE_UNUSED, - const irange &op1 ATTRIBUTE_UNUSED) const + const irange &op1 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { return false; } +// The default relation routines return VREL_NONE. + +enum tree_code +range_operator::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED, + const irange &op1 ATTRIBUTE_UNUSED, + const irange &op2 ATTRIBUTE_UNUSED) const +{ + return VREL_NONE; +} + +enum tree_code +range_operator::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED, + const irange &op1 ATTRIBUTE_UNUSED, + const irange &op2 ATTRIBUTE_UNUSED) const +{ + return VREL_NONE; +} + +enum tree_code +range_operator::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const +{ + return VREL_NONE; +} + +// Default is no relation affects the LHS. + +bool +range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const irange &op1_range ATTRIBUTE_UNUSED, + const irange &op2_range ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} // Create and return a range from a pair of wide-ints that are known // to have overflowed (or underflowed). @@ -385,27 +429,82 @@ get_bool_state (irange &r, const irange &lhs, tree val_type) return BRS_TRUE; } +// For relation opcodes, first try to see if the supplied relation +// forces a true or false result, and return that. +// Then check for undefined operands. If none of this applies, +// return false. + +static inline bool +relop_early_resolve (irange &r, tree type, const irange &op1, + const irange &op2, relation_kind rel, + relation_kind my_rel) +{ + // If known relation is a complete subset of this relation, always true. + if (relation_union (rel, my_rel) == my_rel) + { + r = range_true (type); + return true; + } + + // If known relation has no subset of this relation, always false. + if (relation_intersect (rel, my_rel) == VREL_EMPTY) + { + r = range_false (type); + return true; + } + + // If either operand is undefined, return VARYING. + if (empty_range_varying (r, type, op1, op2)) + return true; + + return false; +} + class operator_equal : public range_operator { public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &val) const; + const irange &val, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &val) const; + const irange &val, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_equal; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_equal::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 == op2 indicates NE_EXPR. + if (lhs.zero_p ()) + return NE_EXPR; + + // TRUE = op1 == op2 indicates EQ_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return EQ_EXPR; + return VREL_NONE; +} + + bool operator_equal::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, EQ_EXPR)) return true; // We can be sure the values are always equal or not if both ranges @@ -435,7 +534,8 @@ operator_equal::fold_range (irange &r, tree type, bool operator_equal::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -465,32 +565,55 @@ operator_equal::op1_range (irange &r, tree type, bool operator_equal::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel) const { - return operator_equal::op1_range (r, type, lhs, op1); + return operator_equal::op1_range (r, type, lhs, op1, rel); } - class operator_not_equal : public range_operator { public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_not_equal; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_not_equal::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 != op2 indicates EQ_EXPR. + if (lhs.zero_p ()) + return EQ_EXPR; + + // TRUE = op1 != op2 indicates NE_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return NE_EXPR; + return VREL_NONE; +} + bool operator_not_equal::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, NE_EXPR)) return true; // We can be sure the values are always equal or not if both ranges @@ -520,7 +643,8 @@ operator_not_equal::fold_range (irange &r, tree type, bool operator_not_equal::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -551,9 +675,10 @@ operator_not_equal::op1_range (irange &r, tree type, bool operator_not_equal::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel) const { - return operator_not_equal::op1_range (r, type, lhs, op1); + return operator_not_equal::op1_range (r, type, lhs, op1, rel); } // (X < VAL) produces the range of [MIN, VAL - 1]. @@ -607,21 +732,44 @@ class operator_lt : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_lt; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_lt::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 < op2 indicates GE_EXPR. + if (lhs.zero_p ()) + return GE_EXPR; + + // TRUE = op1 < op2 indicates LT_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return LT_EXPR; + return VREL_NONE; +} + bool operator_lt::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, LT_EXPR)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -639,7 +787,8 @@ operator_lt::fold_range (irange &r, tree type, bool operator_lt::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -660,7 +809,8 @@ operator_lt::op1_range (irange &r, tree type, bool operator_lt::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -684,21 +834,44 @@ class operator_le : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_le; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_le::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 <= op2 indicates GT_EXPR. + if (lhs.zero_p ()) + return GT_EXPR; + + // TRUE = op1 <= op2 indicates LE_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return LE_EXPR; + return VREL_NONE; +} + bool operator_le::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, LE_EXPR)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -716,7 +889,8 @@ operator_le::fold_range (irange &r, tree type, bool operator_le::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -737,7 +911,8 @@ operator_le::op1_range (irange &r, tree type, bool operator_le::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -761,20 +936,44 @@ class operator_gt : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_gt; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_gt::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 > op2 indicates LE_EXPR. + if (lhs.zero_p ()) + return LE_EXPR; + + // TRUE = op1 > op2 indicates GT_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return GT_EXPR; + return VREL_NONE; +} + + bool operator_gt::fold_range (irange &r, tree type, - const irange &op1, const irange &op2) const + const irange &op1, const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, GT_EXPR)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -791,7 +990,8 @@ operator_gt::fold_range (irange &r, tree type, bool operator_gt::op1_range (irange &r, tree type, - const irange &lhs, const irange &op2) const + const irange &lhs, const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -812,7 +1012,8 @@ operator_gt::op1_range (irange &r, tree type, bool operator_gt::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -836,21 +1037,44 @@ class operator_ge : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; } op_ge; +// Check if the LHS range indicates a relation between OP1 and OP2. + +enum tree_code +operator_ge::op1_op2_relation (const irange &lhs) const +{ + if (lhs.undefined_p ()) + return VREL_EMPTY; + + // FALSE = op1 >= op2 indicates LT_EXPR. + if (lhs.zero_p ()) + return LT_EXPR; + + // TRUE = op1 >= op2 indicates GE_EXPR. + if (!lhs.contains_p (build_zero_cst (lhs.type ()))) + return GE_EXPR; + return VREL_NONE; +} + bool operator_ge::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel) const { - if (empty_range_varying (r, type, op1, op2)) + if (relop_early_resolve (r, type, op1, op2, rel, GE_EXPR)) return true; signop sign = TYPE_SIGN (op1.type ()); @@ -868,7 +1092,8 @@ operator_ge::fold_range (irange &r, tree type, bool operator_ge::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -889,7 +1114,8 @@ operator_ge::op1_range (irange &r, tree type, bool operator_ge::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -913,17 +1139,99 @@ class operator_plus : public range_operator public: virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, const wide_int &rh_ub) const; + virtual enum tree_code lhs_op1_relation (const irange &lhs, const irange &op1, + const irange &op2) const; + virtual enum tree_code lhs_op2_relation (const irange &lhs, const irange &op1, + const irange &op2) const; } op_plus; +// Check to see if the range of OP2 indicates anything about the relation +// between LHS and OP1. + +enum tree_code +operator_plus::lhs_op1_relation (const irange &lhs, + const irange &op1, + const irange &op2) const +{ + if (lhs.undefined_p () || op1.undefined_p () || op2.undefined_p ()) + return VREL_NONE; + + tree type = lhs.type (); + unsigned prec = TYPE_PRECISION (type); + wi::overflow_type ovf1, ovf2; + signop sign = TYPE_SIGN (type); + + // LHS = OP1 + 0 indicates LHS == OP1. + if (op2.zero_p ()) + return EQ_EXPR; + + if (TYPE_OVERFLOW_WRAPS (type)) + { + wi::add (op1.lower_bound (), op2.lower_bound (), sign, &ovf1); + wi::add (op1.upper_bound (), op2.upper_bound (), sign, &ovf2); + } + else + ovf1 = ovf2 = wi::OVF_NONE; + + // Never wrapping additions. + if (!ovf1 && !ovf2) + { + // Positive op2 means lhs > op1. + if (wi::gt_p (op2.lower_bound (), wi::zero (prec), sign)) + return GT_EXPR; + if (wi::ge_p (op2.lower_bound (), wi::zero (prec), sign)) + return GE_EXPR; + + // Negative op2 means lhs < op1. + if (wi::lt_p (op2.upper_bound (), wi::zero (prec), sign)) + return LT_EXPR; + if (wi::le_p (op2.upper_bound (), wi::zero (prec), sign)) + return LE_EXPR; + } + // Always wrapping additions. + else if (ovf1 && ovf1 == ovf2) + { + // Positive op2 means lhs < op1. + if (wi::gt_p (op2.lower_bound (), wi::zero (prec), sign)) + return LT_EXPR; + if (wi::ge_p (op2.lower_bound (), wi::zero (prec), sign)) + return LE_EXPR; + + // Negative op2 means lhs > op1. + if (wi::lt_p (op2.upper_bound (), wi::zero (prec), sign)) + return GT_EXPR; + if (wi::le_p (op2.upper_bound (), wi::zero (prec), sign)) + return GE_EXPR; + } + + // If op2 does not contain 0, then LHS and OP1 can never be equal. + if (!range_includes_zero_p (&op2)) + return NE_EXPR; + + return VREL_NONE; +} + +// PLUS is symmetrical, so we can simply call lhs_op1_relation with reversed +// operands. + +enum tree_code +operator_plus::lhs_op2_relation (const irange &lhs, const irange &op1, + const irange &op2) const +{ + return lhs_op1_relation (lhs, op2, op1); +} + void operator_plus::wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -939,7 +1247,8 @@ operator_plus::wi_fold (irange &r, tree type, bool operator_plus::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2); } @@ -947,7 +1256,8 @@ operator_plus::op1_range (irange &r, tree type, bool operator_plus::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1); } @@ -958,15 +1268,22 @@ class operator_minus : public range_operator public: virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, const wide_int &rh_ub) const; + virtual bool op1_op2_relation_effect (irange &lhs_range, + tree type, + const irange &op1_range, + const irange &op2_range, + relation_kind rel) const; } op_minus; void @@ -981,10 +1298,50 @@ operator_minus::wi_fold (irange &r, tree type, value_range_with_overflow (r, type, new_lb, new_ub, ov_lb, ov_ub); } +// Check to see if the relation REL between OP1 and OP2 has any effect on the +// LHS of the epxression. If so, apply it to LHS_RANGE. + +bool +operator_minus::op1_op2_relation_effect (irange &lhs_range, tree type, + const irange &op1_range ATTRIBUTE_UNUSED, + const irange &op2_range ATTRIBUTE_UNUSED, + relation_kind rel) const +{ + if (rel == VREL_NONE) + return false; + + int_range<2> rel_range; + unsigned prec = TYPE_PRECISION (type); + signop sgn = TYPE_SIGN (type); + + switch (rel) + { + // op1 > op2, op1 - op2 can be restricted to [1, max] + case GT_EXPR: + rel_range = int_range<2> (type, wi::one (prec), + wi::max_value (prec, sgn)); + break; + // op1 >= op2, op1 - op2 can be restricted to [0, max] + case GE_EXPR: + rel_range = int_range<2> (type, wi::zero (prec), + wi::max_value (prec, sgn)); + break; + // op1 == op2, op1 - op2 can be restricted to [0, 0] + case EQ_EXPR: + rel_range = int_range<2> (type, wi::zero (prec), wi::zero (prec)); + break; + default: + return false; + } + lhs_range.intersect (rel_range); + return true; +} + bool operator_minus::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2); } @@ -992,7 +1349,8 @@ operator_minus::op1_range (irange &r, tree type, bool operator_minus::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return fold_range (r, type, op1, lhs); } @@ -1127,15 +1485,18 @@ public: const wide_int &w0, const wide_int &w1) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const; } op_mult; bool operator_mult::op1_range (irange &r, tree type, - const irange &lhs, const irange &op2) const + const irange &lhs, const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { tree offset; @@ -1153,9 +1514,10 @@ operator_mult::op1_range (irange &r, tree type, bool operator_mult::op2_range (irange &r, tree type, - const irange &lhs, const irange &op1) const + const irange &lhs, const irange &op1, + relation_kind rel) const { - return operator_mult::op1_range (r, type, lhs, op1); + return operator_mult::op1_range (r, type, lhs, op1, rel); } bool @@ -1391,14 +1753,16 @@ public: operator_exact_divide () : operator_div (TRUNC_DIV_EXPR) { } virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; } op_exact_div; bool operator_exact_divide::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { tree offset; // [2, 4] = op1 / [3,3] since its exact divide, no need to worry about @@ -1419,10 +1783,12 @@ class operator_lshift : public cross_product_operator public: virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -1438,7 +1804,8 @@ class operator_rshift : public cross_product_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -1450,14 +1817,16 @@ public: const wide_int &w1) const; virtual bool op1_range (irange &, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_rshift; bool operator_lshift::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { int_range_max shift_range; if (!get_shift_range (shift_range, type, op2)) @@ -1572,7 +1941,8 @@ bool operator_lshift::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { tree shift_amount; if (op2.singleton_p (&shift_amount)) @@ -1632,7 +2002,8 @@ bool operator_rshift::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { tree shift; if (op2.singleton_p (&shift)) @@ -1708,7 +2079,8 @@ operator_rshift::wi_op_overflows (wide_int &res, bool operator_rshift::fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { int_range_max shift; if (!get_shift_range (shift, type, op2)) @@ -1737,10 +2109,16 @@ class operator_cast: public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code lhs_op1_relation (const irange &lhs, + const irange &op1, + const irange &op2) const; + private: bool truncating_cast_p (const irange &inner, const irange &outer) const; bool inside_domain_p (const wide_int &min, const wide_int &max, @@ -1749,6 +2127,27 @@ private: const irange &outer) const; } op_convert; +// Determine if there is a relationship between LHS and OP1. + +enum tree_code +operator_cast::lhs_op1_relation (const irange &lhs, + const irange &op1, + const irange &op2 ATTRIBUTE_UNUSED) const +{ + if (op1.undefined_p ()) + return VREL_NONE; + // We can't make larger types equivalent to smaller types because we can + // miss sign extensions in a chain of casts. + // u32 = 0xfffff + // s32 = (s32) u32 + // s64 = (s64) s32 + // we cant simply "convert" s64 = (s64)u32 or we get positive 0xffff + // value instead of sign extended negative value. + if (TYPE_PRECISION (lhs.type ()) == TYPE_PRECISION (op1.type ())) + return EQ_EXPR; + return VREL_NONE; +} + // Return TRUE if casting from INNER to OUTER is a truncating cast. inline bool @@ -1818,7 +2217,8 @@ operator_cast::fold_pair (irange &r, unsigned index, bool operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, const irange &inner, - const irange &outer) const + const irange &outer, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, inner, outer)) return true; @@ -1844,7 +2244,8 @@ operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, bool operator_cast::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { tree lhs_type = lhs.type (); gcc_checking_assert (types_compatible_p (op2.type(), type)); @@ -1954,20 +2355,24 @@ class operator_logical_and : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; } op_logical_and; bool operator_logical_and::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -1989,7 +2394,8 @@ operator_logical_and::fold_range (irange &r, tree type, bool operator_logical_and::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2 ATTRIBUTE_UNUSED) const + const irange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -2010,7 +2416,8 @@ operator_logical_and::op1_range (irange &r, tree type, bool operator_logical_and::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_logical_and::op1_range (r, type, lhs, op1); } @@ -2021,13 +2428,16 @@ class operator_bitwise_and : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -2106,7 +2516,8 @@ operator_bitwise_and::remove_impossible_ranges (irange &r, bool operator_bitwise_and::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (range_operator::fold_range (r, type, lh, rh)) { @@ -2397,7 +2808,8 @@ operator_bitwise_and::simple_op1_range_solver (irange &r, tree type, bool operator_bitwise_and::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { if (types_compatible_p (type, boolean_type_node)) return op_logical_and.op1_range (r, type, lhs, op2); @@ -2420,7 +2832,8 @@ operator_bitwise_and::op1_range (irange &r, tree type, bool operator_bitwise_and::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_bitwise_and::op1_range (r, type, lhs, op1); } @@ -2431,19 +2844,23 @@ class operator_logical_or : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; } op_logical_or; bool operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -2456,7 +2873,8 @@ operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, bool operator_logical_or::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2 ATTRIBUTE_UNUSED) const + const irange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { switch (get_bool_state (r, lhs, type)) { @@ -2477,7 +2895,8 @@ operator_logical_or::op1_range (irange &r, tree type, bool operator_logical_or::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_logical_or::op1_range (r, type, lhs, op1); } @@ -2488,10 +2907,12 @@ class operator_bitwise_or : public range_operator public: virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel= VREL_NONE) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, @@ -2553,7 +2974,8 @@ operator_bitwise_or::wi_fold (irange &r, tree type, bool operator_bitwise_or::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { // If this is really a logical wi_fold, call that. if (types_compatible_p (type, boolean_type_node)) @@ -2572,7 +2994,8 @@ operator_bitwise_or::op1_range (irange &r, tree type, bool operator_bitwise_or::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_bitwise_or::op1_range (r, type, lhs, op1); } @@ -2588,10 +3011,12 @@ public: const wide_int &rh_ub) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; } op_bitwise_xor; void @@ -2628,7 +3053,8 @@ operator_bitwise_xor::wi_fold (irange &r, tree type, bool operator_bitwise_xor::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { if (lhs.undefined_p () || lhs.varying_p ()) { @@ -2662,7 +3088,8 @@ operator_bitwise_xor::op1_range (irange &r, tree type, bool operator_bitwise_xor::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_bitwise_xor::op1_range (r, type, lhs, op1); } @@ -2677,10 +3104,12 @@ public: const wide_int &rh_ub) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const; } op_trunc_mod; void @@ -2730,7 +3159,8 @@ operator_trunc_mod::wi_fold (irange &r, tree type, bool operator_trunc_mod::op1_range (irange &r, tree type, const irange &lhs, - const irange &) const + const irange &, + relation_kind rel ATTRIBUTE_UNUSED) const { // PR 91029. signop sign = TYPE_SIGN (type); @@ -2753,7 +3183,8 @@ operator_trunc_mod::op1_range (irange &r, tree type, bool operator_trunc_mod::op2_range (irange &r, tree type, const irange &lhs, - const irange &) const + const irange &, + relation_kind rel ATTRIBUTE_UNUSED) const { // PR 91029. signop sign = TYPE_SIGN (type); @@ -2792,10 +3223,12 @@ class operator_logical_not : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_logical_not; // Folding a logical NOT, oddly enough, involves doing nothing on the @@ -2815,7 +3248,8 @@ public: bool operator_logical_not::fold_range (irange &r, tree type, const irange &lh, - const irange &rh ATTRIBUTE_UNUSED) const + const irange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -2831,7 +3265,8 @@ bool operator_logical_not::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { // Logical NOT is involutary...do it again. return fold_range (r, type, lhs, op2); @@ -2843,16 +3278,19 @@ class operator_bitwise_not : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_bitwise_not; bool operator_bitwise_not::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -2870,7 +3308,8 @@ operator_bitwise_not::fold_range (irange &r, tree type, bool operator_bitwise_not::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { if (types_compatible_p (type, boolean_type_node)) return op_logical_not.op1_range (r, type, lhs, op2); @@ -2885,13 +3324,15 @@ class operator_cst : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_integer_cst; bool operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, const irange &lh, - const irange &rh ATTRIBUTE_UNUSED) const + const irange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { r = lh; return true; @@ -2903,16 +3344,35 @@ class operator_identity : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; + virtual enum tree_code lhs_op1_relation (const irange &lhs, + const irange &op1, + const irange &op2) const; } op_identity; +// Determine if there is a relationship between LHS and OP1. + +enum tree_code +operator_identity::lhs_op1_relation (const irange &lhs, + const irange &op1 ATTRIBUTE_UNUSED, + const irange &op2 ATTRIBUTE_UNUSED) const +{ + if (lhs.undefined_p ()) + return VREL_NONE; + // Simply a copy, so they are equivalent. + return EQ_EXPR; +} + bool operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, const irange &lh, - const irange &rh ATTRIBUTE_UNUSED) const + const irange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { r = lh; return true; @@ -2921,7 +3381,8 @@ operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED, bool operator_identity::op1_range (irange &r, tree type ATTRIBUTE_UNUSED, const irange &lhs, - const irange &op2 ATTRIBUTE_UNUSED) const + const irange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { r = lhs; return true; @@ -2933,13 +3394,15 @@ class operator_unknown : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_unknown; bool operator_unknown::fold_range (irange &r, tree type, const irange &lh ATTRIBUTE_UNUSED, - const irange &rh ATTRIBUTE_UNUSED) const + const irange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { r.set_varying (type); return true; @@ -2956,7 +3419,8 @@ class operator_abs : public range_operator const wide_int &rh_ub) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const; } op_abs; void @@ -3036,7 +3500,8 @@ operator_abs::wi_fold (irange &r, tree type, bool operator_abs::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lhs, op2)) return true; @@ -3108,16 +3573,19 @@ class operator_negate : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_negate; bool operator_negate::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -3130,7 +3598,8 @@ operator_negate::fold_range (irange &r, tree type, bool operator_negate::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { // NEGATE is involutory. return fold_range (r, type, lhs, op2); @@ -3142,16 +3611,19 @@ class operator_addr_expr : public range_operator public: virtual bool fold_range (irange &r, tree type, const irange &op1, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; } op_addr; bool operator_addr_expr::fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const + const irange &rh, + relation_kind rel ATTRIBUTE_UNUSED) const { if (empty_range_varying (r, type, lh, rh)) return true; @@ -3169,7 +3641,8 @@ operator_addr_expr::fold_range (irange &r, tree type, bool operator_addr_expr::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const + const irange &op2, + relation_kind rel ATTRIBUTE_UNUSED) const { return operator_addr_expr::fold_range (r, type, lhs, op2); } @@ -3288,10 +3761,12 @@ class pointer_or_operator : public range_operator public: virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; virtual void wi_fold (irange &r, tree type, const wide_int &lh_lb, const wide_int &lh_ub, const wide_int &rh_lb, const wide_int &rh_ub) const; @@ -3300,7 +3775,8 @@ public: bool pointer_or_operator::op1_range (irange &r, tree type, const irange &lhs, - const irange &op2 ATTRIBUTE_UNUSED) const + const irange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const { if (lhs.zero_p ()) { @@ -3315,7 +3791,8 @@ pointer_or_operator::op1_range (irange &r, tree type, bool pointer_or_operator::op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const + const irange &op1, + relation_kind rel ATTRIBUTE_UNUSED) const { return pointer_or_operator::op1_range (r, type, lhs, op1); } @@ -3767,6 +4244,30 @@ range_op_bitwise_and_tests () ASSERT_FALSE (res.contains_p (INT (0))); } +static void +range_relational_tests () +{ + int_range<2> lhs (unsigned_char_type_node); + int_range<2> op1 (UCHAR (8), UCHAR (10)); + int_range<2> op2 (UCHAR (20), UCHAR (20)); + + // Never wrapping additions mean LHS > OP1. + tree_code code = op_plus.lhs_op1_relation (lhs, op1, op2); + ASSERT_TRUE (code == GT_EXPR); + + // Most wrapping additions mean nothing... + op1 = int_range<2> (UCHAR (8), UCHAR (10)); + op2 = int_range<2> (UCHAR (0), UCHAR (255)); + code = op_plus.lhs_op1_relation (lhs, op1, op2); + ASSERT_TRUE (code == VREL_NONE); + + // However, always wrapping additions mean LHS < OP1. + op1 = int_range<2> (UCHAR (1), UCHAR (255)); + op2 = int_range<2> (UCHAR (255), UCHAR (255)); + code = op_plus.lhs_op1_relation (lhs, op1, op2); + ASSERT_TRUE (code == LT_EXPR); +} + void range_op_tests () { @@ -3774,6 +4275,7 @@ range_op_tests () range_op_lshift_tests (); range_op_bitwise_and_tests (); range_op_cast_tests (); + range_relational_tests (); } } // namespace selftest diff --git a/gcc/range-op.h b/gcc/range-op.h index d3d4408..2b5db64 100644 --- a/gcc/range-op.h +++ b/gcc/range-op.h @@ -52,7 +52,8 @@ public: // Perform an operation between 2 ranges and return it. virtual bool fold_range (irange &r, tree type, const irange &lh, - const irange &rh) const; + const irange &rh, + relation_kind rel = VREL_NONE) const; // Return the range for op[12] in the general case. LHS is the range for // the LHS of the expression, OP[12]is the range for the other @@ -67,11 +68,23 @@ public: // is re-formed as R = [LHS] - OP2. virtual bool op1_range (irange &r, tree type, const irange &lhs, - const irange &op2) const; + const irange &op2, + relation_kind rel = VREL_NONE) const; virtual bool op2_range (irange &r, tree type, const irange &lhs, - const irange &op1) const; + const irange &op1, + relation_kind rel = VREL_NONE) const; + // The following routines are used to represent relations between the + // various operations. If the caller knows where the symbolics are, + // it can query for relationships between them given known ranges. + virtual enum tree_code lhs_op1_relation (const irange &lhs, + const irange &op1, + const irange &op2) const; + virtual enum tree_code lhs_op2_relation (const irange &lhs, + const irange &op1, + const irange &op2) const; + virtual enum tree_code op1_op2_relation (const irange &lhs) const; protected: // Perform an integral operation between 2 sub-ranges and return it. virtual void wi_fold (irange &r, tree type, @@ -79,6 +92,11 @@ protected: const wide_int &lh_ub, const wide_int &rh_lb, const wide_int &rh_ub) const; + // Side effect of relation for generic fold_range clients. + virtual bool op1_op2_relation_effect (irange &lhs_range, tree type, + const irange &op1_range, + const irange &op2_range, + relation_kind rel) const; }; extern range_operator *range_op_handler (enum tree_code code, tree type); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3b11dcf..b2de840 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,90 @@ +2021-06-22 Sandra Loosemore <sandra@codesourcery.com> + Tobias Burnus <tobias@codesourcery.com> + + PR fortran/93524 + * gfortran.dg/pr93524.c: New. + * gfortran.dg/pr93524.f90: New. + +2021-06-22 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/101143 + * gcc.dg/analyzer/pr101143.c: New test. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR middle-end/101156 + * gcc.dg/pr101156.c: New testcase. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/101159 + * gcc.c-torture/compile/pr101159.c: New test. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/101160 + * g++.target/i386/empty-class1.C: Require lp64 effective target + instead of x86_64-*-*. + * g++.target/i386/empty-class2.C: New test. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + Andrew Pinski <apinski@marvell.com> + + PR tree-optimization/101162 + * g++.dg/opt/pr101162.C: New test. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101151 + * gcc.dg/torture/pr101151.c: New testcase. + +2021-06-22 Kito Cheng <kito.cheng@sifive.com> + + * g++.dg/modules/omp-1_a.C: Check pthread is available for + dg-module-cmi. + * g++.dg/modules/omp-2_a.C: Ditto. + +2021-06-22 Richard Biener <rguenther@suse.de> + + PR tree-optimization/101158 + * gfortran.dg/pr101158.f90: New testcase. + +2021-06-22 Jakub Jelinek <jakub@redhat.com> + + PR target/11877 + * gcc.target/i386/pr11877-2.c: New test. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR tree-optimization/97770 + * gcc.target/i386/avx512bitalg-pr97770-1.c: Remove xfail. + * gcc.target/i386/avx512vpopcntdq-pr97770-1.c: Remove xfail. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR target/100267 + * gcc.target/i386/avx512bw-pr100267-1.c: New test. + * gcc.target/i386/avx512bw-pr100267-b-2.c: New test. + * gcc.target/i386/avx512bw-pr100267-d-2.c: New test. + * gcc.target/i386/avx512bw-pr100267-q-2.c: New test. + * gcc.target/i386/avx512bw-pr100267-w-2.c: New test. + * gcc.target/i386/avx512f-pr100267-1.c: New test. + * gcc.target/i386/avx512f-pr100267-pd-2.c: New test. + * gcc.target/i386/avx512f-pr100267-ps-2.c: New test. + * gcc.target/i386/avx512vl-pr100267-1.c: New test. + * gcc.target/i386/avx512vl-pr100267-pd-2.c: New test. + * gcc.target/i386/avx512vl-pr100267-ps-2.c: New test. + * gcc.target/i386/avx512vlbw-pr100267-1.c: New test. + * gcc.target/i386/avx512vlbw-pr100267-b-2.c: New test. + * gcc.target/i386/avx512vlbw-pr100267-d-2.c: New test. + * gcc.target/i386/avx512vlbw-pr100267-q-2.c: New test. + * gcc.target/i386/avx512vlbw-pr100267-w-2.c: New test. + +2021-06-22 liuhongt <hongtao.liu@intel.com> + + PR target/100310 + * gcc.target/i386/pr100310.c: New test. + 2021-06-21 Jason Merrill <jason@redhat.com> PR target/88529 diff --git a/gcc/testsuite/g++.dg/modules/omp-1_a.C b/gcc/testsuite/g++.dg/modules/omp-1_a.C index 94e1171..7ddb776 100644 --- a/gcc/testsuite/g++.dg/modules/omp-1_a.C +++ b/gcc/testsuite/g++.dg/modules/omp-1_a.C @@ -2,7 +2,7 @@ // { dg-require-effective-target pthread } export module foo; -// { dg-module-cmi foo } +// { dg-module-cmi foo { target pthread } } export inline void frob (unsigned (&ary)[64]) { diff --git a/gcc/testsuite/g++.dg/modules/omp-2_a.C b/gcc/testsuite/g++.dg/modules/omp-2_a.C index b0d4bbc..e030ac7 100644 --- a/gcc/testsuite/g++.dg/modules/omp-2_a.C +++ b/gcc/testsuite/g++.dg/modules/omp-2_a.C @@ -2,7 +2,7 @@ // { dg-require-effective-target pthread } export module foo; -// { dg-module-cmi foo } +// { dg-module-cmi foo { target pthread } } // The OpenMPness doesn't escape to the interface. export void frob (unsigned (&ary)[64]) diff --git a/gcc/testsuite/g++.dg/opt/pr101162.C b/gcc/testsuite/g++.dg/opt/pr101162.C new file mode 100644 index 0000000..4032189 --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr101162.C @@ -0,0 +1,21 @@ +// PR tree-optimization/101162 +// { dg-do compile } +// { dg-options "-O2" } + +struct A { int i1, i2, i3, i4, i5, i6; }; + +int A::* +foo (int i) +{ + switch (i) + { + case 1: return &A::i1; + case 2: return &A::i2; + case 3: return &A::i3; + case 4: return &A::i4; + case 5: return &A::i5; + case 6: return &A::i6; + } + + return 0; +} diff --git a/gcc/testsuite/g++.target/i386/empty-class1.C b/gcc/testsuite/g++.target/i386/empty-class1.C index c199277..96a1fad 100644 --- a/gcc/testsuite/g++.target/i386/empty-class1.C +++ b/gcc/testsuite/g++.target/i386/empty-class1.C @@ -1,5 +1,5 @@ // PR target/88529 -// { dg-do compile { target { c++11 && x86_64-*-* } } } +// { dg-do compile { target { c++11 && lp64 } } } // { dg-additional-options -fdump-rtl-expand } // { dg-final { scan-rtl-dump-not "set" "expand" } } // The x86_64 psABI says that f() doesn't put the return value anywhere. diff --git a/gcc/testsuite/g++.target/i386/empty-class2.C b/gcc/testsuite/g++.target/i386/empty-class2.C new file mode 100644 index 0000000..b9317c5 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/empty-class2.C @@ -0,0 +1,20 @@ +// PR middle-end/101160 +// Test passing aligned empty aggregate +// { dg-do compile } +// { dg-options "-O2" } +// { dg-additional-options "-Wno-psabi" { target { { i?86-*-* x86_64-*-* } && ilp32 } } } + +struct S { union {} a; } __attribute__((aligned)); + +S +foo (S arg) +{ + return arg; +} + +void +bar (void) +{ + S arg; + foo (arg); +} diff --git a/gcc/testsuite/gcc.c-torture/compile/pr101159.c b/gcc/testsuite/gcc.c-torture/compile/pr101159.c new file mode 100644 index 0000000..81b2d2c --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/compile/pr101159.c @@ -0,0 +1,10 @@ +/* PR tree-optimization/101159 */ + +unsigned long a; +long b; + +void +foo (void) +{ + a += __builtin_popcountl (b); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pr101143.c b/gcc/testsuite/gcc.dg/analyzer/pr101143.c new file mode 100644 index 0000000..bcc0974 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr101143.c @@ -0,0 +1,18 @@ +/* { dg-additional-options "-Wno-builtin-declaration-mismatch" } */ + +extern void *malloc (unsigned int); +extern void *alloca (unsigned int); +extern void unknown_fn (void *); + +void * +test_malloc (void) +{ + return malloc (sizeof (int)); +} + +void * +test_alloca (void) +{ + void *p = alloca (sizeof (int)); + unknown_fn (p); +} diff --git a/gcc/testsuite/gcc.dg/pr101156.c b/gcc/testsuite/gcc.dg/pr101156.c new file mode 100644 index 0000000..5c25bd7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr101156.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-fchecking" } */ + +struct S { int i; }; +void baz(struct S *p) +{ + __builtin_setjmp(p--); +} diff --git a/gcc/testsuite/gcc.dg/torture/pr101151.c b/gcc/testsuite/gcc.dg/torture/pr101151.c new file mode 100644 index 0000000..15c9a7b --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr101151.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ + +int a, *b = &a, c, d; +int main() { + *b; + if (a) { + L1: + a = 0; + L2: + if (d) { + while (b) + ; + goto L1; + } + } + if (c) + goto L2; + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr11877-2.c b/gcc/testsuite/gcc.target/i386/pr11877-2.c new file mode 100644 index 0000000..4782dd2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr11877-2.c @@ -0,0 +1,26 @@ +/* PR target/11877 */ +/* { dg-do compile } */ +/* { dg-options "-Os" } */ + +void +foo (int *p) +{ + p[0] = 0; + p[7] = 0; + p[23] = 0; + p[41] = 0; + p[48] = 0; + p[59] = 0; + p[69] = 0; + p[78] = 0; + p[83] = 0; + p[89] = 0; + p[98] = 0; + p[121] = 0; + p[132] = 0; + p[143] = 0; + p[154] = 0; +} + +/* { dg-final { scan-assembler-times "xorl\[ \t\]" 1 } } */ +/* { dg-final { scan-assembler-not "\\\$0," } } */ diff --git a/gcc/testsuite/gfortran.dg/pr101158.f90 b/gcc/testsuite/gfortran.dg/pr101158.f90 new file mode 100644 index 0000000..9a4d9a2 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr101158.f90 @@ -0,0 +1,25 @@ +! { dg-do compile } +! { dg-options "-O1 -ftree-slp-vectorize -fwrapv" } +! { dg-additional-options "-march=armv8-a+sve" { target aarch64-*-* } } + +subroutine sprpl5 (left) + implicit none + + integer :: left + integer :: avail1, avail2, delx1, delx2, i2, ic + + ic = left + delx1 = ic / 2 + delx2 = delx1 + 1 + i2 = ic + delx2 + avail1 = i2 + avail2 = 1 + + do delx1 = 1, 2 + ic = left + nint (real (left) / 2) + if (ic .ge. avail1) avail1 = ic + 1 + + i2 = ic + delx2 + if (i2 .le. avail2) avail2 = i2 + 1 + end do +end subroutine sprpl5 diff --git a/gcc/testsuite/gfortran.dg/pr93524.c b/gcc/testsuite/gfortran.dg/pr93524.c new file mode 100644 index 0000000..24e5e09 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr93524.c @@ -0,0 +1,33 @@ +/* Test the fix for PR93524, in which CFI_allocate was computing + sm incorrectly for dimensions > 2. */ + +#include <stdlib.h> // For size_t +#include "../../../libgfortran/ISO_Fortran_binding.h" + +void my_fortran_sub_1 (CFI_cdesc_t *dv); +void my_fortran_sub_2 (CFI_cdesc_t *dv); + +int main () +{ + CFI_CDESC_T (3) a; + CFI_cdesc_t *dv = (CFI_cdesc_t *) &a; + // dv, base_addr, attribute, type, elem_len, rank, extents + CFI_establish (dv, NULL, CFI_attribute_allocatable, CFI_type_float, 0, 3, NULL); + + if (dv->base_addr != NULL) + return 1; // shall not be allocated + + CFI_index_t lower_bounds[] = {-10, 0, 3}; + CFI_index_t upper_bounds[] = {10, 5, 10}; + size_t elem_len = 0; // only needed for strings + if (CFI_SUCCESS != CFI_allocate (dv, lower_bounds, upper_bounds, elem_len)) + return 2; + + if (!CFI_is_contiguous (dv)) + return 2; // allocatables shall be contiguous,unless a strided section is used + + my_fortran_sub_1 (dv); + my_fortran_sub_2 (dv); + CFI_deallocate (dv); + return 0; +} diff --git a/gcc/testsuite/gfortran.dg/pr93524.f90 b/gcc/testsuite/gfortran.dg/pr93524.f90 new file mode 100644 index 0000000..0cebc8f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr93524.f90 @@ -0,0 +1,17 @@ +! { dg-additional-sources pr93524.c } +! { dg-do run } +! +! Test the fix for PR93524. The main program is in pr93524.c. + +subroutine my_fortran_sub_1 (A) bind(C) + real :: A(:, :, :) + if (any (lbound(A) /= 1)) stop 1 + if (any (ubound(A) /= [21,6,8])) stop 2 + if (.not. is_contiguous (A)) stop 3 +end +subroutine my_fortran_sub_2 (A) bind(C) + real, ALLOCATABLE :: A(:, :, :) + if (any (lbound(A) /= [-10,0,3])) stop 1 + if (any (ubound(A) /= [10,5,10])) stop 2 + if (.not. is_contiguous (A)) stop 3 +end subroutine my_fortran_sub_2 diff --git a/gcc/tree-data-ref.c b/gcc/tree-data-ref.c index 6f3352f..b6abd8b 100644 --- a/gcc/tree-data-ref.c +++ b/gcc/tree-data-ref.c @@ -97,8 +97,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-eh.h" #include "ssa.h" #include "internal-fn.h" -#include "range-op.h" #include "vr-values.h" +#include "range-op.h" static struct datadep_stats { diff --git a/gcc/tree-ssa-sink.c b/gcc/tree-ssa-sink.c index d252cbb..92f444e 100644 --- a/gcc/tree-ssa-sink.c +++ b/gcc/tree-ssa-sink.c @@ -398,7 +398,14 @@ statement_sink_location (gimple *stmt, basic_block frombb, && dominated_by_p (CDI_POST_DOMINATORS, commondom, bb) /* If the blocks are possibly within the same irreducible cycle the above check breaks down. */ - && !(bb->flags & commondom->flags & BB_IRREDUCIBLE_LOOP)) + && !((bb->flags & commondom->flags & BB_IRREDUCIBLE_LOOP) + && bb->loop_father == commondom->loop_father) + && !((commondom->flags & BB_IRREDUCIBLE_LOOP) + && flow_loop_nested_p (commondom->loop_father, + bb->loop_father)) + && !((bb->flags & BB_IRREDUCIBLE_LOOP) + && flow_loop_nested_p (bb->loop_father, + commondom->loop_father))) continue; bb = EDGE_PRED (bb, PHI_ARG_INDEX_FROM_USE (use_p))->src; } diff --git a/gcc/tree-vect-patterns.c b/gcc/tree-vect-patterns.c index 5972705..b2e7fc2 100644 --- a/gcc/tree-vect-patterns.c +++ b/gcc/tree-vect-patterns.c @@ -1300,7 +1300,7 @@ vect_recog_widen_minus_pattern (vec_info *vinfo, stmt_vec_info last_stmt_info, TYPE1 B; UTYPE2 temp_in; TYPE3 temp_out; - temp_in = (TYPE2)A; + temp_in = (UTYPE2)A; temp_out = __builtin_popcount{,l,ll} (temp_in); B = (TYPE1) temp_out; @@ -1372,8 +1372,8 @@ vect_recog_popcount_pattern (vec_info *vinfo, if (!rhs_origin) return NULL; - /* Input and outout of .POPCOUNT should be same-precision integer. - Also A should be unsigned or same presion as temp_in, + /* Input and output of .POPCOUNT should be same-precision integer. + Also A should be unsigned or same precision as temp_in, otherwise there would be sign_extend from A to temp_in. */ if (TYPE_PRECISION (unprom_diff.type) != TYPE_PRECISION (lhs_type) || (!TYPE_UNSIGNED (unprom_diff.type) @@ -1384,10 +1384,10 @@ vect_recog_popcount_pattern (vec_info *vinfo, vect_pattern_detected ("vec_regcog_popcount_pattern", popcount_stmt); vec_type = get_vectype_for_scalar_type (vinfo, lhs_type); - /* Do it only the backend existed popcount<vector_mode>2. */ - if (!direct_internal_fn_supported_p (IFN_POPCOUNT, - vec_type, - OPTIMIZE_FOR_SPEED)) + /* Do it only if the backend has popcount<vector_mode>2 pattern. */ + if (!vec_type + || !direct_internal_fn_supported_p (IFN_POPCOUNT, vec_type, + OPTIMIZE_FOR_SPEED)) return NULL; /* Create B = .POPCOUNT (A). */ diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c index a32f86b..6c98acb 100644 --- a/gcc/tree-vect-slp.c +++ b/gcc/tree-vect-slp.c @@ -1177,21 +1177,6 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap, continue; } - if (need_same_oprnds) - { - tree other_op1 = (call_stmt - ? gimple_call_arg (call_stmt, 1) - : gimple_assign_rhs2 (stmt)); - if (!operand_equal_p (first_op1, other_op1, 0)) - { - if (dump_enabled_p ()) - dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, - "Build SLP failed: different shift " - "arguments in %G", stmt); - /* Mismatch. */ - continue; - } - } if (!load_p && first_stmt_code == BIT_FIELD_REF && (TREE_OPERAND (gimple_assign_rhs1 (first_stmt_info->stmt), 0) @@ -1231,6 +1216,22 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap, continue; } + if (need_same_oprnds) + { + tree other_op1 = (call_stmt + ? gimple_call_arg (call_stmt, 1) + : gimple_assign_rhs2 (stmt)); + if (!operand_equal_p (first_op1, other_op1, 0)) + { + if (dump_enabled_p ()) + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, + "Build SLP failed: different shift " + "arguments in %G", stmt); + /* Mismatch. */ + continue; + } + } + if (!types_compatible_p (vectype, *node_vectype)) { if (dump_enabled_p ()) @@ -1963,15 +1964,15 @@ vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node, if (dt == vect_constant_def || dt == vect_external_def) { - /* We can always build those. Might want to sort last - or defer building. */ - vec<tree> ops; - ops.create (group_size); - for (lane = 0; lane < group_size; ++lane) - ops.quick_push (chains[lane][n].op); - slp_tree child = vect_create_new_slp_node (ops); - SLP_TREE_DEF_TYPE (child) = dt; - children.safe_push (child); + /* We can always build those. Might want to sort last + or defer building. */ + vec<tree> ops; + ops.create (group_size); + for (lane = 0; lane < group_size; ++lane) + ops.quick_push (chains[lane][n].op); + slp_tree child = vect_create_new_slp_node (ops); + SLP_TREE_DEF_TYPE (child) = dt; + children.safe_push (child); } else if (dt != vect_internal_def) { @@ -2036,9 +2037,10 @@ vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node, dump_printf_loc (MSG_NOTE, vect_location, "failed to match up op %d\n", n); op_stmts.release (); - matches[lane] = false; if (lane != group_size - 1) matches[0] = false; + else + matches[lane] = false; goto out; } if (dump_enabled_p ()) diff --git a/gcc/value-query.cc b/gcc/value-query.cc index 93609f3..17dfdb1 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -174,6 +174,7 @@ range_query::get_value_range (const_tree expr, gimple *stmt) range_query::range_query () { equiv_alloc = new equiv_allocator; + m_oracle = NULL; } range_query::~range_query () @@ -452,3 +453,52 @@ global_range_query::range_of_expr (irange &r, tree expr, gimple *stmt) return true; } + +// Return any known relation between SSA1 and SSA2 before stmt S is executed. +// If GET_RANGE is true, query the range of both operands first to ensure +// the defintions have been processed and any relations have be created. + +relation_kind +range_query::query_relation (gimple *s, tree ssa1, tree ssa2, bool get_range) +{ + int_range_max tmp; + if (!m_oracle || TREE_CODE (ssa1) != SSA_NAME || TREE_CODE (ssa2) != SSA_NAME) + return VREL_NONE; + + // Ensure ssa1 and ssa2 have both been evaluated. + if (get_range) + { + range_of_expr (tmp, ssa1, s); + range_of_expr (tmp, ssa2, s); + } + return m_oracle->query_relation (gimple_bb (s), ssa1, ssa2); +} + +// Return any known relation between SSA1 and SSA2 on edge E. +// If GET_RANGE is true, query the range of both operands first to ensure +// the defintions have been processed and any relations have be created. + +relation_kind +range_query::query_relation (edge e, tree ssa1, tree ssa2, bool get_range) +{ + basic_block bb; + int_range_max tmp; + if (!m_oracle || TREE_CODE (ssa1) != SSA_NAME || TREE_CODE (ssa2) != SSA_NAME) + return VREL_NONE; + + // Use destination block if it has a single predecessor, and this picks + // up any relation on the edge. + // Otherwise choose the src edge and the result is the same as on-exit. + if (!single_pred_p (e->dest)) + bb = e->src; + else + bb = e->dest; + + // Ensure ssa1 and ssa2 have both been evaluated. + if (get_range) + { + range_on_edge (tmp, e, ssa1); + range_on_edge (tmp, e, ssa2); + } + return m_oracle->query_relation (bb, ssa1, ssa2); +} diff --git a/gcc/value-query.h b/gcc/value-query.h index 54af031..5161d23 100644 --- a/gcc/value-query.h +++ b/gcc/value-query.h @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_QUERY_H #define GCC_QUERY_H +#include "value-relation.h" + // The value_query class is used by optimization passes that require // valueizing SSA names in terms of a tree value, but have no neeed // for ranges. @@ -91,6 +93,14 @@ public: virtual bool range_on_edge (irange &r, edge, tree expr); virtual bool range_of_stmt (irange &r, gimple *, tree name = NULL); + // Query if there is any relation between SSA1 and SSA2. + relation_kind query_relation (gimple *s, tree ssa1, tree ssa2, + bool get_range = true); + relation_kind query_relation (edge e, tree ssa1, tree ssa2, + bool get_range = true); + // If present, Access relation oracle for more advanced uses. + inline relation_oracle *oracle () const { return m_oracle; } + // DEPRECATED: This method is used from vr-values. The plan is to // rewrite all uses of it to the above API. virtual const class value_range_equiv *get_value_range (const_tree, @@ -102,6 +112,7 @@ protected: void free_value_range_equiv (class value_range_equiv *); bool get_tree_range (irange &r, tree expr, gimple *stmt); bool get_arith_expr_range (irange &r, tree expr, gimple *stmt); + relation_oracle *m_oracle; private: class equiv_allocator *equiv_alloc; diff --git a/gcc/value-relation.cc b/gcc/value-relation.cc new file mode 100644 index 0000000..3c8698f --- /dev/null +++ b/gcc/value-relation.cc @@ -0,0 +1,932 @@ +/* Header file for the value range relational processing. + Copyright (C) 2020-2021 Free Software Foundation, Inc. + Contributed by Andrew MacLeod <amacleod@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "ssa.h" + +#include "gimple-range.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "alloc-pool.h" +#include "dominance.h" + +// These VREL codes are arranged such that VREL_NONE is the first +// code, and all the rest are contiguous up to and including VREL_LAST. + +#define VREL_FIRST VREL_NONE +#define VREL_LAST NE_EXPR +#define VREL_COUNT (VREL_LAST - VREL_FIRST + 1) + +// vrel_range_assert will either assert that the tree code passed is valid, +// or mark invalid codes as unreachable to help with table optimation. +#if CHECKING_P + #define vrel_range_assert(c) \ + gcc_checking_assert ((c) >= VREL_FIRST && (c) <= VREL_LAST) +#else + #define vrel_range_assert(c) \ + if ((c) < VREL_FIRST || (c) > VREL_LAST) \ + gcc_unreachable (); +#endif + +static const char *kind_string[VREL_COUNT] = +{ "none", "<", "<=", ">", ">=", "empty", "==", "!=" }; + +// Print a relation_kind REL to file F. + +void +print_relation (FILE *f, relation_kind rel) +{ + vrel_range_assert (rel); + fprintf (f, " %s ", kind_string[rel - VREL_FIRST]); +} + +// This table is used to negate the operands. op1 REL op2 -> !(op1 REL op2). +relation_kind rr_negate_table[VREL_COUNT] = { +// NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EMPTY, EQ_EXPR, NE_EXPR + VREL_NONE, GE_EXPR, GT_EXPR, LE_EXPR, LT_EXPR, VREL_EMPTY, NE_EXPR, EQ_EXPR }; + +// Negate the relation, as in logical negation. + +relation_kind +relation_negate (relation_kind r) +{ + vrel_range_assert (r); + return rr_negate_table [r - VREL_FIRST]; +} + +// This table is used to swap the operands. op1 REL op2 -> op2 REL op1. +relation_kind rr_swap_table[VREL_COUNT] = { +// NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EMPTY, EQ_EXPR, NE_EXPR + VREL_NONE, GT_EXPR, GE_EXPR, LT_EXPR, LE_EXPR, VREL_EMPTY, EQ_EXPR, NE_EXPR }; + +// Return the relation as if the operands were swapped. + +relation_kind +relation_swap (relation_kind r) +{ + vrel_range_assert (r); + return rr_swap_table [r - VREL_FIRST]; +} + +// This table is used to perform an intersection between 2 relations. + +relation_kind rr_intersect_table[VREL_COUNT][VREL_COUNT] = { +// NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EMPTY, EQ_EXPR, NE_EXPR +// VREL_NONE + { VREL_NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, VREL_EMPTY, EQ_EXPR, NE_EXPR }, +// LT_EXPR + { LT_EXPR, LT_EXPR, LT_EXPR, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, LT_EXPR }, +// LE_EXPR + { LE_EXPR, LT_EXPR, LE_EXPR, VREL_EMPTY, EQ_EXPR, VREL_EMPTY, EQ_EXPR, LT_EXPR }, +// GT_EXPR + { GT_EXPR, VREL_EMPTY, VREL_EMPTY, GT_EXPR, GT_EXPR, VREL_EMPTY, VREL_EMPTY, GT_EXPR }, +// GE_EXPR + { GE_EXPR, VREL_EMPTY, EQ_EXPR, GT_EXPR, GE_EXPR, VREL_EMPTY, EQ_EXPR, GT_EXPR }, +// VREL_EMPTY + { VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY, VREL_EMPTY }, +// EQ_EXPR + { EQ_EXPR, VREL_EMPTY, EQ_EXPR, VREL_EMPTY, EQ_EXPR, VREL_EMPTY, EQ_EXPR, VREL_EMPTY }, +// NE_EXPR + { NE_EXPR, LT_EXPR, LT_EXPR, GT_EXPR, GT_EXPR, VREL_EMPTY, VREL_EMPTY, NE_EXPR } }; + + +// Intersect relation R! with relation R2 and return the resulting relation. + +relation_kind +relation_intersect (relation_kind r1, relation_kind r2) +{ + vrel_range_assert (r1); + vrel_range_assert (r2); + return rr_intersect_table[r1 - VREL_FIRST][r2 - VREL_FIRST]; +} + + +// This table is used to perform a union between 2 relations. + +relation_kind rr_union_table[VREL_COUNT][VREL_COUNT] = { +// NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, EMPTY, EQ_EXPR, NE_EXPR +// VREL_NONE + { VREL_NONE, VREL_NONE, VREL_NONE, VREL_NONE, VREL_NONE, VREL_NONE, VREL_NONE, VREL_NONE }, +// LT_EXPR + { VREL_NONE, LT_EXPR, LE_EXPR, NE_EXPR, VREL_NONE, LT_EXPR, LE_EXPR, NE_EXPR }, +// LE_EXPR + { VREL_NONE, LE_EXPR, LE_EXPR, VREL_NONE, VREL_NONE, LE_EXPR, LE_EXPR, VREL_NONE }, +// GT_EXPR + { VREL_NONE, NE_EXPR, VREL_NONE, GT_EXPR, GE_EXPR, GT_EXPR, GE_EXPR, NE_EXPR }, +// GE_EXPR + { VREL_NONE, VREL_NONE, VREL_NONE, GE_EXPR, GE_EXPR, GE_EXPR, GE_EXPR, VREL_NONE }, +// VREL_EMPTY + { VREL_NONE, LT_EXPR, LE_EXPR, GT_EXPR, GE_EXPR, VREL_EMPTY, EQ_EXPR, NE_EXPR }, +// EQ_EXPR + { VREL_NONE, LE_EXPR, LE_EXPR, GE_EXPR, GE_EXPR, EQ_EXPR, EQ_EXPR, VREL_NONE }, +// NE_EXPR + { VREL_NONE, NE_EXPR, VREL_NONE, NE_EXPR, VREL_NONE, NE_EXPR, VREL_NONE, NE_EXPR } }; + +// Union relation R1 with relation R2 and return the result. + +relation_kind +relation_union (relation_kind r1, relation_kind r2) +{ + vrel_range_assert (r1); + vrel_range_assert (r2); + return rr_union_table[r1 - VREL_FIRST][r2 - VREL_FIRST]; +} + + +// ------------------------------------------------------------------------- + +// This class represents an equivalency set, and contains a link to the next +// one in the list to be searched. + +// The very first element in the m_equiv chain is actually just a summary +// element in which the m_names bitmap is used to indicate that an ssa_name +// has an equivalence set in this block. +// This allows for much faster traversal of the DOM chain, as a search for +// SSA_NAME simply requires walking the DOM chain until a block is found +// which has the bit for SSA_NAME set. Then scan for the equivalency set in +// that block. No previous blcoks need be searched. + +class equiv_chain +{ +public: + bitmap m_names; // ssa-names in equiv set. + basic_block m_bb; // Block this belongs to + equiv_chain *m_next; // Next in block list. + void dump (FILE *f) const; // Show names in this list. +}; + + +// Dump the names in this equivalence set. + +void +equiv_chain::dump (FILE *f) const +{ + bitmap_iterator bi; + unsigned i; + + if (!m_names) + return; + fprintf (f, "Equivalence set : ["); + unsigned c = 0; + EXECUTE_IF_SET_IN_BITMAP (m_names, 0, i, bi) + { + if (ssa_name (i)) + { + if (c++) + fprintf (f, ", "); + print_generic_expr (f, ssa_name (i), TDF_SLIM); + } + } + fprintf (f, "]\n"); +} + +// Instantiate an equivalency oracle. + +equiv_oracle::equiv_oracle () +{ + bitmap_obstack_initialize (&m_bitmaps); + m_equiv.create (0); + m_equiv.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1); + m_equiv_set = BITMAP_ALLOC (&m_bitmaps); + obstack_init (&m_chain_obstack); +} + +// Destruct an equivalency oracle. + +equiv_oracle::~equiv_oracle () +{ + obstack_free (&m_chain_obstack, NULL); + m_equiv.release (); + bitmap_obstack_release (&m_bitmaps); +} + +// Find and return the equivalency set for SSA along the dominators of BB. +// This is the external API. + +const_bitmap +equiv_oracle::equiv_set (tree ssa, basic_block bb) const +{ + // Search the dominator tree for an equivalency. + equiv_chain *equiv = find_equiv_dom (ssa, bb); + if (equiv) + return equiv->m_names; + + return NULL; +} + + +// If SSA has an equivalence in block BB, find and return it. +// Otherwise return NULL. + +equiv_chain * +equiv_oracle::find_equiv_block (unsigned ssa, int bb) const +{ + equiv_chain *ptr = NULL; + if (bb >= (int)m_equiv.length ()) + return NULL; + + // If there are equiv sets and SSA is in one in this block, find it. + // Otherwise return NULL. + if (m_equiv[bb] && bitmap_bit_p (m_equiv[bb]->m_names, ssa)) + { + for (ptr = m_equiv[bb]->m_next; ptr; ptr = ptr->m_next) + if (bitmap_bit_p (ptr->m_names, ssa)) + break; + } + return ptr; +} + +// Starting at block BB, walk the dominator chain looking for the nearest +// equivalence set containing NAME. + +equiv_chain * +equiv_oracle::find_equiv_dom (tree name, basic_block bb) const +{ + unsigned v = SSA_NAME_VERSION (name); + // Short circuit looking for names which have no equivalences. + // Saves time looking for something which does not exist. + if (!bitmap_bit_p (m_equiv_set, v)) + return NULL; + + // NAME has at least once equivalence set, check to see if it has one along + // the dominator tree. + for ( ; bb; bb = get_immediate_dominator (CDI_DOMINATORS, bb)) + { + equiv_chain *ptr = find_equiv_block (v, bb->index); + if (ptr) + return ptr; + } + return NULL; +} + +// Register equivalance between ssa_name V and set EQUIV in block BB, + +bitmap +equiv_oracle::register_equiv (basic_block bb, unsigned v, equiv_chain *equiv) +{ + // V will have an equivalency now. + bitmap_set_bit (m_equiv_set, v); + + // If that equiv chain is in this block, simply use it. + if (equiv->m_bb == bb) + { + bitmap_set_bit (equiv->m_names, v); + bitmap_set_bit (m_equiv[bb->index]->m_names, v); + return NULL; + } + + // Otherwise create an equivalence for this block which is a copy + // of equiv, the add V to the set. + bitmap b = BITMAP_ALLOC (&m_bitmaps); + bitmap_copy (b, equiv->m_names); + bitmap_set_bit (b, v); + return b; +} + +// Register equivalence between set equiv_1 and equiv_2 in block BB. +// Return NULL if either name can be merged with the other. Otherwise +// return a pointer to the combined bitmap of names. This allows the +// caller to do any setup required for a new element. + +bitmap +equiv_oracle::register_equiv (basic_block bb, equiv_chain *equiv_1, + equiv_chain *equiv_2) +{ + // If equiv_1 is alreayd in BB, use it as the combined set. + if (equiv_1->m_bb == bb) + { + bitmap_ior_into (equiv_1->m_names, equiv_2->m_names); + // Its hard to delete from a single linked list, so + // just clear the second one. + if (equiv_2->m_bb == bb) + bitmap_clear (equiv_2->m_names); + else + // Ensure equiv_2s names are in the summary for BB. + bitmap_ior_into (m_equiv[bb->index]->m_names, equiv_2->m_names); + return NULL; + } + // If equiv_2 is in BB, use it for the combined set. + if (equiv_2->m_bb == bb) + { + bitmap_ior_into (equiv_2->m_names, equiv_1->m_names); + // Add equiv_1 names into the summary. + bitmap_ior_into (m_equiv[bb->index]->m_names, equiv_1->m_names); + return NULL; + } + + // At this point, neither equivalence is from this block. + bitmap b = BITMAP_ALLOC (&m_bitmaps); + bitmap_copy (b, equiv_1->m_names); + bitmap_ior_into (b, equiv_2->m_names); + return b; +} + + +// Register an equivalence between SSA1 and SSA2 in block BB. +// The equivalence oracle maintains a vector of equivalencies indexed by basic +// block. When an equivalence bteween SSA1 and SSA2 is registered in block BB, +// a query is made as to what equivalences both names have already, and +// any preexisting equivalences are merged to create a single equivalence +// containing all the ssa_names in this basic block. + +void +equiv_oracle::register_equiv (basic_block bb, tree ssa1, tree ssa2) +{ + unsigned v1 = SSA_NAME_VERSION (ssa1); + unsigned v2 = SSA_NAME_VERSION (ssa2); + equiv_chain *equiv_1 = find_equiv_dom (ssa1, bb); + equiv_chain *equiv_2 = find_equiv_dom (ssa2, bb); + + // Check if they are the same set + if (equiv_1 && equiv_1 == equiv_2) + return; + + bitmap equiv_set; + + // Case where we have 2 SSA_NAMEs that are not in any set. + if (!equiv_1 && !equiv_2) + { + bitmap_set_bit (m_equiv_set, v1); + bitmap_set_bit (m_equiv_set, v2); + + equiv_set = BITMAP_ALLOC (&m_bitmaps); + bitmap_set_bit (equiv_set, v1); + bitmap_set_bit (equiv_set, v2); + } + else if (!equiv_1 && equiv_2) + equiv_set = register_equiv (bb, v1, equiv_2); + else if (equiv_1 && !equiv_2) + equiv_set = register_equiv (bb, v2, equiv_1); + else + equiv_set = register_equiv (bb, equiv_1, equiv_2); + + // A non-null return is a bitmap that is to be added to the current + // block as a new equivalence. + if (!equiv_set) + return; + + equiv_chain *ptr; + + // Check if this is the first time a block has an equivalence added. + // and create a header block. And set the summary for this block. + if (!m_equiv[bb->index]) + { + ptr = (equiv_chain *) obstack_alloc (&m_chain_obstack, + sizeof (equiv_chain)); + ptr->m_names = BITMAP_ALLOC (&m_bitmaps); + bitmap_copy (ptr->m_names, equiv_set); + ptr->m_bb = bb; + ptr->m_next = NULL; + m_equiv[bb->index] = ptr; + } + + // Now create the element for this equiv set and initialize it. + ptr = (equiv_chain *) obstack_alloc (&m_chain_obstack, sizeof (equiv_chain)); + ptr->m_names = equiv_set; + ptr->m_bb = bb; + gcc_checking_assert (bb->index < (int)m_equiv.length ()); + ptr->m_next = m_equiv[bb->index]->m_next; + m_equiv[bb->index]->m_next = ptr; + bitmap_ior_into (m_equiv[bb->index]->m_names, equiv_set); +} + +// Make sure the BB vector is big enough and grow it if needed. + +void +equiv_oracle::limit_check (basic_block bb) +{ + int i = (bb) ? bb->index : last_basic_block_for_fn (cfun); + if (i >= (int)m_equiv.length ()) + m_equiv.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1); +} + +// Dump the equivalence sets in BB to file F. + +void +equiv_oracle::dump (FILE *f, basic_block bb) const +{ + if (bb->index >= (int)m_equiv.length ()) + return; + if (!m_equiv[bb->index]) + return; + + equiv_chain *ptr = m_equiv[bb->index]->m_next; + for (; ptr; ptr = ptr->m_next) + ptr->dump (f); +} + +// Dump all equivalence sets known to the oracle. + +void +equiv_oracle::dump (FILE *f) const +{ + fprintf (f, "Equivalency dump\n"); + for (unsigned i = 0; i < m_equiv.length (); i++) + if (m_equiv[i]) + { + fprintf (f, "BB%d\n", i); + dump (f, BASIC_BLOCK_FOR_FN (cfun, i)); + } +} + + +// -------------------------------------------------------------------------- + +// The value-relation class is used to encapsulate the represention of an +// individual relation between 2 ssa-names, and to facilitate operating on +// the relation. + +class value_relation +{ +public: + value_relation (); + value_relation (relation_kind kind, tree n1, tree n2); + void set_relation (relation_kind kind, tree n1, tree n2); + + inline relation_kind kind () const { return related; } + inline tree op1 () const { return name1; } + inline tree op2 () const { return name2; } + + bool union_ (value_relation &p); + bool intersect (value_relation &p); + void negate (); + void swap (); + + void dump (FILE *f) const; +private: + relation_kind related; + tree name1, name2; +}; + +// Set relation R between ssa_name N1 and N2. + +inline void +value_relation::set_relation (relation_kind r, tree n1, tree n2) +{ + gcc_checking_assert (SSA_NAME_VERSION (n1) != SSA_NAME_VERSION (n2)); + related = r; + name1 = n1; + name2 = n2; +} + +// Default constructor. + +inline +value_relation::value_relation () +{ + related = VREL_NONE; + name1 = NULL_TREE; + name2 = NULL_TREE; +} + +// Constructor for relation R between SSA version N1 nd N2. + +inline +value_relation::value_relation (relation_kind kind, tree n1, tree n2) +{ + set_relation (kind, n1, n2); +} + +// Negate the current relation. + +void +value_relation::negate () +{ + related = relation_negate (related); +} + +// Modify the relation as if the operands were being swapped. + +void +value_relation::swap () +{ + related = relation_swap (related); +} + +// Perform an intersection between 2 relations. *this &&= p. + +bool +value_relation::intersect (value_relation &p) +{ + // Save previous value + relation_kind old = related; + + if (p.op1 () == op1 () && p.op2 () == op2 ()) + related = relation_intersect (kind (), p.kind ()); + else if (p.op2 () == op1 () && p.op1 () == op2 ()) + related = relation_intersect (kind (), relation_swap (p.kind ())); + else + return false; + + return old != related; +} + +// Perform a union between 2 relations. *this ||= p. + +bool +value_relation::union_ (value_relation &p) +{ + // Save previous value + relation_kind old = related; + + if (p.op1 () == op1 () && p.op2 () == op2 ()) + related = relation_union (kind(), p.kind()); + else if (p.op2 () == op1 () && p.op1 () == op2 ()) + related = relation_union (kind(), relation_swap (p.kind ())); + else + return false; + + return old != related; +} + + +// Dump the relation to file F. + +void +value_relation::dump (FILE *f) const +{ + if (!name1 || !name2) + { + fprintf (f, "uninitialized"); + return; + } + fputc ('(', f); + print_generic_expr (f, op1 (), TDF_SLIM); + print_relation (f, kind ()); + print_generic_expr (f, op2 (), TDF_SLIM); + fputc(')', f); +} + +// This container is used to link relations in a chain. + +class relation_chain : public value_relation +{ +public: + relation_chain *m_next; +}; + +// ------------------------------------------------------------------------ + +// Instantiate a relation oracle. + +relation_oracle::relation_oracle () +{ + m_relations.create (0); + m_relations.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1); + m_relation_set = BITMAP_ALLOC (&m_bitmaps); + m_tmp = BITMAP_ALLOC (&m_bitmaps); +} + +// Destruct a relation oracle. + +relation_oracle::~relation_oracle () +{ + m_relations.release (); +} + +// Register relation K between ssa_name OP1 and OP2 on STMT. + +void +relation_oracle::register_relation (gimple *stmt, relation_kind k, tree op1, + tree op2) +{ + gcc_checking_assert (TREE_CODE (op1) == SSA_NAME); + gcc_checking_assert (TREE_CODE (op2) == SSA_NAME); + gcc_checking_assert (stmt && gimple_bb (stmt)); + + // Don't register lack of a relation. + if (k == VREL_NONE) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + value_relation vr (k, op1, op2); + fprintf (dump_file, " Registering value_relation "); + vr.dump (dump_file); + fprintf (dump_file, " (bb%d) at ", gimple_bb (stmt)->index); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + + // This relation applies to the entire block, use STMT's block. + // Equivalencies are handled by the equivalence oracle. + if (k == EQ_EXPR) + register_equiv (gimple_bb (stmt), op1, op2); + else + register_relation (gimple_bb (stmt), k, op1, op2); +} + +// Register relation K between ssa_name OP1 and OP2 on edge E. + +void +relation_oracle::register_relation (edge e, relation_kind k, tree op1, + tree op2) +{ + gcc_checking_assert (TREE_CODE (op1) == SSA_NAME); + gcc_checking_assert (TREE_CODE (op2) == SSA_NAME); + + // Do not register lack of relation, or blocks which have more than + // edge E for a predecessor. + if (k == VREL_NONE || !single_pred_p (e->dest)) + return; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + value_relation vr (k, op1, op2); + fprintf (dump_file, " Registering value_relation "); + vr.dump (dump_file); + fprintf (dump_file, " on (%d->%d)\n", e->src->index, e->dest->index); + } + + // Equivalencies are handled by the equivalence oracle. + if (k == EQ_EXPR) + register_equiv (e->dest, op1, op2); + else + register_relation (e->dest, k, op1, op2); +} + +// Register relation K between OP! and OP2 in block BB. +// This creates the record and searches for existing records in the dominator +// tree to merge with. + +void +relation_oracle::register_relation (basic_block bb, relation_kind k, tree op1, + tree op2) +{ + gcc_checking_assert (k != VREL_NONE); + + value_relation vr(k, op1, op2); + int bbi = bb->index; + + if (bbi >= (int)m_relations.length()) + m_relations.safe_grow_cleared (last_basic_block_for_fn (cfun) + 1); + + // Summary bitmap indicating what ssa_names have relations in this BB. + bitmap bm = m_relations[bbi].m_names; + if (!bm) + bm = m_relations[bbi].m_names = BITMAP_ALLOC (&m_bitmaps); + unsigned v1 = SSA_NAME_VERSION (op1); + unsigned v2 = SSA_NAME_VERSION (op2); + + relation_kind curr; + relation_chain *ptr; + curr = find_relation_block (bbi, v1, v2, &ptr); + // There is an existing relation in this block, just intersect with it. + if (curr != VREL_NONE) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Intersecting with existing "); + ptr->dump (dump_file); + } + // Check into whether we can simply replace the relation rather than + // intersecting it. THis may help with some optimistic iterative + // updating algorithms. + ptr->intersect (vr); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " to produce "); + ptr->dump (dump_file); + fprintf (dump_file, "\n"); + } + return; + } + + // Check for an existing relation further up the DOM chain. + // By including dominating relations, The first one found in any search + // will be the aggregate of all the previous ones. + curr = find_relation_dom (bb, v1, v2); + if (curr != VREL_NONE) + k = relation_intersect (curr, k); + + bitmap_set_bit (bm, v1); + bitmap_set_bit (bm, v2); + bitmap_set_bit (m_relation_set, v1); + bitmap_set_bit (m_relation_set, v2); + + ptr = (relation_chain *) obstack_alloc (&m_chain_obstack, + sizeof (relation_chain)); + ptr->set_relation (k, op1, op2); + ptr->m_next = m_relations[bbi].m_head; + m_relations[bbi].m_head = ptr;; +} + +// Find the relation between any ssa_name in B1 and any name in B2 in block BB. +// This will allow equivalencies to be applied to any SSA_NAME in a relation. + +relation_kind +relation_oracle::find_relation_block (unsigned bb, const_bitmap b1, + const_bitmap b2) +{ + const_bitmap bm; + if (bb >= m_relations.length()) + return VREL_NONE; + + bm = m_relations[bb].m_names; + if (!bm) + return VREL_NONE; + + // If both b1 and b2 aren't referenced in thie block, cant be a relation + if (!bitmap_intersect_p (bm, b1) || !bitmap_intersect_p (bm, b2)) + return VREL_NONE; + + // Search for the fiorst relation that contains BOTH an element from B1 + // and B2, and return that relation. + for (relation_chain *ptr = m_relations[bb].m_head; ptr ; ptr = ptr->m_next) + { + unsigned op1 = SSA_NAME_VERSION (ptr->op1 ()); + unsigned op2 = SSA_NAME_VERSION (ptr->op2 ()); + if (bitmap_bit_p (b1, op1) && bitmap_bit_p (b1, op2)) + return ptr->kind (); + if (bitmap_bit_p (b1, op2) && bitmap_bit_p (b1, op1)) + return relation_swap (ptr->kind ()); + } + + return VREL_NONE; +} + +// Search the DOM tree for a relation between an element of B1 and B2, starting +// with block BB. + +relation_kind +relation_oracle::find_relation_dom (basic_block bb, const_bitmap b1, + const_bitmap b2) +{ + relation_kind r; + // If either name does not occur in a relation anywhere, there isnt one. + if (!bitmap_intersect_p (m_relation_set, b1) + || !bitmap_intersect_p (m_relation_set, b2)) + return VREL_NONE; + + // Search each block in the DOM tree checking. + for ( ; bb; bb = get_immediate_dominator (CDI_DOMINATORS, bb)) + { + r = find_relation_block (bb->index, b1, b2); + if (r != VREL_NONE) + return r; + } + return VREL_NONE; + +} + +// Find a relation in block BB between ssa version V1 and V2. If a relation +// is found, return a pointer to the chain object in OBJ. + +relation_kind +relation_oracle::find_relation_block (int bb, unsigned v1, unsigned v2, + relation_chain **obj) +{ + if (bb >= (int)m_relations.length()) + return VREL_NONE; + + const_bitmap bm = m_relations[bb].m_names; + if (!bm) + return VREL_NONE; + + // If both b1 and b2 aren't referenced in thie block, cant be a relation + if (!bitmap_bit_p (bm, v1) || !bitmap_bit_p (bm, v2)) + return VREL_NONE; + + relation_chain *ptr; + for (ptr = m_relations[bb].m_head; ptr ; ptr = ptr->m_next) + { + unsigned op1 = SSA_NAME_VERSION (ptr->op1 ()); + unsigned op2 = SSA_NAME_VERSION (ptr->op2 ()); + if (v1 == op1 && v2 == op2) + { + if (obj) + *obj = ptr; + return ptr->kind (); + } + if (v1 == op2 && v2 == op1) + { + if (obj) + *obj = ptr; + return relation_swap (ptr->kind ()); + } + } + + return VREL_NONE; +} + +// Find a relation between SSA version V1 and V2 in the dominator tree +// starting with block BB + +relation_kind +relation_oracle::find_relation_dom (basic_block bb, unsigned v1, unsigned v2) +{ + relation_kind r; + // IF either name does not occur in a relation anywhere, there isnt one. + if (!bitmap_bit_p (m_relation_set, v1) || !bitmap_bit_p (m_relation_set, v2)) + return VREL_NONE; + + for ( ; bb; bb = get_immediate_dominator (CDI_DOMINATORS, bb)) + { + r = find_relation_block (bb->index, v1, v2); + if (r != VREL_NONE) + return r; + } + return VREL_NONE; + +} + +// Query if there is a relation between SSA1 and SS2 in block BB or a +// dominator of BB + +relation_kind +relation_oracle::query_relation (basic_block bb, tree ssa1, tree ssa2) +{ + relation_kind kind; + unsigned v1 = SSA_NAME_VERSION (ssa1); + unsigned v2 = SSA_NAME_VERSION (ssa2); + if (v1 == v2) + return EQ_EXPR; + + // Check for equivalence first. + const_bitmap equiv1 = equiv_set (ssa1, bb); + if (equiv1 && bitmap_bit_p (equiv1, v2)) + return EQ_EXPR; + + // Initially look for a direct relationship and just return that. + kind = find_relation_dom (bb, v1, v2); + if (kind != VREL_NONE) + return kind; + + // If one is not found, see if there is a relationship between equivalences. + // If v2 isn't in v1s equiv set, then v1 shouldn't be in v2's set either. + const_bitmap equiv2 = equiv_set (ssa2, bb); + gcc_checking_assert (!equiv2 || !bitmap_bit_p (equiv2, v1)); + + if (!equiv1 && !equiv2) + kind = VREL_NONE; + else if (!equiv1) + { + bitmap_clear (m_tmp); + bitmap_set_bit (m_tmp, v1); + kind = find_relation_dom (bb, m_tmp, equiv2); + } + else if (!equiv2) + { + bitmap_clear (m_tmp); + bitmap_set_bit (m_tmp, v2); + kind = find_relation_dom (bb, equiv1, m_tmp); + } + else + kind = find_relation_dom (bb, equiv1, equiv2); + return kind; +} + +// Dump all the relations in block BB to file F. + +void +relation_oracle::dump (FILE *f, basic_block bb) const +{ + equiv_oracle::dump (f,bb); + + if (bb->index >= (int)m_relations.length ()) + return; + if (!m_relations[bb->index].m_names) + return; + + relation_chain *ptr = m_relations[bb->index].m_head; + for (; ptr; ptr = ptr->m_next) + { + fprintf (f, "Relational : "); + ptr->dump (f); + fprintf (f, "\n"); + } +} + +// Dump all the relations known to file F. + +void +relation_oracle::dump (FILE *f) const +{ + fprintf (f, "Relation dump\n"); + for (unsigned i = 0; i < m_relations.length (); i++) + { + fprintf (f, "BB%d\n", i); + dump (f, BASIC_BLOCK_FOR_FN (cfun, i)); + } +} diff --git a/gcc/value-relation.h b/gcc/value-relation.h new file mode 100644 index 0000000..1148854 --- /dev/null +++ b/gcc/value-relation.h @@ -0,0 +1,159 @@ +/* Header file for the value range relational processing. + Copyright (C) 2020-2021 Free Software Foundation, Inc. + Contributed by Andrew MacLeod <amacleod@redhat.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_VALUE_RELATION_H +#define GCC_VALUE_RELATION_H + + +// This file provides access to a relation oracle which can be used to +// maintain and query relations and equivalences between SSA_NAMES. +// +// The general range_query object provided in value-query.h provides +// access to an oracle, if one is available, via the oracle() method. +// Thre are also a couple of access routines provided, which even if there is +// no oracle, will return the default VREL_NONE no relation. +// +// Typically, when a ranger object is active, there will be an oracle, and +// any information available can be directly queried. Ranger also sets and +// utilizes the relation information to enhance it's range calculations, this +// is totally transparent to the client, and they are free to make queries. +// +// +// relation_kind is a typedef of enum tree_code, but has restricted range +// and a couple of extra values. +// +// A query is made requesting the relation between SSA1 and SSA@ in a basic +// block, or on an edge, the possible return values are: +// +// EQ_EXPR, NE_EXPR, LT_EXPR, LE_EXPR, GT_EXPR, and GE_EXPR mean the same. +// VREL_NONE : No relation between the 2 names. +// VREL_EMPTY : Impossible relation (ie, A < B && A > B produces VREL_EMPTY. +// +// The oracle maintains EQ_EXPR relations with equivalency sets, so if a +// relation comes back EQ_EXPR, it is also possible to query the set of +// equivlaencies. These are basically bitmaps over ssa_names. +// +// relations are maintained via the dominace trees, are are optimized assuming +// they are registered in dominance order. When a new relation is added, it +// is intersected with whatever existing relation exists in the dominance tree +// and registered at the specified block. + + +// Rather than introduce a new enumerated type for relations, we can use the +// existing tree_codes for relations, plus add a couple of #defines for +// the other cases. These codes are arranged such that VREL_NONE is the first +// code, and all the rest are contiguous. + +typedef enum tree_code relation_kind; + +#define VREL_NONE TRUTH_NOT_EXPR +#define VREL_EMPTY LTGT_EXPR + +// General relation kind transformations. +relation_kind relation_union (relation_kind r1, relation_kind r2); +relation_kind relation_intersect (relation_kind r1, relation_kind r2); +relation_kind relation_negate (relation_kind r); +relation_kind relation_swap (relation_kind r); +void print_relation (FILE *f, relation_kind rel); + +// Declared internally in value-relation.cc +class equiv_chain; + +// The equivalency oracle maintains equivalencies using the dominator tree. +// Equivalencies apply to an entire basic block. Equivalencies on edges +// can be represented only on edges whose destination is a single-pred block, +// and the equivalence is simply applied to that succesor block. + +class equiv_oracle +{ +public: + equiv_oracle (); + ~equiv_oracle (); + + const_bitmap equiv_set (tree ssa, basic_block bb) const; + void register_equiv (basic_block bb, tree ssa1, tree ssa2); + + void dump (FILE *f, basic_block bb) const; + void dump (FILE *f) const; + +protected: + bitmap_obstack m_bitmaps; + struct obstack m_chain_obstack; +private: + bitmap m_equiv_set; // Index by ssa-name. true if an equivalence exists. + vec <equiv_chain *> m_equiv; // Index by BB. list of equivalences. + + void limit_check (basic_block bb = NULL); + equiv_chain *find_equiv_block (unsigned ssa, int bb) const; + equiv_chain *find_equiv_dom (tree name, basic_block bb) const; + + bitmap register_equiv (basic_block bb, unsigned v, equiv_chain *equiv_1); + bitmap register_equiv (basic_block bb, equiv_chain *equiv_1, + equiv_chain *equiv_2); + +}; + +// Summary block header for relations. + +class relation_chain_head +{ +public: + bitmap m_names; // ssa_names with relations in this block. + class relation_chain *m_head; // List of relations in block. +}; + +// A relation oracle maintains a set of relations between ssa_names using the +// dominator tree structures. Equivalencies are considered a subset of +// a general relation and maintained by an equivalence oracle by transparently +// passing any EQ_EXPR relations to it. +// Relations are handled at the basic block level. All relations apply to +// an entire block, and are thus kept in a summary index by block. +// Similar to the equivalence oracle, edges are handled by applying the +// relation to the destination block of the edge, but ONLY if that block +// has a single successor. For now. + +class relation_oracle : public equiv_oracle +{ +public: + relation_oracle (); + ~relation_oracle (); + + void register_relation (gimple *stmt, relation_kind k, tree op1, tree op2); + void register_relation (edge e, relation_kind k, tree op1, tree op2); + + relation_kind query_relation (basic_block bb, tree ssa1, tree ssa2); + + void dump (FILE *f, basic_block bb) const; + void dump (FILE *f) const; +private: + bitmap m_tmp; + bitmap m_relation_set; // Index by ssa-name. True if a relation exists + vec <relation_chain_head> m_relations; // Index by BB, list of relations. + relation_kind find_relation_block (unsigned bb, const_bitmap b1, + const_bitmap b2); + relation_kind find_relation_dom (basic_block bb, const_bitmap b1, + const_bitmap b2); + relation_kind find_relation_block (int bb, unsigned v1, unsigned v2, + relation_chain **obj = NULL); + relation_kind find_relation_dom (basic_block bb, unsigned v1, unsigned v2); + void register_relation (basic_block bb, relation_kind k, tree op1, tree op2); +}; + +#endif /* GCC_VALUE_RELATION_H */ diff --git a/libgcc/config/rs6000/float128-ifunc.c b/libgcc/config/rs6000/float128-ifunc.c index 57545dd..ef7f731 100644 --- a/libgcc/config/rs6000/float128-ifunc.c +++ b/libgcc/config/rs6000/float128-ifunc.c @@ -46,7 +46,9 @@ #endif #define SW_OR_HW(SW, HW) (__builtin_cpu_supports ("ieee128") ? HW : SW) +#ifdef FLOAT128_HW_INSNS_ISA3_1 #define SW_OR_HW_ISA3_1(SW, HW) (__builtin_cpu_supports ("arch_3_1") ? HW : SW) +#endif /* Resolvers. */ static __typeof__ (__addkf3_sw) * @@ -97,6 +99,7 @@ __floatdikf_resolve (void) return SW_OR_HW (__floatdikf_sw, __floatdikf_hw); } +#ifdef FLOAT128_HW_INSNS_ISA3_1 static __typeof__ (__floattikf_sw) * __floattikf_resolve (void) { @@ -108,6 +111,7 @@ __floatuntikf_resolve (void) { return SW_OR_HW_ISA3_1 (__floatuntikf_sw, __floatuntikf_hw); } +#endif static __typeof__ (__floatunsikf_sw) * __floatunsikf_resolve (void) @@ -121,7 +125,7 @@ __floatundikf_resolve (void) return SW_OR_HW (__floatundikf_sw, __floatundikf_hw); } - +#ifdef FLOAT128_HW_INSNS_ISA3_1 static __typeof__ (__fixkfti_sw) * __fixkfti_resolve (void) { @@ -133,6 +137,7 @@ __fixunskfti_resolve (void) { return SW_OR_HW_ISA3_1 (__fixunskfti_sw, __fixunskfti_hw); } +#endif static __typeof__ (__fixkfsi_sw) * __fixkfsi_resolve (void) @@ -323,6 +328,7 @@ TFtype __floatsikf (SItype_ppc) TFtype __floatdikf (DItype_ppc) __attribute__ ((__ifunc__ ("__floatdikf_resolve"))); +#ifdef FLOAT128_HW_INSNS_ISA3_1 TFtype __floattikf (TItype_ppc) __attribute__ ((__ifunc__ ("__floattikf_resolve"))); @@ -334,6 +340,7 @@ TItype_ppc __fixkfti (TFtype) UTItype_ppc __fixunskfti (TFtype) __attribute__ ((__ifunc__ ("__fixunskfti_resolve"))); +#endif TFtype __floatunsikf (USItype_ppc) __attribute__ ((__ifunc__ ("__floatunsikf_resolve"))); diff --git a/libgcc/config/rs6000/t-float128-hw b/libgcc/config/rs6000/t-float128-hw index c082736..d64ca4d 100644 --- a/libgcc/config/rs6000/t-float128-hw +++ b/libgcc/config/rs6000/t-float128-hw @@ -13,13 +13,6 @@ fp128_hw_static_obj = $(addsuffix $(objext),$(fp128_hw_funcs)) fp128_hw_shared_obj = $(addsuffix _s$(objext),$(fp128_hw_funcs)) fp128_hw_obj = $(fp128_hw_static_obj) $(fp128_hw_shared_obj) -# New functions for ISA 3.1 hardware support -fp128_3_1_hw_funcs = float128-p10 -fp128_3_1_hw_src = $(srcdir)/config/rs6000/float128-p10.c -fp128_3_1_hw_static_obj = $(addsuffix $(objext),$(fp128_3_1_hw_funcs)) -fp128_3_1_hw_shared_obj = $(addsuffix _s$(objext),$(fp128_3_1_hw_funcs)) -fp128_3_1_hw_obj = $(fp128_3_1_hw_static_obj) $(fp128_3_1_hw_shared_obj) - fp128_ifunc_funcs = float128-ifunc fp128_ifunc_src = $(srcdir)/config/rs6000/float128-ifunc.c fp128_ifunc_static_obj = float128-ifunc$(objext) @@ -37,18 +30,9 @@ FP128_CFLAGS_HW = -Wno-type-limits -mvsx -mfloat128 \ -I$(srcdir)/config/rs6000 \ $(FLOAT128_HW_INSNS) -FP128_3_1_CFLAGS_HW = -Wno-type-limits -mvsx -mcpu=power10 \ - -mfloat128-hardware -mno-gnu-attribute \ - -I$(srcdir)/soft-fp \ - -I$(srcdir)/config/rs6000 \ - $(FLOAT128_HW_INSNS) - $(fp128_hw_obj) : INTERNAL_CFLAGS += $(FP128_CFLAGS_HW) $(fp128_hw_obj) : $(srcdir)/config/rs6000/t-float128-hw -$(fp128_3_1_hw_obj) : INTERNAL_CFLAGS += $(FP128_3_1_CFLAGS_HW) -$(fp128_3_1_hw_obj) : $(srcdir)/config/rs6000/t-float128-p10-hw - $(fp128_ifunc_obj) : INTERNAL_CFLAGS += $(FP128_CFLAGS_SW) $(fp128_ifunc_obj) : $(srcdir)/config/rs6000/t-float128-hw diff --git a/libgcc/config/rs6000/t-float128-p10-hw b/libgcc/config/rs6000/t-float128-p10-hw index de36227..edaaee0 100644 --- a/libgcc/config/rs6000/t-float128-p10-hw +++ b/libgcc/config/rs6000/t-float128-p10-hw @@ -2,7 +2,7 @@ # Tell the float128 functions that the ISA 3.1 hardware support can # be compiled it to be selected via IFUNC functions. -FLOAT128_HW_INSNS = -DFLOAT128_HW_INSNS +FLOAT128_HW_INSNS += -DFLOAT128_HW_INSNS_ISA3_1 # New functions for hardware support @@ -14,7 +14,7 @@ fp128_3_1_hw_obj = $(fp128_3_1_hw_static_obj) $(fp128_3_1_hw_shared_obj) # Build the hardware support functions with appropriate hardware support FP128_3_1_CFLAGS_HW = -Wno-type-limits -mvsx -mfloat128 \ - -mpower10 \ + -mcpu=power10 \ -mfloat128-hardware -mno-gnu-attribute \ -I$(srcdir)/soft-fp \ -I$(srcdir)/config/rs6000 \ diff --git a/libgcc/configure b/libgcc/configure index ce05e0d..4919a56 100755 --- a/libgcc/configure +++ b/libgcc/configure @@ -5265,10 +5265,10 @@ $as_echo "$libgcc_cv_powerpc_float128_hw" >&6; } CFLAGS="$saved_CFLAGS" saved_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -mpower10 -mfloat128-hardware" + CFLAGS="$CFLAGS -mcpu=power10 -mfloat128-hardware" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PowerPC ISA 3.1 to build hardware __float128 libraries" >&5 $as_echo_n "checking for PowerPC ISA 3.1 to build hardware __float128 libraries... " >&6; } -if ${libgcc_cv_powerpc_float128_hw+:} false; then : +if ${libgcc_cv_powerpc_3_1_float128_hw+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -5280,15 +5280,15 @@ else #ifndef __BUILTIN_CPU_SUPPORTS__ #error "__builtin_cpu_supports is not available" #endif - vector unsigned char add (vector unsigned char a, vector unsigned char b) + vector unsigned char conv (vector unsigned char qs) { vector unsigned char ret; - __asm__ ("xscvsqqp %0,%1,%2" : "=v" (ret) : "v" (a), "v" (b)); + __asm__ ("xscvsqqp %0,%1" : "=v" (ret) : "v" (qs)); return ret; } - void *add_resolver (void) { return (void *) add; } - __float128 add_ifunc (__float128, __float128) - __attribute__ ((__ifunc__ ("add_resolver"))); + void *conv_resolver (void) { return (void *) conv; } + __float128 conv_ifunc (__float128) + __attribute__ ((__ifunc__ ("conv_resolver"))); _ACEOF if ac_fn_c_try_compile "$LINENO"; then : libgcc_cv_powerpc_3_1_float128_hw=yes @@ -5297,8 +5297,8 @@ else fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_powerpc_float128_hw" >&5 - $as_echo "$libgcc_cv_powerpc_float128_hw" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libgcc_cv_powerpc_3_1_float128_hw" >&5 +$as_echo "$libgcc_cv_powerpc_3_1_float128_hw" >&6; } CFLAGS="$saved_CFLAGS" esac diff --git a/libgcc/configure.ac b/libgcc/configure.ac index bc315de..13a80b2 100644 --- a/libgcc/configure.ac +++ b/libgcc/configure.ac @@ -460,9 +460,9 @@ powerpc*-*-linux*) CFLAGS="$saved_CFLAGS" saved_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -mpower10 -mfloat128-hardware" + CFLAGS="$CFLAGS -mcpu=power10 -mfloat128-hardware" AC_CACHE_CHECK([for PowerPC ISA 3.1 to build hardware __float128 libraries], - [libgcc_cv_powerpc_float128_hw], + [libgcc_cv_powerpc_3_1_float128_hw], [AC_COMPILE_IFELSE( [AC_LANG_SOURCE([#include <sys/auxv.h> #ifndef AT_PLATFORM @@ -471,15 +471,15 @@ powerpc*-*-linux*) #ifndef __BUILTIN_CPU_SUPPORTS__ #error "__builtin_cpu_supports is not available" #endif - vector unsigned char add (vector unsigned char a, vector unsigned char b) + vector unsigned char conv (vector unsigned char qs) { vector unsigned char ret; - __asm__ ("xscvsqqp %0,%1,%2" : "=v" (ret) : "v" (a), "v" (b)); + __asm__ ("xscvsqqp %0,%1" : "=v" (ret) : "v" (qs)); return ret; } - void *add_resolver (void) { return (void *) add; } - __float128 add_ifunc (__float128, __float128) - __attribute__ ((__ifunc__ ("add_resolver")));])], + void *conv_resolver (void) { return (void *) conv; } + __float128 conv_ifunc (__float128) + __attribute__ ((__ifunc__ ("conv_resolver")));])], [libgcc_cv_powerpc_3_1_float128_hw=yes], [libgcc_cv_powerpc_3_1_float128_hw=no])]) CFLAGS="$saved_CFLAGS" diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index 97bc035..b763a22 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,10 @@ +2021-06-22 Sandra Loosemore <sandra@codesourcery.com> + Tobias Burnus <tobias@codesourcery.com> + + PR fortran/93524 + * runtime/ISO_Fortran_binding.c (CFI_allocate): Fix + sm computation. + 2021-06-08 Martin Liska <mliska@suse.cz> * intrinsics/chmod.c (chmod_internal): Fix typo. diff --git a/libgfortran/runtime/ISO_Fortran_binding.c b/libgfortran/runtime/ISO_Fortran_binding.c index 20833ad..0978832 100644 --- a/libgfortran/runtime/ISO_Fortran_binding.c +++ b/libgfortran/runtime/ISO_Fortran_binding.c @@ -254,10 +254,7 @@ CFI_allocate (CFI_cdesc_t *dv, const CFI_index_t lower_bounds[], { dv->dim[i].lower_bound = lower_bounds[i]; dv->dim[i].extent = upper_bounds[i] - dv->dim[i].lower_bound + 1; - if (i == 0) - dv->dim[i].sm = dv->elem_len; - else - dv->dim[i].sm = dv->elem_len * dv->dim[i - 1].extent; + dv->dim[i].sm = dv->elem_len * arr_len; arr_len *= dv->dim[i].extent; } } diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index f652c4d..c1c04be 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,44 @@ +2021-06-22 Jonathan Wakely <jwakely@redhat.com> + Matthias Kretz <m.kretz@gsi.de> + + * include/std/mutex (lock): Replace recursion with iteration + when lockables all have the same type. + (__detail::__try_lock_impl): Likewise. Pass lockables as + parameters, instead of a tuple. Always lock the first one, and + recurse for the rest. + (__detail::__lock_impl): Adjust call to __try_lock_impl. + (__detail::__try_to_lock): Remove. + * testsuite/30_threads/lock/3.cc: Check that mutexes are locked. + * testsuite/30_threads/lock/4.cc: Also test non-heterogeneous + arguments. + * testsuite/30_threads/unique_lock/cons/60497.cc: Also check + std::try_lock. + * testsuite/30_threads/try_lock/5.cc: New test. + +2021-06-22 Jonathan Wakely <jwakely@redhat.com> + + * include/std/memory (declare_reachable, undeclare_reachable) + (declare_no_pointers, undeclare_no_pointers, get_pointer_safety) + (pointer_safety): Only define for C++11 to C++20 inclusive. + * testsuite/20_util/pointer_safety/1.cc: Do not run for C++23. + +2021-06-22 Jonathan Wakely <jwakely@redhat.com> + + * include/bits/random.h (seed_seq): Constrain initializer-list + constructor. + * include/bits/random.tcc (seed_seq): Add template parameter. + * testsuite/26_numerics/random/seed_seq/cons/default.cc: Check + for noexcept. + * testsuite/26_numerics/random/seed_seq/cons/initlist.cc: Check + constraints. + +2021-06-22 Thomas Rodgers <rodgert@appliantology.com> + + PR libstdc++/100806 + * include/bits/semaphore_base.h (__atomic_semaphore::_M_release): + Force _M_release() to wake all waiting threads. + * testsuite/30_threads/semaphore/100806.cc: New test. + 2021-06-21 Jonathan Wakely <jwakely@redhat.com> * include/std/mutex (__try_to_lock): Move to __detail namespace. diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h index 0da013c..ed0d7a8 100644 --- a/libstdc++-v3/include/bits/random.h +++ b/libstdc++-v3/include/bits/random.h @@ -6073,7 +6073,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : _M_v() { } - template<typename _IntType> + template<typename _IntType, typename = _Require<is_integral<_IntType>>> seed_seq(std::initializer_list<_IntType> __il); template<typename _InputIterator> diff --git a/libstdc++-v3/include/bits/random.tcc b/libstdc++-v3/include/bits/random.tcc index 1357e18..8e2b702 100644 --- a/libstdc++-v3/include/bits/random.tcc +++ b/libstdc++-v3/include/bits/random.tcc @@ -3231,7 +3231,7 @@ namespace __detail } - template<typename _IntType> + template<typename _IntType, typename> seed_seq::seed_seq(std::initializer_list<_IntType> __il) { for (auto __iter = __il.begin(); __iter != __il.end(); ++__iter) diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h index 9a55978..c4565d7 100644 --- a/libstdc++-v3/include/bits/semaphore_base.h +++ b/libstdc++-v3/include/bits/semaphore_base.h @@ -256,7 +256,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__update > 1) __atomic_notify_address_bare(&_M_counter, true); else - __atomic_notify_address_bare(&_M_counter, false); + __atomic_notify_address_bare(&_M_counter, true); +// FIXME - Figure out why this does not wake a waiting thread +// __atomic_notify_address_bare(&_M_counter, false); } private: diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index f19de27..da64be2 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -87,7 +87,7 @@ # include <bits/uses_allocator_args.h> #endif -#if __cplusplus >= 201103L +#if __cplusplus >= 201103L && __cplusplus <= 202002L namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -132,7 +132,7 @@ get_pointer_safety() noexcept { return pointer_safety::relaxed; } _GLIBCXX_END_NAMESPACE_VERSION } // namespace -#endif // C++11 +#endif // C++11 to C++20 #if __cplusplus >= 201703L // Parallel STL algorithms diff --git a/libstdc++-v3/include/std/mutex b/libstdc++-v3/include/std/mutex index 5f2d8f9..c18ca1a 100644 --- a/libstdc++-v3/include/std/mutex +++ b/libstdc++-v3/include/std/mutex @@ -514,39 +514,61 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @cond undocumented namespace __detail { + // Lock the last lockable, after all previous ones are locked. template<typename _Lockable> - inline unique_lock<_Lockable> - __try_to_lock(_Lockable& __l) - { return unique_lock<_Lockable>{__l, try_to_lock}; } - - // Lock the last element of the tuple, after all previous ones are locked. - template<int _Idx, typename... _Lockables> - inline __enable_if_t<_Idx + 1 == sizeof...(_Lockables), int> - __try_lock_impl(tuple<_Lockables&...>& __lockables) + inline int + __try_lock_impl(_Lockable& __lockable) { - if (auto __lock = __detail::__try_to_lock(std::get<_Idx>(__lockables))) + if (unique_lock<_Lockable> __lock{__lockable, try_to_lock}) { __lock.release(); return -1; } else - return _Idx; + return 0; } - // Lock tuple elements starting from _Idx. - template<int _Idx, typename... _Lockables> - inline __enable_if_t<_Idx + 1 != sizeof...(_Lockables), int> - __try_lock_impl(tuple<_Lockables&...>& __lockables) + // Lock each lockable in turn. + // Use iteration if all lockables are the same type, recursion otherwise. + template<typename _L0, typename... _Lockables> + inline int + __try_lock_impl(_L0& __l0, _Lockables&... __lockables) { - if (auto __lock = __detail::__try_to_lock(std::get<_Idx>(__lockables))) +#if __cplusplus >= 201703L + if constexpr ((is_same_v<_L0, _Lockables> && ...)) { - int __idx = __detail::__try_lock_impl<_Idx + 1>(__lockables); + constexpr int _Np = 1 + sizeof...(_Lockables); + unique_lock<_L0> __locks[_Np] = { + {__l0, defer_lock}, {__lockables, defer_lock}... + }; + for (int __i = 0; __i < _Np; ++__i) + { + if (!__locks[__i].try_lock()) + { + const int __failed = __i; + while (__i--) + __locks[__i].unlock(); + return __failed; + } + } + for (auto& __l : __locks) + __l.release(); + return -1; + } + else +#endif + if (unique_lock<_L0> __lock{__l0, try_to_lock}) + { + int __idx = __detail::__try_lock_impl(__lockables...); if (__idx == -1) - __lock.release(); - return __idx; + { + __lock.release(); + return -1; + } + return __idx + 1; } else - return _Idx; + return 0; } } // namespace __detail @@ -562,12 +584,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * * Sequentially calls try_lock() on each argument. */ - template<typename _Lock1, typename _Lock2, typename... _Lock3> + template<typename _L1, typename _L2, typename... _L3> int - try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3) + try_lock(_L1& __l1, _L2& __l2, _L3&... __l3) { - auto __lockables = std::tie(__l1, __l2, __l3...); - return __detail::__try_lock_impl<0>(__lockables); + return __detail::__try_lock_impl(__l1, __l2, __l3...); } /// @cond undocumented @@ -589,8 +610,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int __failed = 1; // index that couldn't be locked { unique_lock<_L0> __first(__l0); - auto __rest = std::tie(__l1...); - __failed += __detail::__try_lock_impl<0>(__rest); + __failed += __detail::__try_lock_impl(__l1...); if (!__failed) { __i = -1; // finished @@ -620,15 +640,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @post All arguments are locked. * * All arguments are locked via a sequence of calls to lock(), try_lock() - * and unlock(). If the call exits via an exception any locks that were - * obtained will be released. + * and unlock(). If this function exits via an exception any locks that + * were obtained will be released. */ template<typename _L1, typename _L2, typename... _L3> void lock(_L1& __l1, _L2& __l2, _L3&... __l3) { - int __i = 0; - __detail::__lock_impl(__i, 0, __l1, __l2, __l3...); +#if __cplusplus >= 201703L + if constexpr (is_same_v<_L1, _L2> && (is_same_v<_L1, _L3> && ...)) + { + constexpr int _Np = 2 + sizeof...(_L3); + unique_lock<_L1> __locks[] = { + {__l1, defer_lock}, {__l2, defer_lock}, {__l3, defer_lock}... + }; + int __first = 0; + do { + __locks[__first].lock(); + for (int __j = 1; __j < _Np; ++__j) + { + const int __idx = (__first + __j) % _Np; + if (!__locks[__idx].try_lock()) + { + for (int __k = __j; __k != 0; --__k) + __locks[(__first + __k - 1) % _Np].unlock(); + __first = __idx; + break; + } + } + } while (!__locks[__first].owns_lock()); + + for (auto& __l : __locks) + __l.release(); + } + else +#endif + { + int __i = 0; + __detail::__lock_impl(__i, 0, __l1, __l2, __l3...); + } } #if __cplusplus >= 201703L diff --git a/libstdc++-v3/testsuite/20_util/pointer_safety/1.cc b/libstdc++-v3/testsuite/20_util/pointer_safety/1.cc index 7d9a425..bfacbce 100644 --- a/libstdc++-v3/testsuite/20_util/pointer_safety/1.cc +++ b/libstdc++-v3/testsuite/20_util/pointer_safety/1.cc @@ -15,7 +15,7 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// { dg-do run { target c++11 } } +// { dg-do run { target { c++11 && { ! c++23 } } } } #include <memory> #include <testsuite_hooks.h> diff --git a/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/default.cc index 18f55e7..62434a6 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/default.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/default.cc @@ -25,6 +25,9 @@ #include <random> #include <testsuite_hooks.h> +static_assert( std::is_nothrow_default_constructible<std::seed_seq>::value, + "LWG 3422" ); + void test01() { @@ -34,7 +37,6 @@ test01() seq.generate(foo.begin(), foo.end()); VERIFY( seq.size() == 0 ); - //VERIFY(); } int diff --git a/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/initlist.cc b/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/initlist.cc index 44b855e..1ed9eb7 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/initlist.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/seed_seq/cons/initlist.cc @@ -36,6 +36,13 @@ test01() VERIFY( seq.size() == 10 ); } +void +lwg3422() +{ + int i[32] = { }; + std::seed_seq ss{i, i+32}; // LWG 3422 +} + int main() { test01(); diff --git a/libstdc++-v3/testsuite/30_threads/lock/3.cc b/libstdc++-v3/testsuite/30_threads/lock/3.cc index 5213607..5fa1187 100644 --- a/libstdc++-v3/testsuite/30_threads/lock/3.cc +++ b/libstdc++-v3/testsuite/30_threads/lock/3.cc @@ -37,7 +37,7 @@ struct user_lock is_locked = true; } - bool try_lock() + bool try_lock() { return is_locked ? false : (is_locked = true); } void unlock() @@ -62,6 +62,8 @@ int main() { //heterogeneous types std::lock(m1, m2, m3); + VERIFY( !m1.try_lock() ); + VERIFY( !m3.try_lock() ); m1.unlock(); m2.unlock(); m3.unlock(); diff --git a/libstdc++-v3/testsuite/30_threads/lock/4.cc b/libstdc++-v3/testsuite/30_threads/lock/4.cc index 7ba15cb..130c1f6 100644 --- a/libstdc++-v3/testsuite/30_threads/lock/4.cc +++ b/libstdc++-v3/testsuite/30_threads/lock/4.cc @@ -73,6 +73,7 @@ int unreliable_lock::lock_on = -1; void test01() { unreliable_lock l1, l2, l3; + std::mutex m1, m2, m3; try { @@ -87,6 +88,60 @@ void test01() { VERIFY( false ); } + + // Repeat with non-heterogeneous arguments + + try + { + unreliable_lock::count = 0; + std::lock(l1, l2, l3, m1); + VERIFY( unreliable_lock::count == 3 ); + l1.unlock(); + l2.unlock(); + l3.unlock(); + VERIFY( !m1.try_lock() ); // already locked + m1.unlock(); + } + catch (...) + { + VERIFY( false ); + } + + try + { + unreliable_lock::count = 0; + std::lock(m1, l1, l2, l3); + VERIFY( unreliable_lock::count == 3 ); + VERIFY( !m1.try_lock() ); // already locked + m1.unlock(); + l1.unlock(); + l2.unlock(); + l3.unlock(); + } + catch (...) + { + VERIFY( false ); + } + + try + { + unreliable_lock::count = 0; + std::lock(l1, m1, l2, m2, l3, m3); + VERIFY( unreliable_lock::count == 3 ); + l1.unlock(); + l2.unlock(); + l3.unlock(); + VERIFY( !m1.try_lock() ); // already locked + VERIFY( !m2.try_lock() ); // already locked + VERIFY( !m3.try_lock() ); // already locked + m1.unlock(); + m2.unlock(); + m3.unlock(); + } + catch (...) + { + VERIFY( false ); + } } void test02() @@ -111,6 +166,31 @@ void test02() { VERIFY( false ); } + + // Repeat with non-heterogeneous arguments + + try + { + unreliable_lock::lock_on = 1; + while (unreliable_lock::lock_on < 3) + { + unreliable_lock::count = 0; + unreliable_lock l1, l2, l3; + std::mutex m1; + std::lock(l1, l2, l3, m1); + VERIFY( unreliable_lock::count > 3 ); + l1.unlock(); + l2.unlock(); + l3.unlock(); + VERIFY( !m1.try_lock() ); // already locked + m1.unlock(); + ++unreliable_lock::lock_on; + } + } + catch (...) + { + VERIFY( false ); + } } void test03() @@ -133,6 +213,50 @@ void test03() VERIFY( test ); ++unreliable_lock::throw_on; } + + // Repeat with non-heterogeneous arguments + + unreliable_lock::throw_on = 0; + while (unreliable_lock::throw_on < 3) + { + unreliable_lock::count = 0; + unreliable_lock l1, l2, l3; + std::mutex m1; + bool test = false; + try + { + std::lock(l1, l2, l3, m1); + } + catch (...) + { + test = true; + } + VERIFY( test ); + VERIFY( m1.try_lock() ); // m1 was not left locked by failed std::lock + m1.unlock(); + ++unreliable_lock::throw_on; + } + + unreliable_lock::throw_on = 0; + while (unreliable_lock::throw_on < 3) + { + unreliable_lock::count = 0; + unreliable_lock l1, l2, l3; + std::mutex m1; + bool test = false; + try + { + std::lock(m1, l1, l2, l3); + } + catch (...) + { + test = true; + } + VERIFY( test ); + VERIFY( m1.try_lock() ); // m1 was not left locked by failed std::lock + m1.unlock(); + ++unreliable_lock::throw_on; + } } int main() diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc new file mode 100644 index 0000000..2fa2628 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc @@ -0,0 +1,57 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } +// { dg-add-options libatomic } + +#include <iostream> +#include <sstream> + +#include <thread> +#include <semaphore> +#include <mutex> +#include <chrono> +#include <vector> + +std::counting_semaphore<4> semaphore{6}; + +std::mutex mtx; +std::vector<std::string> results; + +void thread_main(size_t x) +{ + semaphore.acquire(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + semaphore.release(); + { + std::ostringstream stm; + stm << "Thread " << x << " finished."; + std::lock_guard g{ mtx }; + results.push_back(stm.str()); + } +} + +int main() +{ + constexpr auto nthreads = 10; + + std::vector<std::thread> threads(nthreads); + + size_t counter{0}; + for(auto& t : threads) + { + t = std::thread(thread_main, counter++); + } + + for(auto& t : threads) + { + t.join(); + { + std::lock_guard g{ mtx }; + for (auto&& r : results) + std::cout << r << '\n'; + std::cout.flush(); + results.clear(); + } + } +} diff --git a/libstdc++-v3/testsuite/30_threads/try_lock/5.cc b/libstdc++-v3/testsuite/30_threads/try_lock/5.cc new file mode 100644 index 0000000..a5574ff --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/try_lock/5.cc @@ -0,0 +1,41 @@ +// { dg-do run { target c++11 } } + +#include <mutex> +#include <testsuite_hooks.h> + +struct Lockable +{ + static int tries; + + void lock() { } + void unlock() { } + bool try_lock() { return ++tries != 2; } +}; + +int Lockable::tries = 0; + +void test01() +{ + Lockable l1, l2, l3; + std::mutex m1, m2; + + VERIFY( std::try_lock(l1, l2, l3) == 1 ); + VERIFY( Lockable::tries == 2 ); + + Lockable::tries = 0; + VERIFY( std::try_lock(m1, l1, l2, l3) == 2 ); + VERIFY( Lockable::tries == 2 ); + + Lockable::tries = 0; + VERIFY( std::try_lock(l1, l2, l3, m1) == 1 ); + VERIFY( Lockable::tries == 2 ); + + Lockable::tries = 0; + VERIFY( std::try_lock(m1, l1, l2, l3, m2) == 2 ); + VERIFY( Lockable::tries == 2 ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/30_threads/unique_lock/cons/60497.cc b/libstdc++-v3/testsuite/30_threads/unique_lock/cons/60497.cc index 8603e56..08698ce 100644 --- a/libstdc++-v3/testsuite/30_threads/unique_lock/cons/60497.cc +++ b/libstdc++-v3/testsuite/30_threads/unique_lock/cons/60497.cc @@ -46,3 +46,9 @@ void test02() test_type l1, l2, l3; std::lock(l1, l2, l3); } + +void test03() +{ + test_type l1, l2, l3; + std::try_lock(l1, l2, l3); +} |