aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2022-07-26 10:04:01 +0200
committerMartin Liska <mliska@suse.cz>2022-07-26 10:04:01 +0200
commit8f694e3f186d734a20ab870cd72e98646142810a (patch)
tree082a4a95e89bfb0dd888876209099060c715709d
parent9630093982a263fd86dc7c87afbb0afed89c9d47 (diff)
parentbb04f9f23ac0dee2c003118c85372ece50a52220 (diff)
downloadgcc-8f694e3f186d734a20ab870cd72e98646142810a.zip
gcc-8f694e3f186d734a20ab870cd72e98646142810a.tar.gz
gcc-8f694e3f186d734a20ab870cd72e98646142810a.tar.bz2
Merge branch 'master' into devel/sphinx
-rw-r--r--gcc/ChangeLog120
-rw-r--r--gcc/DATESTAMP2
-rw-r--r--gcc/Makefile.in1
-rw-r--r--gcc/analyzer/ChangeLog4
-rw-r--r--gcc/analyzer/sm-fd.cc2114
-rw-r--r--gcc/common/config/loongarch/loongarch-common.cc1
-rw-r--r--gcc/config/aarch64/aarch64-builtins.cc71
-rw-r--r--gcc/config/aarch64/aarch64.md13
-rw-r--r--gcc/config/aarch64/arm_acle.h53
-rw-r--r--gcc/config/i386/i386-expand.cc40
-rw-r--r--gcc/config/i386/i386-protos.h1
-rw-r--r--gcc/config/i386/i386.md140
-rw-r--r--gcc/config/loongarch/constraints.md24
-rw-r--r--gcc/config/loongarch/genopts/loongarch.opt.in4
-rw-r--r--gcc/config/loongarch/loongarch-opts.cc7
-rw-r--r--gcc/config/loongarch/loongarch-protos.h10
-rw-r--r--gcc/config/loongarch/loongarch.cc652
-rw-r--r--gcc/config/loongarch/loongarch.h2
-rw-r--r--gcc/config/loongarch/loongarch.md401
-rw-r--r--gcc/config/loongarch/loongarch.opt4
-rw-r--r--gcc/config/loongarch/predicates.md56
-rw-r--r--gcc/config/rs6000/rs6000-p8swap.cc20
-rw-r--r--gcc/config/rs6000/rtems.h3
-rw-r--r--gcc/cp/ChangeLog10
-rw-r--r--gcc/cp/class.cc11
-rw-r--r--gcc/cp/semantics.cc5
-rw-r--r--gcc/gimple-array-bounds.cc2
-rw-r--r--gcc/gimple-range.cc4
-rw-r--r--gcc/match.pd3
-rw-r--r--gcc/range-op-float.cc206
-rw-r--r--gcc/range-op.cc153
-rw-r--r--gcc/range-op.h69
-rw-r--r--gcc/testsuite/ChangeLog42
-rw-r--r--gcc/testsuite/c-c++-common/attr-fd.c36
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/range-for38.C16
-rw-r--r--gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C7
-rw-r--r--gcc/testsuite/g++.target/i386/pr91681.C20
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-2.c96
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-3.c168
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-4.c142
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fd-5.c104
-rw-r--r--gcc/testsuite/gcc.dg/pr106189.c5
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr106414-1.c12
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr106414-2.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c468
-rw-r--r--gcc/testsuite/gcc.target/i386/pr91681-1.c20
-rw-r--r--gcc/testsuite/gcc.target/i386/pr91681-2.c20
-rw-r--r--gcc/testsuite/gcc.target/i386/pr91681-3.c16
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-1.c32
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-2.c32
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-3.c32
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-4.c32
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-5.c33
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-6.c33
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-7.c34
-rw-r--r--gcc/testsuite/gcc.target/loongarch/func-call-8.c33
-rw-r--r--gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c23
-rw-r--r--gcc/testsuite/gcc.target/powerpc/pr106091.c15
-rw-r--r--gcc/value-query.cc1
59 files changed, 3766 insertions, 1924 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b090352..258cc48 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,123 @@
+2022-07-25 Aldy Hernandez <aldyh@redhat.com>
+
+ * Makefile.in (OBJS): Add range-op-float.o.
+ * range-op.cc (get_float_handler): New.
+ (range_op_handler::range_op_handler): Save code and type for
+ delayed querying.
+ (range_op_handler::oeprator bool): Move from header file, and
+ add support for floats.
+ (range_op_handler::fold_range): Add support for floats.
+ (range_op_handler::op1_range): Same.
+ (range_op_handler::op2_range): Same.
+ (range_op_handler::lhs_op1_relation): Same.
+ (range_op_handler::lhs_op2_relation): Same.
+ (range_op_handler::op1_op2_relation): Same.
+ * range-op.h (class range_operator_float): New.
+ (class floating_op_table): New.
+ * value-query.cc (range_query::get_tree_range): Add case for
+ REAL_CST.
+ * range-op-float.cc: New file.
+
+2022-07-25 Roger Sayle <roger@nextmovesoftware.com>
+ Uroš Bizjak <ubizjak@gmail.com>
+
+ PR target/91681
+ * config/i386/i386-expand.cc (split_double_concat): A new helper
+ function for setting a double word value from two word values.
+ * config/i386/i386-protos.h (split_double_concat): Prototype here.
+ * config/i386/i386.md (zero_extendditi2): New define_insn_and_split.
+ (*add<dwi>3_doubleword_zext): New define_insn_and_split.
+ (*sub<dwi>3_doubleword_zext): New define_insn_and_split.
+ (*concat<mode><dwi>3_1): New define_insn_and_split replacing
+ previous define_split for implementing DST = (HI<<32)|LO as
+ pair of move instructions, setting lopart and hipart.
+ (*concat<mode><dwi>3_2): Likewise.
+ (*concat<mode><dwi>3_3): Likewise, where HI is zero_extended.
+ (*concat<mode><dwi>3_4): Likewise, where HI is zero_extended.
+
+2022-07-25 Aldy Hernandez <aldyh@redhat.com>
+
+ PR middle-end/106432
+ * gimple-range.cc (gimple_ranger::range_on_edge): Return false
+ when the result range type is unsupported.
+
+2022-07-25 Sebastian Huber <sebastian.huber@embedded-brains.de>
+
+ * config/rs6000/rtems.h (CPLUSPLUS_CPP_SPEC): Undef.
+
+2022-07-25 Richard Biener <rguenther@suse.de>
+
+ PR middle-end/106414
+ * match.pd (~(x ^ y) -> x == y): Restrict to single bit
+ precision types.
+
+2022-07-25 Andre Vieira <andre.simoesdiasvieira@arm.com>
+
+ * config/aarch64/aarch64.md (rbit<mode>2): Rename this ...
+ (@aarch64_rbit<mode>): ... to this and change it in...
+ (ffs<mode>2,ctz<mode>2): ... here.
+ (@aarch64_rev16<mode>): New.
+ * config/aarch64/aarch64-builtins.cc: (aarch64_builtins):
+ Define the following enum AARCH64_REV16, AARCH64_REV16L,
+ AARCH64_REV16LL, AARCH64_RBIT, AARCH64_RBITL, AARCH64_RBITLL.
+ (aarch64_init_data_intrinsics): New.
+ (aarch64_general_init_builtins): Add call to
+ aarch64_init_data_intrinsics.
+ (aarch64_expand_builtin_data_intrinsic): New.
+ (aarch64_general_expand_builtin): Add call to
+ aarch64_expand_builtin_data_intrinsic.
+ * config/aarch64/arm_acle.h (__clz, __clzl, __clzll, __cls, __clsl,
+ __clsll, __rbit, __rbitl, __rbitll, __rev, __revl, __revll, __rev16,
+ __rev16l, __rev16ll, __ror, __rorl, __rorll, __revsh): New.
+
+2022-07-25 Martin Liska <mliska@suse.cz>
+
+ * doc/extend.texi: Remove trailing whitespaces.
+ * doc/invoke.texi: Likewise.
+
+2022-07-25 Aldy Hernandez <aldyh@redhat.com>
+
+ * value-range-pretty-print.cc (vrange_printer::visit): New.
+ (vrange_printer::print_frange_prop): New.
+ * value-range-pretty-print.h (class vrange_printer): Add visit and
+ print_frange_prop.
+ * value-range-storage.h (vrange_allocator::alloc_vrange): Handle frange.
+ (vrange_allocator::alloc_frange): New.
+ * value-range.cc (vrange::operator=): Handle frange.
+ (vrange::operator==): Same.
+ (frange::accept): New.
+ (frange::set): New.
+ (frange::normalize_kind): New.
+ (frange::union_): New.
+ (frange::intersect): New.
+ (frange::operator=): New.
+ (frange::operator==): New.
+ (frange::supports_type_p): New.
+ (frange::verify_range): New.
+ * value-range.h (enum value_range_discriminator): Handle frange.
+ (class fp_prop): New.
+ (FP_PROP_ACCESSOR): New.
+ (class frange_props): New.
+ (FRANGE_PROP_ACCESSOR): New.
+ (class frange): New.
+ (Value_Range::init): Handle frange.
+ (Value_Range::operator=): Same.
+ (Value_Range::supports_type_p): Same.
+ (frange_props::operator==): New.
+ (frange_props::union_): New.
+ (frange_props::intersect): New
+ (frange::frange): New.
+ (frange::type): New.
+ (frange::set_varying): New.
+ (frange::set_undefined): New.
+
+2022-07-25 Peter Bergner <bergner@linux.ibm.com>
+ Kewen Lin <linkw@linux.ibm.com>
+
+ PR testsuite/106345
+ * config/rs6000/rs6000.h (DRIVER_SELF_SPECS): Adjust -mdejagnu-cpu
+ to filter out all -mtune options.
+
2022-07-24 Aldy Hernandez <aldyh@redhat.com>
* value-query.cc (range_query::get_value_range): Add assert.
diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP
index c35b552..5d98481 100644
--- a/gcc/DATESTAMP
+++ b/gcc/DATESTAMP
@@ -1 +1 @@
-20220725
+20220726
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 001506f..203f0a1 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1548,6 +1548,7 @@ OBJS = \
profile-count.o \
range.o \
range-op.o \
+ range-op-float.o \
read-md.o \
read-rtl.o \
read-rtl-function.o \
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog
index 59642a6..00905b2 100644
--- a/gcc/analyzer/ChangeLog
+++ b/gcc/analyzer/ChangeLog
@@ -1,3 +1,7 @@
+2022-07-25 Martin Liska <mliska@suse.cz>
+
+ * sm-fd.cc: Run dos2unix and fix coding style issues.
+
2022-07-23 Immad Mir <mirimmad@outlook.com>
* sm-fd.cc (fd_param_diagnostic): New diagnostic class.
diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc
index c3dac48..56b0063 100644
--- a/gcc/analyzer/sm-fd.cc
+++ b/gcc/analyzer/sm-fd.cc
@@ -1,1057 +1,1057 @@
-/* A state machine for detecting misuses of POSIX file descriptor APIs.
- Copyright (C) 2019-2022 Free Software Foundation, Inc.
- Contributed by Immad Mir <mir@sourceware.org>.
-
-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 "tree.h"
-#include "function.h"
-#include "basic-block.h"
-#include "gimple.h"
-#include "options.h"
-#include "diagnostic-path.h"
-#include "diagnostic-metadata.h"
-#include "function.h"
-#include "json.h"
-#include "analyzer/analyzer.h"
-#include "diagnostic-event-id.h"
-#include "analyzer/analyzer-logging.h"
-#include "analyzer/sm.h"
-#include "analyzer/pending-diagnostic.h"
-#include "analyzer/function-set.h"
-#include "analyzer/analyzer-selftests.h"
-#include "tristate.h"
-#include "selftest.h"
-#include "stringpool.h"
-#include "attribs.h"
-#include "analyzer/call-string.h"
-#include "analyzer/program-point.h"
-#include "analyzer/store.h"
-#include "analyzer/region-model.h"
-#include "bitmap.h"
-
-#if ENABLE_ANALYZER
-
-namespace ana {
-
-namespace {
-
-/* An enum for distinguishing between three different access modes. */
-
-enum access_mode
-{
- READ_WRITE,
- READ_ONLY,
- WRITE_ONLY
-};
-
-enum access_directions
-{
- DIRS_READ_WRITE,
- DIRS_READ,
- DIRS_WRITE
-};
-
-class fd_state_machine : public state_machine
-{
-public:
- fd_state_machine (logger *logger);
-
- bool
- inherited_state_p () const final override
- {
- return false;
- }
-
- state_machine::state_t
- get_default_state (const svalue *sval) const final override
- {
- if (tree cst = sval->maybe_get_constant ())
- {
- if (TREE_CODE (cst) == INTEGER_CST)
- {
- int val = TREE_INT_CST_LOW (cst);
- if (val >= 0)
- return m_constant_fd;
- else
- return m_invalid;
- }
- }
- return m_start;
- }
-
- bool on_stmt (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt) const final override;
-
- void on_condition (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const svalue *lhs, const tree_code op,
- const svalue *rhs) const final override;
-
- bool can_purge_p (state_t s) const final override;
- pending_diagnostic *on_leak (tree var) const final override;
-
- bool is_unchecked_fd_p (state_t s) const;
- bool is_valid_fd_p (state_t s) const;
- bool is_closed_fd_p (state_t s) const;
- bool is_constant_fd_p (state_t s) const;
- bool is_readonly_fd_p (state_t s) const;
- bool is_writeonly_fd_p (state_t s) const;
- enum access_mode get_access_mode_from_flag (int flag) const;
-
- /* State for a constant file descriptor (>= 0) */
- state_t m_constant_fd;
-
- /* States representing a file descriptor that hasn't yet been
- checked for validity after opening, for three different
- access modes. */
- state_t m_unchecked_read_write;
-
- state_t m_unchecked_read_only;
-
- state_t m_unchecked_write_only;
-
- /* States for representing a file descriptor that is known to be valid (>=
- 0), for three different access modes.*/
- state_t m_valid_read_write;
-
- state_t m_valid_read_only;
-
- state_t m_valid_write_only;
-
- /* State for a file descriptor that is known to be invalid (< 0). */
- state_t m_invalid;
-
- /* State for a file descriptor that has been closed.*/
- state_t m_closed;
-
- /* State for a file descriptor that we do not want to track anymore . */
- state_t m_stop;
-
-private:
- void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call) const;
- void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call) const;
- void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call, const tree callee_fndecl) const;
- void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call, const tree callee_fndecl) const;
- void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call,
- const tree callee_fndecl,
- enum access_directions access_fn) const;
-
- void make_valid_transitions_on_condition (sm_context *sm_ctxt,
- const supernode *node,
- const gimple *stmt,
- const svalue *lhs) const;
- void make_invalid_transitions_on_condition (sm_context *sm_ctxt,
- const supernode *node,
- const gimple *stmt,
- const svalue *lhs) const;
- void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call,
- const tree callee_fndecl, const char *attr_name,
- access_directions fd_attr_access_dir) const;
-};
-
-/* Base diagnostic class relative to fd_state_machine. */
-class fd_diagnostic : public pending_diagnostic
-{
-public:
- fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
- {
- }
-
- bool
- subclass_equal_p (const pending_diagnostic &base_other) const override
- {
- return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
- }
-
- label_text
- describe_state_change (const evdesc::state_change &change) override
- {
- if (change.m_old_state == m_sm.get_start_state ()
- && m_sm.is_unchecked_fd_p (change.m_new_state))
- {
- if (change.m_new_state == m_sm.m_unchecked_read_write)
- return change.formatted_print ("opened here as read-write");
-
- if (change.m_new_state == m_sm.m_unchecked_read_only)
- return change.formatted_print ("opened here as read-only");
-
- if (change.m_new_state == m_sm.m_unchecked_write_only)
- return change.formatted_print ("opened here as write-only");
- }
-
- if (change.m_new_state == m_sm.m_closed)
- return change.formatted_print ("closed here");
-
- if (m_sm.is_unchecked_fd_p (change.m_old_state)
- && m_sm.is_valid_fd_p (change.m_new_state))
- {
- if (change.m_expr)
- return change.formatted_print (
- "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
- else
- return change.formatted_print ("assuming a valid file descriptor");
- }
-
- if (m_sm.is_unchecked_fd_p (change.m_old_state)
- && change.m_new_state == m_sm.m_invalid)
- {
- if (change.m_expr)
- return change.formatted_print (
- "assuming %qE is an invalid file descriptor (< 0)",
- change.m_expr);
- else
- return change.formatted_print ("assuming an invalid file descriptor");
- }
-
- return label_text ();
- }
-
-protected:
- const fd_state_machine &m_sm;
- tree m_arg;
-};
-
-class fd_param_diagnostic : public fd_diagnostic
-{
-public:
- fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
- const char *attr_name, int arg_idx)
- : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
- m_attr_name (attr_name), m_arg_idx (arg_idx)
- {
- }
-
- fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
- : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
- m_attr_name (NULL), m_arg_idx (-1)
- {
- }
-
- bool
- subclass_equal_p (const pending_diagnostic &base_other) const override
- {
- const fd_param_diagnostic &sub_other
- = (const fd_param_diagnostic &)base_other;
- return (same_tree_p (m_arg, sub_other.m_arg)
- && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
- && m_arg_idx == sub_other.m_arg_idx
- && ((m_attr_name)
- ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
- : true));
- }
-
- void
- inform_filedescriptor_attribute (access_directions fd_dir)
- {
-
- if (m_attr_name)
- switch (fd_dir)
- {
- case DIRS_READ_WRITE:
- inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
- "argument %d of %qD must be an open file descriptor, due to "
- "%<__attribute__((%s(%d)))%>",
- m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
- break;
- case DIRS_WRITE:
- inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
- "argument %d of %qD must be a readable file descriptor, due "
- "to %<__attribute__((%s(%d)))%>",
- m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
- break;
- case DIRS_READ:
- inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
- "argument %d of %qD must be a writable file descriptor, due "
- "to %<__attribute__((%s(%d)))%>",
- m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
- break;
- }
- }
-
-protected:
- tree m_callee_fndecl;
- const char *m_attr_name;
- /* ARG_IDX is 0-based. */
- int m_arg_idx;
-};
-
-class fd_leak : public fd_diagnostic
-{
-public:
- fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
-
- const char *
- get_kind () const final override
- {
- return "fd_leak";
- }
-
- int
- get_controlling_option () const final override
- {
- return OPT_Wanalyzer_fd_leak;
- }
-
- bool
- emit (rich_location *rich_loc) final override
- {
- /*CWE-775: Missing Release of File Descriptor or Handle after Effective
- Lifetime
- */
- diagnostic_metadata m;
- m.add_cwe (775);
- if (m_arg)
- return warning_meta (rich_loc, m, get_controlling_option (),
- "leak of file descriptor %qE", m_arg);
- else
- return warning_meta (rich_loc, m, get_controlling_option (),
- "leak of file descriptor");
- }
-
- label_text
- describe_state_change (const evdesc::state_change &change) final override
- {
- if (m_sm.is_unchecked_fd_p (change.m_new_state))
- {
- m_open_event = change.m_event_id;
- return label_text::borrow ("opened here");
- }
-
- return fd_diagnostic::describe_state_change (change);
- }
-
- label_text
- describe_final_event (const evdesc::final_event &ev) final override
- {
- if (m_open_event.known_p ())
- {
- if (ev.m_expr)
- return ev.formatted_print ("%qE leaks here; was opened at %@",
- ev.m_expr, &m_open_event);
- else
- return ev.formatted_print ("leaks here; was opened at %@",
- &m_open_event);
- }
- else
- {
- if (ev.m_expr)
- return ev.formatted_print ("%qE leaks here", ev.m_expr);
- else
- return ev.formatted_print ("leaks here");
- }
- }
-
-private:
- diagnostic_event_id_t m_open_event;
-};
-
-class fd_access_mode_mismatch : public fd_param_diagnostic
-{
-public:
- fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
- enum access_directions fd_dir,
- const tree callee_fndecl, const char *attr_name,
- int arg_idx)
- : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
- m_fd_dir (fd_dir)
-
- {
- }
-
- fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
- enum access_directions fd_dir,
- const tree callee_fndecl)
- : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
- {
- }
-
- const char *
- get_kind () const final override
- {
- return "fd_access_mode_mismatch";
- }
-
- int
- get_controlling_option () const final override
- {
- return OPT_Wanalyzer_fd_access_mode_mismatch;
- }
-
- bool
- emit (rich_location *rich_loc) final override
- {
- bool warned;
- switch (m_fd_dir)
- {
- case DIRS_READ:
- warned = warning_at (rich_loc, get_controlling_option (),
- "%qE on read-only file descriptor %qE",
- m_callee_fndecl, m_arg);
- break;
- case DIRS_WRITE:
- warned = warning_at (rich_loc, get_controlling_option (),
- "%qE on write-only file descriptor %qE",
- m_callee_fndecl, m_arg);
- break;
- default:
- gcc_unreachable ();
- }
- if (warned)
- inform_filedescriptor_attribute (m_fd_dir);
- return warned;
- }
-
- label_text
- describe_final_event (const evdesc::final_event &ev) final override
- {
- switch (m_fd_dir)
- {
- case DIRS_READ:
- return ev.formatted_print ("%qE on read-only file descriptor %qE",
- m_callee_fndecl, m_arg);
- case DIRS_WRITE:
- return ev.formatted_print ("%qE on write-only file descriptor %qE",
- m_callee_fndecl, m_arg);
- default:
- gcc_unreachable ();
- }
- }
-
-private:
- enum access_directions m_fd_dir;
-};
-
-class fd_double_close : public fd_diagnostic
-{
-public:
- fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
- {
- }
-
- const char *
- get_kind () const final override
- {
- return "fd_double_close";
- }
-
- int
- get_controlling_option () const final override
- {
- return OPT_Wanalyzer_fd_double_close;
- }
- bool
- emit (rich_location *rich_loc) final override
- {
- diagnostic_metadata m;
- // CWE-1341: Multiple Releases of Same Resource or Handle
- m.add_cwe (1341);
- return warning_meta (rich_loc, m, get_controlling_option (),
- "double %<close%> of file descriptor %qE", m_arg);
- }
-
- label_text
- describe_state_change (const evdesc::state_change &change) override
- {
- if (m_sm.is_unchecked_fd_p (change.m_new_state))
- return label_text::borrow ("opened here");
-
- if (change.m_new_state == m_sm.m_closed)
- {
- m_first_close_event = change.m_event_id;
- return change.formatted_print ("first %qs here", "close");
- }
- return fd_diagnostic::describe_state_change (change);
- }
-
- label_text
- describe_final_event (const evdesc::final_event &ev) final override
- {
- if (m_first_close_event.known_p ())
- return ev.formatted_print ("second %qs here; first %qs was at %@",
- "close", "close", &m_first_close_event);
- return ev.formatted_print ("second %qs here", "close");
- }
-
-private:
- diagnostic_event_id_t m_first_close_event;
-};
-
-class fd_use_after_close : public fd_param_diagnostic
-{
-public:
- fd_use_after_close (const fd_state_machine &sm, tree arg,
- const tree callee_fndecl, const char *attr_name,
- int arg_idx)
- : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
- {
- }
-
- fd_use_after_close (const fd_state_machine &sm, tree arg,
- const tree callee_fndecl)
- : fd_param_diagnostic (sm, arg, callee_fndecl)
- {
- }
-
- const char *
- get_kind () const final override
- {
- return "fd_use_after_close";
- }
-
- int
- get_controlling_option () const final override
- {
- return OPT_Wanalyzer_fd_use_after_close;
- }
-
- bool
- emit (rich_location *rich_loc) final override
- {
- bool warned;
- warned = warning_at (rich_loc, get_controlling_option (),
- "%qE on closed file descriptor %qE", m_callee_fndecl,
- m_arg);
- if (warned)
- inform_filedescriptor_attribute (DIRS_READ_WRITE);
- return warned;
- }
-
- label_text
- describe_state_change (const evdesc::state_change &change) override
- {
- if (m_sm.is_unchecked_fd_p (change.m_new_state))
- return label_text::borrow ("opened here");
-
- if (change.m_new_state == m_sm.m_closed)
- {
- m_first_close_event = change.m_event_id;
- return change.formatted_print ("closed here");
- }
-
- return fd_diagnostic::describe_state_change (change);
- }
-
- label_text
- describe_final_event (const evdesc::final_event &ev) final override
- {
- if (m_first_close_event.known_p ())
- return ev.formatted_print (
- "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
- m_arg, "close", &m_first_close_event);
- else
- return ev.formatted_print ("%qE on closed file descriptor %qE",
- m_callee_fndecl, m_arg);
- }
-
-private:
- diagnostic_event_id_t m_first_close_event;
-};
-
-class fd_use_without_check : public fd_param_diagnostic
-{
-public:
- fd_use_without_check (const fd_state_machine &sm, tree arg,
- const tree callee_fndecl, const char *attr_name,
- int arg_idx)
- : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
- {
- }
-
- fd_use_without_check (const fd_state_machine &sm, tree arg,
- const tree callee_fndecl)
- : fd_param_diagnostic (sm, arg, callee_fndecl)
- {
- }
-
- const char *
- get_kind () const final override
- {
- return "fd_use_without_check";
- }
-
- int
- get_controlling_option () const final override
- {
- return OPT_Wanalyzer_fd_use_without_check;
- }
-
- bool
- emit (rich_location *rich_loc) final override
- {
- bool warned;
- warned = warning_at (rich_loc, get_controlling_option (),
- "%qE on possibly invalid file descriptor %qE",
- m_callee_fndecl, m_arg);
- if (warned)
- inform_filedescriptor_attribute (DIRS_READ_WRITE);
- return warned;
- }
-
- label_text
- describe_state_change (const evdesc::state_change &change) override
- {
- if (m_sm.is_unchecked_fd_p (change.m_new_state))
- {
- m_first_open_event = change.m_event_id;
- return label_text::borrow ("opened here");
- }
-
- return fd_diagnostic::describe_state_change (change);
- }
-
- label_text
- describe_final_event (const evdesc::final_event &ev) final override
- {
- if (m_first_open_event.known_p ())
- return ev.formatted_print (
- "%qE could be invalid: unchecked value from %@", m_arg,
- &m_first_open_event);
- else
- return ev.formatted_print ("%qE could be invalid", m_arg);
- }
-
-private:
- diagnostic_event_id_t m_first_open_event;
-};
-
-fd_state_machine::fd_state_machine (logger *logger)
- : state_machine ("file-descriptor", logger),
- m_constant_fd (add_state ("fd-constant")),
- m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
- m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
- m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
- m_valid_read_write (add_state ("fd-valid-read-write")),
- m_valid_read_only (add_state ("fd-valid-read-only")),
- m_valid_write_only (add_state ("fd-valid-write-only")),
- m_invalid (add_state ("fd-invalid")),
- m_closed (add_state ("fd-closed")),
- m_stop (add_state ("fd-stop"))
-{
-}
-
-bool
-fd_state_machine::is_unchecked_fd_p (state_t s) const
-{
- return (s == m_unchecked_read_write
- || s == m_unchecked_read_only
- || s == m_unchecked_write_only);
-}
-
-bool
-fd_state_machine::is_valid_fd_p (state_t s) const
-{
- return (s == m_valid_read_write
- || s == m_valid_read_only
- || s == m_valid_write_only);
-}
-
-enum access_mode
-fd_state_machine::get_access_mode_from_flag (int flag) const
-{
- /* FIXME: this code assumes the access modes on the host and
- target are the same, which in practice might not be the case. */
-
- if ((flag & O_ACCMODE) == O_RDONLY)
- {
- return READ_ONLY;
- }
- else if ((flag & O_ACCMODE) == O_WRONLY)
- {
- return WRITE_ONLY;
- }
- return READ_WRITE;
-}
-
-bool
-fd_state_machine::is_readonly_fd_p (state_t state) const
-{
- return (state == m_unchecked_read_only || state == m_valid_read_only);
-}
-
-bool
-fd_state_machine::is_writeonly_fd_p (state_t state) const
-{
- return (state == m_unchecked_write_only || state == m_valid_write_only);
-}
-
-bool
-fd_state_machine::is_closed_fd_p (state_t state) const
-{
- return (state == m_closed);
-}
-
-bool
-fd_state_machine::is_constant_fd_p (state_t state) const
-{
- return (state == m_constant_fd);
-}
-
-bool
-fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt) const
-{
- if (const gcall *call = dyn_cast<const gcall *> (stmt))
- if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
- {
- if (is_named_call_p (callee_fndecl, "open", call, 2))
- {
- on_open (sm_ctxt, node, stmt, call);
- return true;
- } // "open"
-
- if (is_named_call_p (callee_fndecl, "close", call, 1))
- {
- on_close (sm_ctxt, node, stmt, call);
- return true;
- } // "close"
-
- if (is_named_call_p (callee_fndecl, "write", call, 3))
- {
- on_write (sm_ctxt, node, stmt, call, callee_fndecl);
- return true;
- } // "write"
-
- if (is_named_call_p (callee_fndecl, "read", call, 3))
- {
- on_read (sm_ctxt, node, stmt, call, callee_fndecl);
- return true;
- } // "read"
-
-
- {
- // Handle __attribute__((fd_arg))
-
- check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
- "fd_arg", DIRS_READ_WRITE);
-
- // Handle __attribute__((fd_arg_read))
-
- check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
- "fd_arg_read", DIRS_READ);
-
- // Handle __attribute__((fd_arg_write))
-
- check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
- "fd_arg_write", DIRS_WRITE);
- }
- }
-
- return false;
-}
-
-void
-fd_state_machine::check_for_fd_attrs (
- sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call, const tree callee_fndecl, const char *attr_name,
- access_directions fd_attr_access_dir) const
-{
-
- tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
- attrs = lookup_attribute (attr_name, attrs);
- if (!attrs)
- return;
-
- if (!TREE_VALUE (attrs))
- return;
-
- auto_bitmap argmap;
-
- for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
- {
- unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
- bitmap_set_bit (argmap, val);
- }
- if (bitmap_empty_p (argmap))
- return;
-
- for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
- {
- tree arg = gimple_call_arg (call, arg_idx);
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- state_t state = sm_ctxt->get_state (stmt, arg);
- bool bit_set = bitmap_bit_p (argmap, arg_idx);
- if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
- continue;
- if (bit_set) // Check if arg_idx is marked by any of the file descriptor
- // attributes
- {
-
- if (is_closed_fd_p (state))
- {
-
- sm_ctxt->warn (node, stmt, arg,
- new fd_use_after_close (*this, diag_arg,
- callee_fndecl, attr_name,
- arg_idx));
- continue;
- }
-
- if (!(is_valid_fd_p (state) || (state == m_stop)))
- {
- if (!is_constant_fd_p (state))
- sm_ctxt->warn (node, stmt, arg,
- new fd_use_without_check (*this, diag_arg,
- callee_fndecl, attr_name,
- arg_idx));
- }
-
- switch (fd_attr_access_dir)
- {
- case DIRS_READ_WRITE:
- break;
- case DIRS_READ:
-
- if (is_writeonly_fd_p (state))
- {
- sm_ctxt->warn (
- node, stmt, arg,
- new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE,
- callee_fndecl, attr_name, arg_idx));
- }
-
- break;
- case DIRS_WRITE:
-
- if (is_readonly_fd_p (state))
- {
- sm_ctxt->warn (
- node, stmt, arg,
- new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ,
- callee_fndecl, attr_name, arg_idx));
- }
-
- break;
- }
- }
- }
-}
-
-
-void
-fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call) const
-{
- tree lhs = gimple_call_lhs (call);
- if (lhs)
- {
- tree arg = gimple_call_arg (call, 1);
- if (TREE_CODE (arg) == INTEGER_CST)
- {
- int flag = TREE_INT_CST_LOW (arg);
- enum access_mode mode = get_access_mode_from_flag (flag);
-
- switch (mode)
- {
- case READ_ONLY:
- sm_ctxt->on_transition (node, stmt, lhs, m_start,
- m_unchecked_read_only);
- break;
- case WRITE_ONLY:
- sm_ctxt->on_transition (node, stmt, lhs, m_start,
- m_unchecked_write_only);
- break;
- default:
- sm_ctxt->on_transition (node, stmt, lhs, m_start,
- m_unchecked_read_write);
- }
- }
- }
- else
- {
- sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE));
- }
-}
-
-void
-fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call) const
-{
- tree arg = gimple_call_arg (call, 0);
- state_t state = sm_ctxt->get_state (stmt, arg);
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
-
- sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
- sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
-
- if (is_closed_fd_p (state))
- {
- sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg));
- sm_ctxt->set_next_state (stmt, arg, m_stop);
- }
-}
-void
-fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call,
- const tree callee_fndecl) const
-{
- check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);
-}
-void
-fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const gcall *call,
- const tree callee_fndecl) const
-{
- check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);
-}
-
-void
-fd_state_machine::check_for_open_fd (
- sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const gcall *call, const tree callee_fndecl,
- enum access_directions callee_fndecl_dir) const
-{
- tree arg = gimple_call_arg (call, 0);
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- state_t state = sm_ctxt->get_state (stmt, arg);
-
- if (is_closed_fd_p (state))
- {
- sm_ctxt->warn (node, stmt, arg,
- new fd_use_after_close (*this, diag_arg, callee_fndecl));
- }
-
- else
- {
- if (!(is_valid_fd_p (state) || (state == m_stop)))
- {
- if (!is_constant_fd_p (state))
- sm_ctxt->warn (
- node, stmt, arg,
- new fd_use_without_check (*this, diag_arg, callee_fndecl));
- }
- switch (callee_fndecl_dir)
- {
- case DIRS_READ:
- if (is_writeonly_fd_p (state))
- {
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- sm_ctxt->warn (node, stmt, arg,
- new fd_access_mode_mismatch (
- *this, diag_arg, DIRS_WRITE, callee_fndecl));
- }
-
- break;
- case DIRS_WRITE:
-
- if (is_readonly_fd_p (state))
- {
- tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
- sm_ctxt->warn (node, stmt, arg,
- new fd_access_mode_mismatch (
- *this, diag_arg, DIRS_READ, callee_fndecl));
- }
- break;
- default:
- gcc_unreachable ();
- }
- }
-}
-
-void
-fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
- const gimple *stmt, const svalue *lhs,
- enum tree_code op, const svalue *rhs) const
-{
- if (tree cst = rhs->maybe_get_constant ())
- {
- if (TREE_CODE (cst) == INTEGER_CST)
- {
- int val = TREE_INT_CST_LOW (cst);
- if (val == -1)
- {
- if (op == NE_EXPR)
- make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
-
- else if (op == EQ_EXPR)
- make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
- lhs);
- }
- }
- }
-
- if (rhs->all_zeroes_p ())
- {
- if (op == GE_EXPR)
- make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
- else if (op == LT_EXPR)
- make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
- }
-}
-
-void
-fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt,
- const supernode *node,
- const gimple *stmt,
- const svalue *lhs) const
-{
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write,
- m_valid_read_write);
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only,
- m_valid_read_only);
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only,
- m_valid_write_only);
-}
-
-void
-fd_state_machine::make_invalid_transitions_on_condition (
- sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
- const svalue *lhs) const
-{
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid);
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid);
- sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid);
-}
-
-bool
-fd_state_machine::can_purge_p (state_t s) const
-{
- if (is_unchecked_fd_p (s) || is_valid_fd_p (s))
- return false;
- else
- return true;
-}
-
-pending_diagnostic *
-fd_state_machine::on_leak (tree var) const
-{
- return new fd_leak (*this, var);
-}
-} // namespace
-
-state_machine *
-make_fd_state_machine (logger *logger)
-{
- return new fd_state_machine (logger);
-}
-} // namespace ana
-
-#endif // ENABLE_ANALYZER \ No newline at end of file
+/* A state machine for detecting misuses of POSIX file descriptor APIs.
+ Copyright (C) 2019-2022 Free Software Foundation, Inc.
+ Contributed by Immad Mir <mir@sourceware.org>.
+
+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 "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "options.h"
+#include "diagnostic-path.h"
+#include "diagnostic-metadata.h"
+#include "function.h"
+#include "json.h"
+#include "analyzer/analyzer.h"
+#include "diagnostic-event-id.h"
+#include "analyzer/analyzer-logging.h"
+#include "analyzer/sm.h"
+#include "analyzer/pending-diagnostic.h"
+#include "analyzer/function-set.h"
+#include "analyzer/analyzer-selftests.h"
+#include "tristate.h"
+#include "selftest.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "analyzer/call-string.h"
+#include "analyzer/program-point.h"
+#include "analyzer/store.h"
+#include "analyzer/region-model.h"
+#include "bitmap.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+namespace {
+
+/* An enum for distinguishing between three different access modes. */
+
+enum access_mode
+{
+ READ_WRITE,
+ READ_ONLY,
+ WRITE_ONLY
+};
+
+enum access_directions
+{
+ DIRS_READ_WRITE,
+ DIRS_READ,
+ DIRS_WRITE
+};
+
+class fd_state_machine : public state_machine
+{
+public:
+ fd_state_machine (logger *logger);
+
+ bool
+ inherited_state_p () const final override
+ {
+ return false;
+ }
+
+ state_machine::state_t
+ get_default_state (const svalue *sval) const final override
+ {
+ if (tree cst = sval->maybe_get_constant ())
+ {
+ if (TREE_CODE (cst) == INTEGER_CST)
+ {
+ int val = TREE_INT_CST_LOW (cst);
+ if (val >= 0)
+ return m_constant_fd;
+ else
+ return m_invalid;
+ }
+ }
+ return m_start;
+ }
+
+ bool on_stmt (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt) const final override;
+
+ void on_condition (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const svalue *lhs, const tree_code op,
+ const svalue *rhs) const final override;
+
+ bool can_purge_p (state_t s) const final override;
+ pending_diagnostic *on_leak (tree var) const final override;
+
+ bool is_unchecked_fd_p (state_t s) const;
+ bool is_valid_fd_p (state_t s) const;
+ bool is_closed_fd_p (state_t s) const;
+ bool is_constant_fd_p (state_t s) const;
+ bool is_readonly_fd_p (state_t s) const;
+ bool is_writeonly_fd_p (state_t s) const;
+ enum access_mode get_access_mode_from_flag (int flag) const;
+
+ /* State for a constant file descriptor (>= 0) */
+ state_t m_constant_fd;
+
+ /* States representing a file descriptor that hasn't yet been
+ checked for validity after opening, for three different
+ access modes. */
+ state_t m_unchecked_read_write;
+
+ state_t m_unchecked_read_only;
+
+ state_t m_unchecked_write_only;
+
+ /* States for representing a file descriptor that is known to be valid (>=
+ 0), for three different access modes. */
+ state_t m_valid_read_write;
+
+ state_t m_valid_read_only;
+
+ state_t m_valid_write_only;
+
+ /* State for a file descriptor that is known to be invalid (< 0). */
+ state_t m_invalid;
+
+ /* State for a file descriptor that has been closed. */
+ state_t m_closed;
+
+ /* State for a file descriptor that we do not want to track anymore . */
+ state_t m_stop;
+
+private:
+ void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call) const;
+ void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call) const;
+ void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call, const tree callee_fndecl) const;
+ void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call, const tree callee_fndecl) const;
+ void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call,
+ const tree callee_fndecl,
+ enum access_directions access_fn) const;
+
+ void make_valid_transitions_on_condition (sm_context *sm_ctxt,
+ const supernode *node,
+ const gimple *stmt,
+ const svalue *lhs) const;
+ void make_invalid_transitions_on_condition (sm_context *sm_ctxt,
+ const supernode *node,
+ const gimple *stmt,
+ const svalue *lhs) const;
+ void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call,
+ const tree callee_fndecl, const char *attr_name,
+ access_directions fd_attr_access_dir) const;
+};
+
+/* Base diagnostic class relative to fd_state_machine. */
+class fd_diagnostic : public pending_diagnostic
+{
+public:
+ fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
+ {
+ }
+
+ bool
+ subclass_equal_p (const pending_diagnostic &base_other) const override
+ {
+ return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
+ }
+
+ label_text
+ describe_state_change (const evdesc::state_change &change) override
+ {
+ if (change.m_old_state == m_sm.get_start_state ()
+ && m_sm.is_unchecked_fd_p (change.m_new_state))
+ {
+ if (change.m_new_state == m_sm.m_unchecked_read_write)
+ return change.formatted_print ("opened here as read-write");
+
+ if (change.m_new_state == m_sm.m_unchecked_read_only)
+ return change.formatted_print ("opened here as read-only");
+
+ if (change.m_new_state == m_sm.m_unchecked_write_only)
+ return change.formatted_print ("opened here as write-only");
+ }
+
+ if (change.m_new_state == m_sm.m_closed)
+ return change.formatted_print ("closed here");
+
+ if (m_sm.is_unchecked_fd_p (change.m_old_state)
+ && m_sm.is_valid_fd_p (change.m_new_state))
+ {
+ if (change.m_expr)
+ return change.formatted_print (
+ "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
+ else
+ return change.formatted_print ("assuming a valid file descriptor");
+ }
+
+ if (m_sm.is_unchecked_fd_p (change.m_old_state)
+ && change.m_new_state == m_sm.m_invalid)
+ {
+ if (change.m_expr)
+ return change.formatted_print (
+ "assuming %qE is an invalid file descriptor (< 0)",
+ change.m_expr);
+ else
+ return change.formatted_print ("assuming an invalid file descriptor");
+ }
+
+ return label_text ();
+ }
+
+protected:
+ const fd_state_machine &m_sm;
+ tree m_arg;
+};
+
+class fd_param_diagnostic : public fd_diagnostic
+{
+public:
+ fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
+ const char *attr_name, int arg_idx)
+ : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
+ m_attr_name (attr_name), m_arg_idx (arg_idx)
+ {
+ }
+
+ fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
+ : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
+ m_attr_name (NULL), m_arg_idx (-1)
+ {
+ }
+
+ bool
+ subclass_equal_p (const pending_diagnostic &base_other) const override
+ {
+ const fd_param_diagnostic &sub_other
+ = (const fd_param_diagnostic &)base_other;
+ return (same_tree_p (m_arg, sub_other.m_arg)
+ && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
+ && m_arg_idx == sub_other.m_arg_idx
+ && ((m_attr_name)
+ ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
+ : true));
+ }
+
+ void
+ inform_filedescriptor_attribute (access_directions fd_dir)
+ {
+
+ if (m_attr_name)
+ switch (fd_dir)
+ {
+ case DIRS_READ_WRITE:
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
+ "argument %d of %qD must be an open file descriptor, due to "
+ "%<__attribute__((%s(%d)))%>",
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
+ break;
+ case DIRS_WRITE:
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
+ "argument %d of %qD must be a readable file descriptor, due "
+ "to %<__attribute__((%s(%d)))%>",
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
+ break;
+ case DIRS_READ:
+ inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
+ "argument %d of %qD must be a writable file descriptor, due "
+ "to %<__attribute__((%s(%d)))%>",
+ m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
+ break;
+ }
+ }
+
+protected:
+ tree m_callee_fndecl;
+ const char *m_attr_name;
+ /* ARG_IDX is 0-based. */
+ int m_arg_idx;
+};
+
+class fd_leak : public fd_diagnostic
+{
+public:
+ fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
+
+ const char *
+ get_kind () const final override
+ {
+ return "fd_leak";
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_fd_leak;
+ }
+
+ bool
+ emit (rich_location *rich_loc) final override
+ {
+ /*CWE-775: Missing Release of File Descriptor or Handle after Effective
+ Lifetime
+ */
+ diagnostic_metadata m;
+ m.add_cwe (775);
+ if (m_arg)
+ return warning_meta (rich_loc, m, get_controlling_option (),
+ "leak of file descriptor %qE", m_arg);
+ else
+ return warning_meta (rich_loc, m, get_controlling_option (),
+ "leak of file descriptor");
+ }
+
+ label_text
+ describe_state_change (const evdesc::state_change &change) final override
+ {
+ if (m_sm.is_unchecked_fd_p (change.m_new_state))
+ {
+ m_open_event = change.m_event_id;
+ return label_text::borrow ("opened here");
+ }
+
+ return fd_diagnostic::describe_state_change (change);
+ }
+
+ label_text
+ describe_final_event (const evdesc::final_event &ev) final override
+ {
+ if (m_open_event.known_p ())
+ {
+ if (ev.m_expr)
+ return ev.formatted_print ("%qE leaks here; was opened at %@",
+ ev.m_expr, &m_open_event);
+ else
+ return ev.formatted_print ("leaks here; was opened at %@",
+ &m_open_event);
+ }
+ else
+ {
+ if (ev.m_expr)
+ return ev.formatted_print ("%qE leaks here", ev.m_expr);
+ else
+ return ev.formatted_print ("leaks here");
+ }
+ }
+
+private:
+ diagnostic_event_id_t m_open_event;
+};
+
+class fd_access_mode_mismatch : public fd_param_diagnostic
+{
+public:
+ fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
+ enum access_directions fd_dir,
+ const tree callee_fndecl, const char *attr_name,
+ int arg_idx)
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
+ m_fd_dir (fd_dir)
+
+ {
+ }
+
+ fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
+ enum access_directions fd_dir,
+ const tree callee_fndecl)
+ : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
+ {
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "fd_access_mode_mismatch";
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_fd_access_mode_mismatch;
+ }
+
+ bool
+ emit (rich_location *rich_loc) final override
+ {
+ bool warned;
+ switch (m_fd_dir)
+ {
+ case DIRS_READ:
+ warned = warning_at (rich_loc, get_controlling_option (),
+ "%qE on read-only file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ break;
+ case DIRS_WRITE:
+ warned = warning_at (rich_loc, get_controlling_option (),
+ "%qE on write-only file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (warned)
+ inform_filedescriptor_attribute (m_fd_dir);
+ return warned;
+ }
+
+ label_text
+ describe_final_event (const evdesc::final_event &ev) final override
+ {
+ switch (m_fd_dir)
+ {
+ case DIRS_READ:
+ return ev.formatted_print ("%qE on read-only file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ case DIRS_WRITE:
+ return ev.formatted_print ("%qE on write-only file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+private:
+ enum access_directions m_fd_dir;
+};
+
+class fd_double_close : public fd_diagnostic
+{
+public:
+ fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
+ {
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "fd_double_close";
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_fd_double_close;
+ }
+ bool
+ emit (rich_location *rich_loc) final override
+ {
+ diagnostic_metadata m;
+ // CWE-1341: Multiple Releases of Same Resource or Handle
+ m.add_cwe (1341);
+ return warning_meta (rich_loc, m, get_controlling_option (),
+ "double %<close%> of file descriptor %qE", m_arg);
+ }
+
+ label_text
+ describe_state_change (const evdesc::state_change &change) override
+ {
+ if (m_sm.is_unchecked_fd_p (change.m_new_state))
+ return label_text::borrow ("opened here");
+
+ if (change.m_new_state == m_sm.m_closed)
+ {
+ m_first_close_event = change.m_event_id;
+ return change.formatted_print ("first %qs here", "close");
+ }
+ return fd_diagnostic::describe_state_change (change);
+ }
+
+ label_text
+ describe_final_event (const evdesc::final_event &ev) final override
+ {
+ if (m_first_close_event.known_p ())
+ return ev.formatted_print ("second %qs here; first %qs was at %@",
+ "close", "close", &m_first_close_event);
+ return ev.formatted_print ("second %qs here", "close");
+ }
+
+private:
+ diagnostic_event_id_t m_first_close_event;
+};
+
+class fd_use_after_close : public fd_param_diagnostic
+{
+public:
+ fd_use_after_close (const fd_state_machine &sm, tree arg,
+ const tree callee_fndecl, const char *attr_name,
+ int arg_idx)
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
+ {
+ }
+
+ fd_use_after_close (const fd_state_machine &sm, tree arg,
+ const tree callee_fndecl)
+ : fd_param_diagnostic (sm, arg, callee_fndecl)
+ {
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "fd_use_after_close";
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_fd_use_after_close;
+ }
+
+ bool
+ emit (rich_location *rich_loc) final override
+ {
+ bool warned;
+ warned = warning_at (rich_loc, get_controlling_option (),
+ "%qE on closed file descriptor %qE", m_callee_fndecl,
+ m_arg);
+ if (warned)
+ inform_filedescriptor_attribute (DIRS_READ_WRITE);
+ return warned;
+ }
+
+ label_text
+ describe_state_change (const evdesc::state_change &change) override
+ {
+ if (m_sm.is_unchecked_fd_p (change.m_new_state))
+ return label_text::borrow ("opened here");
+
+ if (change.m_new_state == m_sm.m_closed)
+ {
+ m_first_close_event = change.m_event_id;
+ return change.formatted_print ("closed here");
+ }
+
+ return fd_diagnostic::describe_state_change (change);
+ }
+
+ label_text
+ describe_final_event (const evdesc::final_event &ev) final override
+ {
+ if (m_first_close_event.known_p ())
+ return ev.formatted_print (
+ "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
+ m_arg, "close", &m_first_close_event);
+ else
+ return ev.formatted_print ("%qE on closed file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ }
+
+private:
+ diagnostic_event_id_t m_first_close_event;
+};
+
+class fd_use_without_check : public fd_param_diagnostic
+{
+public:
+ fd_use_without_check (const fd_state_machine &sm, tree arg,
+ const tree callee_fndecl, const char *attr_name,
+ int arg_idx)
+ : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
+ {
+ }
+
+ fd_use_without_check (const fd_state_machine &sm, tree arg,
+ const tree callee_fndecl)
+ : fd_param_diagnostic (sm, arg, callee_fndecl)
+ {
+ }
+
+ const char *
+ get_kind () const final override
+ {
+ return "fd_use_without_check";
+ }
+
+ int
+ get_controlling_option () const final override
+ {
+ return OPT_Wanalyzer_fd_use_without_check;
+ }
+
+ bool
+ emit (rich_location *rich_loc) final override
+ {
+ bool warned;
+ warned = warning_at (rich_loc, get_controlling_option (),
+ "%qE on possibly invalid file descriptor %qE",
+ m_callee_fndecl, m_arg);
+ if (warned)
+ inform_filedescriptor_attribute (DIRS_READ_WRITE);
+ return warned;
+ }
+
+ label_text
+ describe_state_change (const evdesc::state_change &change) override
+ {
+ if (m_sm.is_unchecked_fd_p (change.m_new_state))
+ {
+ m_first_open_event = change.m_event_id;
+ return label_text::borrow ("opened here");
+ }
+
+ return fd_diagnostic::describe_state_change (change);
+ }
+
+ label_text
+ describe_final_event (const evdesc::final_event &ev) final override
+ {
+ if (m_first_open_event.known_p ())
+ return ev.formatted_print (
+ "%qE could be invalid: unchecked value from %@", m_arg,
+ &m_first_open_event);
+ else
+ return ev.formatted_print ("%qE could be invalid", m_arg);
+ }
+
+private:
+ diagnostic_event_id_t m_first_open_event;
+};
+
+fd_state_machine::fd_state_machine (logger *logger)
+ : state_machine ("file-descriptor", logger),
+ m_constant_fd (add_state ("fd-constant")),
+ m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
+ m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
+ m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
+ m_valid_read_write (add_state ("fd-valid-read-write")),
+ m_valid_read_only (add_state ("fd-valid-read-only")),
+ m_valid_write_only (add_state ("fd-valid-write-only")),
+ m_invalid (add_state ("fd-invalid")),
+ m_closed (add_state ("fd-closed")),
+ m_stop (add_state ("fd-stop"))
+{
+}
+
+bool
+fd_state_machine::is_unchecked_fd_p (state_t s) const
+{
+ return (s == m_unchecked_read_write
+ || s == m_unchecked_read_only
+ || s == m_unchecked_write_only);
+}
+
+bool
+fd_state_machine::is_valid_fd_p (state_t s) const
+{
+ return (s == m_valid_read_write
+ || s == m_valid_read_only
+ || s == m_valid_write_only);
+}
+
+enum access_mode
+fd_state_machine::get_access_mode_from_flag (int flag) const
+{
+ /* FIXME: this code assumes the access modes on the host and
+ target are the same, which in practice might not be the case. */
+
+ if ((flag & O_ACCMODE) == O_RDONLY)
+ {
+ return READ_ONLY;
+ }
+ else if ((flag & O_ACCMODE) == O_WRONLY)
+ {
+ return WRITE_ONLY;
+ }
+ return READ_WRITE;
+}
+
+bool
+fd_state_machine::is_readonly_fd_p (state_t state) const
+{
+ return (state == m_unchecked_read_only || state == m_valid_read_only);
+}
+
+bool
+fd_state_machine::is_writeonly_fd_p (state_t state) const
+{
+ return (state == m_unchecked_write_only || state == m_valid_write_only);
+}
+
+bool
+fd_state_machine::is_closed_fd_p (state_t state) const
+{
+ return (state == m_closed);
+}
+
+bool
+fd_state_machine::is_constant_fd_p (state_t state) const
+{
+ return (state == m_constant_fd);
+}
+
+bool
+fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt) const
+{
+ if (const gcall *call = dyn_cast<const gcall *> (stmt))
+ if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
+ {
+ if (is_named_call_p (callee_fndecl, "open", call, 2))
+ {
+ on_open (sm_ctxt, node, stmt, call);
+ return true;
+ } // "open"
+
+ if (is_named_call_p (callee_fndecl, "close", call, 1))
+ {
+ on_close (sm_ctxt, node, stmt, call);
+ return true;
+ } // "close"
+
+ if (is_named_call_p (callee_fndecl, "write", call, 3))
+ {
+ on_write (sm_ctxt, node, stmt, call, callee_fndecl);
+ return true;
+ } // "write"
+
+ if (is_named_call_p (callee_fndecl, "read", call, 3))
+ {
+ on_read (sm_ctxt, node, stmt, call, callee_fndecl);
+ return true;
+ } // "read"
+
+
+ {
+ // Handle __attribute__((fd_arg))
+
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
+ "fd_arg", DIRS_READ_WRITE);
+
+ // Handle __attribute__((fd_arg_read))
+
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
+ "fd_arg_read", DIRS_READ);
+
+ // Handle __attribute__((fd_arg_write))
+
+ check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
+ "fd_arg_write", DIRS_WRITE);
+ }
+ }
+
+ return false;
+}
+
+void
+fd_state_machine::check_for_fd_attrs (
+ sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call, const tree callee_fndecl, const char *attr_name,
+ access_directions fd_attr_access_dir) const
+{
+
+ tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
+ attrs = lookup_attribute (attr_name, attrs);
+ if (!attrs)
+ return;
+
+ if (!TREE_VALUE (attrs))
+ return;
+
+ auto_bitmap argmap;
+
+ for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
+ {
+ unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
+ bitmap_set_bit (argmap, val);
+ }
+ if (bitmap_empty_p (argmap))
+ return;
+
+ for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
+ {
+ tree arg = gimple_call_arg (call, arg_idx);
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+ state_t state = sm_ctxt->get_state (stmt, arg);
+ bool bit_set = bitmap_bit_p (argmap, arg_idx);
+ if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
+ continue;
+ if (bit_set) // Check if arg_idx is marked by any of the file descriptor
+ // attributes
+ {
+
+ if (is_closed_fd_p (state))
+ {
+
+ sm_ctxt->warn (node, stmt, arg,
+ new fd_use_after_close (*this, diag_arg,
+ callee_fndecl, attr_name,
+ arg_idx));
+ continue;
+ }
+
+ if (!(is_valid_fd_p (state) || (state == m_stop)))
+ {
+ if (!is_constant_fd_p (state))
+ sm_ctxt->warn (node, stmt, arg,
+ new fd_use_without_check (*this, diag_arg,
+ callee_fndecl, attr_name,
+ arg_idx));
+ }
+
+ switch (fd_attr_access_dir)
+ {
+ case DIRS_READ_WRITE:
+ break;
+ case DIRS_READ:
+
+ if (is_writeonly_fd_p (state))
+ {
+ sm_ctxt->warn (
+ node, stmt, arg,
+ new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE,
+ callee_fndecl, attr_name, arg_idx));
+ }
+
+ break;
+ case DIRS_WRITE:
+
+ if (is_readonly_fd_p (state))
+ {
+ sm_ctxt->warn (
+ node, stmt, arg,
+ new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ,
+ callee_fndecl, attr_name, arg_idx));
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+
+void
+fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call) const
+{
+ tree lhs = gimple_call_lhs (call);
+ if (lhs)
+ {
+ tree arg = gimple_call_arg (call, 1);
+ if (TREE_CODE (arg) == INTEGER_CST)
+ {
+ int flag = TREE_INT_CST_LOW (arg);
+ enum access_mode mode = get_access_mode_from_flag (flag);
+
+ switch (mode)
+ {
+ case READ_ONLY:
+ sm_ctxt->on_transition (node, stmt, lhs, m_start,
+ m_unchecked_read_only);
+ break;
+ case WRITE_ONLY:
+ sm_ctxt->on_transition (node, stmt, lhs, m_start,
+ m_unchecked_write_only);
+ break;
+ default:
+ sm_ctxt->on_transition (node, stmt, lhs, m_start,
+ m_unchecked_read_write);
+ }
+ }
+ }
+ else
+ {
+ sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE));
+ }
+}
+
+void
+fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call) const
+{
+ tree arg = gimple_call_arg (call, 0);
+ state_t state = sm_ctxt->get_state (stmt, arg);
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+
+ sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
+ sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
+
+ if (is_closed_fd_p (state))
+ {
+ sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg));
+ sm_ctxt->set_next_state (stmt, arg, m_stop);
+ }
+}
+void
+fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call,
+ const tree callee_fndecl) const
+{
+ check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);
+}
+void
+fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const gcall *call,
+ const tree callee_fndecl) const
+{
+ check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);
+}
+
+void
+fd_state_machine::check_for_open_fd (
+ sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const gcall *call, const tree callee_fndecl,
+ enum access_directions callee_fndecl_dir) const
+{
+ tree arg = gimple_call_arg (call, 0);
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+ state_t state = sm_ctxt->get_state (stmt, arg);
+
+ if (is_closed_fd_p (state))
+ {
+ sm_ctxt->warn (node, stmt, arg,
+ new fd_use_after_close (*this, diag_arg, callee_fndecl));
+ }
+
+ else
+ {
+ if (!(is_valid_fd_p (state) || (state == m_stop)))
+ {
+ if (!is_constant_fd_p (state))
+ sm_ctxt->warn (
+ node, stmt, arg,
+ new fd_use_without_check (*this, diag_arg, callee_fndecl));
+ }
+ switch (callee_fndecl_dir)
+ {
+ case DIRS_READ:
+ if (is_writeonly_fd_p (state))
+ {
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+ sm_ctxt->warn (node, stmt, arg,
+ new fd_access_mode_mismatch (
+ *this, diag_arg, DIRS_WRITE, callee_fndecl));
+ }
+
+ break;
+ case DIRS_WRITE:
+
+ if (is_readonly_fd_p (state))
+ {
+ tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
+ sm_ctxt->warn (node, stmt, arg,
+ new fd_access_mode_mismatch (
+ *this, diag_arg, DIRS_READ, callee_fndecl));
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+}
+
+void
+fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
+ const gimple *stmt, const svalue *lhs,
+ enum tree_code op, const svalue *rhs) const
+{
+ if (tree cst = rhs->maybe_get_constant ())
+ {
+ if (TREE_CODE (cst) == INTEGER_CST)
+ {
+ int val = TREE_INT_CST_LOW (cst);
+ if (val == -1)
+ {
+ if (op == NE_EXPR)
+ make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
+
+ else if (op == EQ_EXPR)
+ make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
+ lhs);
+ }
+ }
+ }
+
+ if (rhs->all_zeroes_p ())
+ {
+ if (op == GE_EXPR)
+ make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
+ else if (op == LT_EXPR)
+ make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
+ }
+}
+
+void
+fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt,
+ const supernode *node,
+ const gimple *stmt,
+ const svalue *lhs) const
+{
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write,
+ m_valid_read_write);
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only,
+ m_valid_read_only);
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only,
+ m_valid_write_only);
+}
+
+void
+fd_state_machine::make_invalid_transitions_on_condition (
+ sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
+ const svalue *lhs) const
+{
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid);
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid);
+ sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid);
+}
+
+bool
+fd_state_machine::can_purge_p (state_t s) const
+{
+ if (is_unchecked_fd_p (s) || is_valid_fd_p (s))
+ return false;
+ else
+ return true;
+}
+
+pending_diagnostic *
+fd_state_machine::on_leak (tree var) const
+{
+ return new fd_leak (*this, var);
+}
+} // namespace
+
+state_machine *
+make_fd_state_machine (logger *logger)
+{
+ return new fd_state_machine (logger);
+}
+} // namespace ana
+
+#endif // ENABLE_ANALYZER
diff --git a/gcc/common/config/loongarch/loongarch-common.cc b/gcc/common/config/loongarch/loongarch-common.cc
index ed3730f..f8b4660 100644
--- a/gcc/common/config/loongarch/loongarch-common.cc
+++ b/gcc/common/config/loongarch/loongarch-common.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
static const struct default_options loongarch_option_optimization_table[] =
{
{ OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 },
+ { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 },
{ OPT_LEVELS_NONE, 0, NULL, 0 }
};
diff --git a/gcc/config/aarch64/aarch64-builtins.cc b/gcc/config/aarch64/aarch64-builtins.cc
index 69f1e4e..2cacb4d 100644
--- a/gcc/config/aarch64/aarch64-builtins.cc
+++ b/gcc/config/aarch64/aarch64-builtins.cc
@@ -612,6 +612,12 @@ enum aarch64_builtins
AARCH64_LS64_BUILTIN_ST64B,
AARCH64_LS64_BUILTIN_ST64BV,
AARCH64_LS64_BUILTIN_ST64BV0,
+ AARCH64_REV16,
+ AARCH64_REV16L,
+ AARCH64_REV16LL,
+ AARCH64_RBIT,
+ AARCH64_RBITL,
+ AARCH64_RBITLL,
AARCH64_BUILTIN_MAX
};
@@ -1659,6 +1665,36 @@ aarch64_init_ls64_builtins (void)
= aarch64_general_add_builtin (data[i].name, data[i].type, data[i].code);
}
+static void
+aarch64_init_data_intrinsics (void)
+{
+ tree uint32_fntype = build_function_type_list (uint32_type_node,
+ uint32_type_node, NULL_TREE);
+ tree ulong_fntype = build_function_type_list (long_unsigned_type_node,
+ long_unsigned_type_node,
+ NULL_TREE);
+ tree uint64_fntype = build_function_type_list (uint64_type_node,
+ uint64_type_node, NULL_TREE);
+ aarch64_builtin_decls[AARCH64_REV16]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rev16", uint32_fntype,
+ AARCH64_REV16);
+ aarch64_builtin_decls[AARCH64_REV16L]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rev16l", ulong_fntype,
+ AARCH64_REV16L);
+ aarch64_builtin_decls[AARCH64_REV16LL]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rev16ll", uint64_fntype,
+ AARCH64_REV16LL);
+ aarch64_builtin_decls[AARCH64_RBIT]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rbit", uint32_fntype,
+ AARCH64_RBIT);
+ aarch64_builtin_decls[AARCH64_RBITL]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rbitl", ulong_fntype,
+ AARCH64_RBITL);
+ aarch64_builtin_decls[AARCH64_RBITLL]
+ = aarch64_general_add_builtin ("__builtin_aarch64_rbitll", uint64_fntype,
+ AARCH64_RBITLL);
+}
+
/* Implement #pragma GCC aarch64 "arm_acle.h". */
void
handle_arm_acle_h (void)
@@ -1737,6 +1773,7 @@ aarch64_general_init_builtins (void)
aarch64_init_crc32_builtins ();
aarch64_init_builtin_rsqrt ();
aarch64_init_rng_builtins ();
+ aarch64_init_data_intrinsics ();
tree ftype_jcvt
= build_function_type_list (intSI_type_node, double_type_node, NULL);
@@ -2389,6 +2426,37 @@ aarch64_expand_builtin_memtag (int fcode, tree exp, rtx target)
return target;
}
+/* Function to expand an expression EXP which calls one of the ACLE Data
+ Intrinsic builtins FCODE with the result going to TARGET. */
+static rtx
+aarch64_expand_builtin_data_intrinsic (unsigned int fcode, tree exp, rtx target)
+{
+ expand_operand ops[2];
+ machine_mode mode = GET_MODE (target);
+ create_output_operand (&ops[0], target, mode);
+ create_input_operand (&ops[1], expand_normal (CALL_EXPR_ARG (exp, 0)), mode);
+ enum insn_code icode;
+
+ switch (fcode)
+ {
+ case AARCH64_REV16:
+ case AARCH64_REV16L:
+ case AARCH64_REV16LL:
+ icode = code_for_aarch64_rev16 (mode);
+ break;
+ case AARCH64_RBIT:
+ case AARCH64_RBITL:
+ case AARCH64_RBITLL:
+ icode = code_for_aarch64_rbit (mode);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ expand_insn (icode, 2, ops);
+ return ops[0].value;
+}
+
/* Expand an expression EXP as fpsr or fpcr setter (depending on
UNSPEC) using MODE. */
static void
@@ -2546,6 +2614,9 @@ aarch64_general_expand_builtin (unsigned int fcode, tree exp, rtx target,
if (fcode >= AARCH64_MEMTAG_BUILTIN_START
&& fcode <= AARCH64_MEMTAG_BUILTIN_END)
return aarch64_expand_builtin_memtag (fcode, exp, target);
+ if (fcode >= AARCH64_REV16
+ && fcode <= AARCH64_RBITLL)
+ return aarch64_expand_builtin_data_intrinsic (fcode, exp, target);
gcc_unreachable ();
}
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index acec8c1..ef0aed2 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -4950,7 +4950,7 @@
rtx ccreg = aarch64_gen_compare_reg (EQ, operands[1], const0_rtx);
rtx x = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
- emit_insn (gen_rbit<mode>2 (operands[0], operands[1]));
+ emit_insn (gen_aarch64_rbit (<MODE>mode, operands[0], operands[1]));
emit_insn (gen_clz<mode>2 (operands[0], operands[0]));
emit_insn (gen_csinc3<mode>_insn (operands[0], x, operands[0], const0_rtx));
DONE;
@@ -4996,7 +4996,7 @@
[(set_attr "type" "clz")]
)
-(define_insn "rbit<mode>2"
+(define_insn "@aarch64_rbit<mode>"
[(set (match_operand:GPI 0 "register_operand" "=r")
(unspec:GPI [(match_operand:GPI 1 "register_operand" "r")] UNSPEC_RBIT))]
""
@@ -5017,7 +5017,7 @@
"reload_completed"
[(const_int 0)]
"
- emit_insn (gen_rbit<mode>2 (operands[0], operands[1]));
+ emit_insn (gen_aarch64_rbit (<MODE>mode, operands[0], operands[1]));
emit_insn (gen_clz<mode>2 (operands[0], operands[0]));
DONE;
")
@@ -6022,6 +6022,13 @@
[(set_attr "type" "rev")]
)
+(define_insn "@aarch64_rev16<mode>"
+ [(set (match_operand:GPI 0 "register_operand" "=r")
+ (unspec:GPI [(match_operand:GPI 1 "register_operand" "r")] UNSPEC_REV))]
+ ""
+ "rev16\\t%<w>0, %<w>1"
+ [(set_attr "type" "rev")])
+
(define_insn "*aarch64_bfxil<mode>"
[(set (match_operand:GPI 0 "register_operand" "=r,r")
(ior:GPI (and:GPI (match_operand:GPI 1 "register_operand" "r,0")
diff --git a/gcc/config/aarch64/arm_acle.h b/gcc/config/aarch64/arm_acle.h
index 9775a48..d26e269 100644
--- a/gcc/config/aarch64/arm_acle.h
+++ b/gcc/config/aarch64/arm_acle.h
@@ -28,6 +28,7 @@
#define _GCC_ARM_ACLE_H
#include <stdint.h>
+#include <stddef.h>
#pragma GCC aarch64 "arm_acle.h"
@@ -35,6 +36,58 @@
extern "C" {
#endif
+#define _GCC_ARM_ACLE_ROR_FN(NAME, TYPE) \
+__extension__ extern __inline TYPE \
+__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \
+NAME (TYPE __value, uint32_t __rotate) \
+{ \
+ size_t __size = sizeof (TYPE) * __CHAR_BIT__; \
+ __rotate = __rotate % __size; \
+ return __value >> __rotate | __value << ((__size - __rotate) % __size); \
+}
+
+_GCC_ARM_ACLE_ROR_FN (__ror, uint32_t)
+_GCC_ARM_ACLE_ROR_FN (__rorl, unsigned long)
+_GCC_ARM_ACLE_ROR_FN (__rorll, uint64_t)
+
+#undef _GCC_ARM_ACLE_ROR_FN
+
+#define _GCC_ARM_ACLE_DATA_FN(NAME, BUILTIN, ITYPE, RTYPE) \
+__extension__ extern __inline RTYPE \
+__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \
+__##NAME (ITYPE __value) \
+{ \
+ return __builtin_##BUILTIN (__value); \
+}
+
+_GCC_ARM_ACLE_DATA_FN (clz, clz, uint32_t, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (clzl, clzl, unsigned long, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (clzll, clzll, uint64_t, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (cls, clrsb, uint32_t, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (clsl, clrsbl, unsigned long, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (clsll, clrsbll, uint64_t, unsigned int)
+_GCC_ARM_ACLE_DATA_FN (rev16, aarch64_rev16, uint32_t, uint32_t)
+_GCC_ARM_ACLE_DATA_FN (rev16l, aarch64_rev16l, unsigned long, unsigned long)
+_GCC_ARM_ACLE_DATA_FN (rev16ll, aarch64_rev16ll, uint64_t, uint64_t)
+_GCC_ARM_ACLE_DATA_FN (rbit, aarch64_rbit, uint32_t, uint32_t)
+_GCC_ARM_ACLE_DATA_FN (rbitl, aarch64_rbitl, unsigned long, unsigned long)
+_GCC_ARM_ACLE_DATA_FN (rbitll, aarch64_rbitll, uint64_t, uint64_t)
+_GCC_ARM_ACLE_DATA_FN (revsh, bswap16, int16_t, int16_t)
+_GCC_ARM_ACLE_DATA_FN (rev, bswap32, uint32_t, uint32_t)
+_GCC_ARM_ACLE_DATA_FN (revll, bswap64, uint64_t, uint64_t)
+
+#undef _GCC_ARM_ACLE_DATA_FN
+
+__extension__ extern __inline unsigned long
+__attribute__ ((__always_inline__, __gnu_inline__, __artificial__))
+__revl (unsigned long __value)
+{
+ if (sizeof (unsigned long) == 8)
+ return __revll (__value);
+ else
+ return __rev (__value);
+}
+
#pragma GCC push_options
#pragma GCC target ("arch=armv8.3-a")
__extension__ extern __inline int32_t
diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 40f821e..66d8f28 100644
--- a/gcc/config/i386/i386-expand.cc
+++ b/gcc/config/i386/i386-expand.cc
@@ -165,6 +165,46 @@ split_double_mode (machine_mode mode, rtx operands[],
}
}
+/* Emit the double word assignment DST = { LO, HI }. */
+
+void
+split_double_concat (machine_mode mode, rtx dst, rtx lo, rtx hi)
+{
+ rtx dlo, dhi;
+ int deleted_move_count = 0;
+ split_double_mode (mode, &dst, 1, &dlo, &dhi);
+ if (!rtx_equal_p (dlo, hi))
+ {
+ if (!rtx_equal_p (dlo, lo))
+ emit_move_insn (dlo, lo);
+ else
+ deleted_move_count++;
+ if (!rtx_equal_p (dhi, hi))
+ emit_move_insn (dhi, hi);
+ else
+ deleted_move_count++;
+ }
+ else if (!rtx_equal_p (lo, dhi))
+ {
+ if (!rtx_equal_p (dhi, hi))
+ emit_move_insn (dhi, hi);
+ else
+ deleted_move_count++;
+ if (!rtx_equal_p (dlo, lo))
+ emit_move_insn (dlo, lo);
+ else
+ deleted_move_count++;
+ }
+ else if (mode == TImode)
+ emit_insn (gen_swapdi (dlo, dhi));
+ else
+ emit_insn (gen_swapsi (dlo, dhi));
+
+ if (deleted_move_count == 2)
+ emit_note (NOTE_INSN_DELETED);
+}
+
+
/* Generate either "mov $0, reg" or "xor reg, reg", as appropriate
for the target. */
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index cf84775..e27c14f 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -85,6 +85,7 @@ extern void print_reg (rtx, int, FILE*);
extern void ix86_print_operand (FILE *, rtx, int);
extern void split_double_mode (machine_mode, rtx[], int, rtx[], rtx[]);
+extern void split_double_concat (machine_mode, rtx, rtx lo, rtx);
extern const char *output_set_got (rtx, rtx);
extern const char *output_387_binary_op (rtx_insn *, rtx*);
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 9aaeb69..fab6aed 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -4116,6 +4116,16 @@
;; Zero extension instructions
+(define_insn_and_split "zero_extendditi2"
+ [(set (match_operand:TI 0 "nonimmediate_operand" "=r,o")
+ (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "rm,r")))]
+ "TARGET_64BIT"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 4) (const_int 0))]
+ "split_double_mode (TImode, &operands[0], 1, &operands[3], &operands[4]);")
+
(define_expand "zero_extendsidi2"
[(set (match_operand:DI 0 "nonimmediate_operand")
(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))])
@@ -5814,6 +5824,31 @@
}
})
+(define_insn_and_split "*add<dwi>3_doubleword_zext"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=r,o")
+ (plus:<DWI>
+ (zero_extend:<DWI>
+ (match_operand:DWIH 2 "nonimmediate_operand" "rm,r"))
+ (match_operand:<DWI> 1 "nonimmediate_operand" "0,0")))
+ (clobber (reg:CC FLAGS_REG))]
+ "ix86_binary_operator_ok (UNKNOWN, <DWI>mode, operands)"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (reg:CCC FLAGS_REG)
+ (compare:CCC
+ (plus:DWIH (match_dup 1) (match_dup 2))
+ (match_dup 1)))
+ (set (match_dup 0)
+ (plus:DWIH (match_dup 1) (match_dup 2)))])
+ (parallel [(set (match_dup 3)
+ (plus:DWIH
+ (plus:DWIH
+ (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0))
+ (match_dup 4))
+ (const_int 0)))
+ (clobber (reg:CC FLAGS_REG))])]
+ "split_double_mode (<DWI>mode, &operands[0], 2, &operands[0], &operands[3]);")
+
(define_insn "*add<mode>_1"
[(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r,r,r")
(plus:SWI48
@@ -6962,6 +6997,29 @@
}
})
+(define_insn_and_split "*sub<dwi>3_doubleword_zext"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=r,o")
+ (minus:<DWI>
+ (match_operand:<DWI> 1 "nonimmediate_operand" "0,0")
+ (zero_extend:<DWI>
+ (match_operand:DWIH 2 "nonimmediate_operand" "rm,r"))))
+ (clobber (reg:CC FLAGS_REG))]
+ "ix86_binary_operator_ok (UNKNOWN, <DWI>mode, operands)"
+ "#"
+ "&& reload_completed"
+ [(parallel [(set (reg:CC FLAGS_REG)
+ (compare:CC (match_dup 1) (match_dup 2)))
+ (set (match_dup 0)
+ (minus:DWIH (match_dup 1) (match_dup 2)))])
+ (parallel [(set (match_dup 3)
+ (minus:DWIH
+ (minus:DWIH
+ (match_dup 4)
+ (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0)))
+ (const_int 0)))
+ (clobber (reg:CC FLAGS_REG))])]
+ "split_double_mode (<DWI>mode, &operands[0], 2, &operands[0], &operands[3]);")
+
(define_insn "*sub<mode>_1"
[(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m,<r>")
(minus:SWI
@@ -11111,34 +11169,68 @@
;; Split DST = (HI<<32)|LO early to minimize register usage.
(define_code_iterator any_or_plus [plus ior xor])
-(define_split
- [(set (match_operand:DI 0 "register_operand")
- (any_or_plus:DI
- (ashift:DI (match_operand:DI 1 "register_operand")
- (const_int 32))
- (zero_extend:DI (match_operand:SI 2 "register_operand"))))]
- "!TARGET_64BIT"
- [(set (match_dup 3) (match_dup 4))
- (set (match_dup 5) (match_dup 2))]
+(define_insn_and_split "*concat<mode><dwi>3_1"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=ro")
+ (any_or_plus:<DWI>
+ (ashift:<DWI> (match_operand:<DWI> 1 "register_operand" "r")
+ (match_operand:<DWI> 2 "const_int_operand"))
+ (zero_extend:<DWI> (match_operand:DWIH 3 "register_operand" "r"))))]
+ "INTVAL (operands[2]) == <MODE_SIZE> * BITS_PER_UNIT"
+ "#"
+ "&& reload_completed"
+ [(clobber (const_int 0))]
{
- operands[3] = gen_highpart (SImode, operands[0]);
- operands[4] = gen_lowpart (SImode, operands[1]);
- operands[5] = gen_lowpart (SImode, operands[0]);
+ split_double_concat (<DWI>mode, operands[0], operands[3],
+ gen_lowpart (<MODE>mode, operands[1]));
+ DONE;
})
-(define_split
- [(set (match_operand:DI 0 "register_operand")
- (any_or_plus:DI
- (zero_extend:DI (match_operand:SI 1 "register_operand"))
- (ashift:DI (match_operand:DI 2 "register_operand")
- (const_int 32))))]
- "!TARGET_64BIT"
- [(set (match_dup 3) (match_dup 4))
- (set (match_dup 5) (match_dup 1))]
+(define_insn_and_split "*concat<mode><dwi>3_2"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=ro")
+ (any_or_plus:<DWI>
+ (zero_extend:<DWI> (match_operand:DWIH 1 "register_operand" "r"))
+ (ashift:<DWI> (match_operand:<DWI> 2 "register_operand" "r")
+ (match_operand:<DWI> 3 "const_int_operand"))))]
+ "INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT"
+ "#"
+ "&& reload_completed"
+ [(clobber (const_int 0))]
+{
+ split_double_concat (<DWI>mode, operands[0], operands[1],
+ gen_lowpart (<MODE>mode, operands[2]));
+ DONE;
+})
+
+(define_insn_and_split "*concat<mode><dwi>3_3"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=ro")
+ (any_or_plus:<DWI>
+ (ashift:<DWI>
+ (zero_extend:<DWI> (match_operand:DWIH 1 "register_operand" "r"))
+ (match_operand:<DWI> 2 "const_int_operand"))
+ (zero_extend:<DWI> (match_operand:DWIH 3 "register_operand" "r"))))]
+ "INTVAL (operands[2]) == <MODE_SIZE> * BITS_PER_UNIT"
+ "#"
+ "&& reload_completed"
+ [(clobber (const_int 0))]
{
- operands[3] = gen_highpart (SImode, operands[0]);
- operands[4] = gen_lowpart (SImode, operands[2]);
- operands[5] = gen_lowpart (SImode, operands[0]);
+ split_double_concat (<DWI>mode, operands[0], operands[3], operands[1]);
+ DONE;
+})
+
+(define_insn_and_split "*concat<mode><dwi>3_4"
+ [(set (match_operand:<DWI> 0 "nonimmediate_operand" "=ro")
+ (any_or_plus:<DWI>
+ (zero_extend:<DWI> (match_operand:DWIH 1 "register_operand" "r"))
+ (ashift:<DWI>
+ (zero_extend:<DWI> (match_operand:DWIH 2 "register_operand" "r"))
+ (match_operand:<DWI> 3 "const_int_operand"))))]
+ "INTVAL (operands[3]) == <MODE_SIZE> * BITS_PER_UNIT"
+ "#"
+ "&& reload_completed"
+ [(clobber (const_int 0))]
+{
+ split_double_concat (<DWI>mode, operands[0], operands[1], operands[2]);
+ DONE;
})
;; Negation instructions
diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md
index d0bfddb..43cb7b5 100644
--- a/gcc/config/loongarch/constraints.md
+++ b/gcc/config/loongarch/constraints.md
@@ -20,14 +20,14 @@
;; Register constraints
-;; "a" "A constant call global and noplt address."
-;; "b" <-----unused
+;; "a" <-----unused
+;; "b" "A constant call not local address."
;; "c" "A constant call local address."
;; "d" <-----unused
;; "e" JIRL_REGS
;; "f" FP_REGS
;; "g" <-----unused
-;; "h" "A constant call plt address."
+;; "h" <-----unused
;; "i" "Matches a general integer constant." (Global non-architectural)
;; "j" SIBCALL_REGS
;; "k" "A memory operand whose address is formed by a base register and
@@ -42,7 +42,7 @@
;; "q" CSR_REGS
;; "r" GENERAL_REGS (Global non-architectural)
;; "s" "Matches a symbolic integer constant." (Global non-architectural)
-;; "t" "A constant call weak address"
+;; "t" <-----unused
;; "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)"
;; "v" "A signed 64-bit constant and low 44-bit is zero (for logic instructions)."
;; "w" "Matches any valid memory."
@@ -89,10 +89,10 @@
;; "<" "Matches a pre-dec or post-dec operand." (Global non-architectural)
;; ">" "Matches a pre-inc or post-inc operand." (Global non-architectural)
-(define_constraint "a"
+(define_constraint "b"
"@internal
- A constant call global and noplt address."
- (match_operand 0 "is_const_call_global_noplt_symbol"))
+ A constant call no local address."
+ (match_operand 0 "is_const_call_no_local_symbol"))
(define_constraint "c"
"@internal
@@ -105,11 +105,6 @@
(define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS"
"A floating-point register (if available).")
-(define_constraint "h"
- "@internal
- A constant call plt address."
- (match_operand 0 "is_const_call_plt_symbol"))
-
(define_register_constraint "j" "SIBCALL_REGS"
"@internal")
@@ -134,11 +129,6 @@
(define_register_constraint "q" "CSR_REGS"
"A general-purpose register except for $r0 and $r1 for lcsr.")
-(define_constraint "t"
- "@internal
- A constant call weak address."
- (match_operand 0 "is_const_call_weak_symbol"))
-
(define_constraint "u"
"A signed 52bit constant and low 32-bit is zero (for logic instructions)."
(and (match_code "const_int")
diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
index 61e7d72..6f39500 100644
--- a/gcc/config/loongarch/genopts/loongarch.opt.in
+++ b/gcc/config/loongarch/genopts/loongarch.opt.in
@@ -154,6 +154,10 @@ mmax-inline-memcpy-size=
Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024)
-mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024.
+mexplicit-relocs
+Target Var(TARGET_EXPLICIT_RELOCS) Init(1)
+Use %reloc() assembly operators.
+
; The code model option names for -mcmodel.
Enum
Name(cmodel) Type(int)
diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc
index eb9c2a5..fc477bf 100644
--- a/gcc/config/loongarch/loongarch-opts.cc
+++ b/gcc/config/loongarch/loongarch-opts.cc
@@ -376,6 +376,13 @@ fallback:
/* 5. Target code model */
t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
+ if (t.cmodel != CMODEL_NORMAL)
+ {
+ warning (0, "%qs is not supported, now cmodel is set to 'normal'.",
+ loongarch_cmodel_strings[t.cmodel]);
+ t.cmodel = CMODEL_NORMAL;
+ }
+
/* Cleanup and return. */
obstack_free (&msg_obstack, NULL);
diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h
index 2287fd37..cadaad7 100644
--- a/gcc/config/loongarch/loongarch-protos.h
+++ b/gcc/config/loongarch/loongarch-protos.h
@@ -27,9 +27,13 @@ along with GCC; see the file COPYING3. If not see
SYMBOL_GOT_DISP
The symbol's value will be loaded directly from the GOT.
+ SYMBOL_PCREL
+ The symbol's value will be loaded directly from data section.
+
SYMBOL_TLS
A thread-local symbol.
+ SYMBOL_TLS_IE
SYMBOL_TLSGD
SYMBOL_TLSLDM
UNSPEC wrappers around SYMBOL_TLS, corresponding to the
@@ -37,7 +41,10 @@ along with GCC; see the file COPYING3. If not see
*/
enum loongarch_symbol_type {
SYMBOL_GOT_DISP,
+ SYMBOL_PCREL,
SYMBOL_TLS,
+ SYMBOL_TLS_IE,
+ SYMBOL_TLS_LE,
SYMBOL_TLSGD,
SYMBOL_TLSLDM,
};
@@ -61,7 +68,6 @@ extern int loongarch_idiv_insns (machine_mode);
#ifdef RTX_CODE
extern void loongarch_emit_binary (enum rtx_code, rtx, rtx, rtx);
#endif
-extern bool loongarch_split_symbol (rtx, rtx, machine_mode, rtx *);
extern rtx loongarch_unspec_address (rtx, enum loongarch_symbol_type);
extern rtx loongarch_strip_unspec_address (rtx);
extern void loongarch_move_integer (rtx, rtx, unsigned HOST_WIDE_INT);
@@ -71,8 +77,6 @@ extern rtx loongarch_legitimize_call_address (rtx);
extern rtx loongarch_subword (rtx, bool);
extern bool loongarch_split_move_p (rtx, rtx);
extern void loongarch_split_move (rtx, rtx, rtx);
-extern bool loongarch_split_move_insn_p (rtx, rtx);
-extern void loongarch_split_move_insn (rtx, rtx, rtx);
extern const char *loongarch_output_move (rtx, rtx);
extern bool loongarch_cfun_has_cprestore_slot_p (void);
#ifdef RTX_CODE
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index 8b0d7f4..7968734 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -100,6 +100,10 @@ along with GCC; see the file COPYING3. If not see
ADDRESS_REG_REG
A base register indexed by (optionally scaled) register.
+ ADDRESS_LO_SUM
+ A LO_SUM rtx. The first operand is a valid base register and the second
+ operand is a symbolic address.
+
ADDRESS_CONST_INT
A signed 16-bit constant address.
@@ -109,24 +113,13 @@ enum loongarch_address_type
{
ADDRESS_REG,
ADDRESS_REG_REG,
+ ADDRESS_LO_SUM,
ADDRESS_CONST_INT,
ADDRESS_SYMBOLIC
};
-/* Information about an address described by loongarch_address_type.
-
- ADDRESS_CONST_INT
- No fields are used.
-
- ADDRESS_REG
- REG is the base register and OFFSET is the constant offset.
-
- ADDRESS_REG_REG
- A base register indexed by (optionally scaled) register.
-
- ADDRESS_SYMBOLIC
- SYMBOL_TYPE is the type of symbol that the address references. */
+/* Information about an address described by loongarch_address_type. */
struct loongarch_address_info
{
enum loongarch_address_type type;
@@ -1617,11 +1610,12 @@ loongarch_weak_symbol_p (const_rtx x)
bool
loongarch_symbol_binds_local_p (const_rtx x)
{
- if (LABEL_REF_P (x))
+ if (SYMBOL_REF_P (x))
+ return (SYMBOL_REF_DECL (x)
+ ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+ : SYMBOL_REF_LOCAL_P (x));
+ else
return false;
-
- return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
- : SYMBOL_REF_LOCAL_P (x));
}
/* Return true if rtx constants of mode MODE should be put into a small
@@ -1640,17 +1634,31 @@ static enum loongarch_symbol_type
loongarch_classify_symbol (const_rtx x)
{
if (LABEL_REF_P (x))
- return SYMBOL_GOT_DISP;
-
- gcc_assert (SYMBOL_REF_P (x));
+ return SYMBOL_PCREL;
if (SYMBOL_REF_TLS_MODEL (x))
return SYMBOL_TLS;
- if (SYMBOL_REF_P (x))
+ if (SYMBOL_REF_P (x)
+ && !loongarch_symbol_binds_local_p (x))
return SYMBOL_GOT_DISP;
- return SYMBOL_GOT_DISP;
+ return SYMBOL_PCREL;
+}
+
+/* Classify the base of symbolic expression X, given that X appears in
+ context CONTEXT. */
+
+static enum loongarch_symbol_type
+loongarch_classify_symbolic_expression (rtx x)
+{
+ rtx offset;
+
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ return UNSPEC_ADDRESS_TYPE (x);
+
+ return loongarch_classify_symbol (x);
}
/* Return true if X is a symbolic constant. If it is,
@@ -1683,9 +1691,15 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type)
relocations. */
switch (*symbol_type)
{
- case SYMBOL_GOT_DISP:
+ case SYMBOL_TLS_IE:
+ case SYMBOL_TLS_LE:
case SYMBOL_TLSGD:
case SYMBOL_TLSLDM:
+ case SYMBOL_PCREL:
+ /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */
+ return sext_hwi (INTVAL (offset), 32) == INTVAL (offset);
+
+ case SYMBOL_GOT_DISP:
case SYMBOL_TLS:
return false;
}
@@ -1702,14 +1716,19 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode)
case SYMBOL_GOT_DISP:
/* The constant will have to be loaded from the GOT before it
is used in an address. */
- if (mode != MAX_MACHINE_MODE)
+ if (!TARGET_EXPLICIT_RELOCS && mode != MAX_MACHINE_MODE)
return 0;
return 3;
+ case SYMBOL_PCREL:
+ case SYMBOL_TLS_IE:
+ case SYMBOL_TLS_LE:
+ return 2;
+
case SYMBOL_TLSGD:
case SYMBOL_TLSLDM:
- return 1;
+ return 3;
case SYMBOL_TLS:
/* We don't treat a bare TLS symbol as a constant. */
@@ -1815,6 +1834,84 @@ loongarch_valid_offset_p (rtx x, machine_mode mode)
return true;
}
+/* Should a symbol of type SYMBOL_TYPE should be split in two? */
+
+bool
+loongarch_split_symbol_type (enum loongarch_symbol_type symbol_type)
+{
+ switch (symbol_type)
+ {
+ case SYMBOL_PCREL:
+ case SYMBOL_GOT_DISP:
+ case SYMBOL_TLS_IE:
+ case SYMBOL_TLS_LE:
+ case SYMBOL_TLSGD:
+ case SYMBOL_TLSLDM:
+ return true;
+
+ case SYMBOL_TLS:
+ return false;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return true if a LO_SUM can address a value of mode MODE when the
+ LO_SUM symbol has type SYMBOL_TYPE. */
+
+static bool
+loongarch_valid_lo_sum_p (enum loongarch_symbol_type symbol_type,
+ machine_mode mode, rtx x)
+{
+ int align, size;
+
+ /* Check that symbols of type SYMBOL_TYPE can be used to access values
+ of mode MODE. */
+ if (loongarch_symbol_insns (symbol_type, mode) == 0)
+ return false;
+
+ /* Check that there is a known low-part relocation. */
+ if (!loongarch_split_symbol_type (symbol_type))
+ return false;
+
+ /* We can't tell size or alignment when we have BLKmode, so try extracing a
+ decl from the symbol if possible. */
+ if (mode == BLKmode)
+ {
+ rtx offset;
+
+ /* Extract the symbol from the LO_SUM operand, if any. */
+ split_const (x, &x, &offset);
+
+ /* Might be a CODE_LABEL. We can compute align but not size for that,
+ so don't bother trying to handle it. */
+ if (!SYMBOL_REF_P (x))
+ return false;
+
+ /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */
+ align = (SYMBOL_REF_DECL (x)
+ ? DECL_ALIGN (SYMBOL_REF_DECL (x))
+ : 1);
+ size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x))
+ ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x)))
+ : 2*BITS_PER_WORD);
+ }
+ else
+ {
+ align = GET_MODE_ALIGNMENT (mode);
+ size = GET_MODE_BITSIZE (mode);
+ }
+
+ /* We may need to split multiword moves, so make sure that each word
+ can be accessed without inducing a carry. */
+ if (size > BITS_PER_WORD
+ && (!TARGET_STRICT_ALIGN || size > align))
+ return false;
+
+ return true;
+}
+
static bool
loongarch_valid_index_p (struct loongarch_address_info *info, rtx x,
machine_mode mode, bool strict_p)
@@ -1881,6 +1978,26 @@ loongarch_classify_address (struct loongarch_address_info *info, rtx x,
info->offset = XEXP (x, 1);
return (loongarch_valid_base_register_p (info->reg, mode, strict_p)
&& loongarch_valid_offset_p (info->offset, mode));
+
+ case LO_SUM:
+ info->type = ADDRESS_LO_SUM;
+ info->reg = XEXP (x, 0);
+ info->offset = XEXP (x, 1);
+ /* We have to trust the creator of the LO_SUM to do something vaguely
+ sane. Target-independent code that creates a LO_SUM should also
+ create and verify the matching HIGH. Target-independent code that
+ adds an offset to a LO_SUM must prove that the offset will not
+ induce a carry. Failure to do either of these things would be
+ a bug, and we are not required to check for it here. The MIPS
+ backend itself should only create LO_SUMs for valid symbolic
+ constants, with the high part being either a HIGH or a copy
+ of _gp. */
+ info->symbol_type
+ = loongarch_classify_symbolic_expression (info->offset);
+ return (loongarch_valid_base_register_p (info->reg, mode, strict_p)
+ && loongarch_valid_lo_sum_p (info->symbol_type, mode,
+ info->offset));
+
default:
return false;
}
@@ -1937,14 +2054,13 @@ loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p)
switch (addr.type)
{
case ADDRESS_REG:
- return factor;
-
case ADDRESS_REG_REG:
- return factor;
-
case ADDRESS_CONST_INT:
return factor;
+ case ADDRESS_LO_SUM:
+ return factor + 1;
+
case ADDRESS_SYMBOLIC:
return factor * loongarch_symbol_insns (addr.symbol_type, mode);
}
@@ -1972,7 +2088,8 @@ loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits,
return loongarch_unsigned_immediate_p (x, bits, shift);
}
-/* Return true if X is a legitimate address with a 12-bit offset.
+/* Return true if X is a legitimate address with a 12-bit offset
+ or addr.type is ADDRESS_LO_SUM.
MODE is the mode of the value being accessed. */
bool
@@ -1981,9 +2098,10 @@ loongarch_12bit_offset_address_p (rtx x, machine_mode mode)
struct loongarch_address_info addr;
return (loongarch_classify_address (&addr, x, mode, false)
- && addr.type == ADDRESS_REG
- && CONST_INT_P (addr.offset)
- && LARCH_U12BIT_OFFSET_P (INTVAL (addr.offset)));
+ && ((addr.type == ADDRESS_REG
+ && CONST_INT_P (addr.offset)
+ && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset)))
+ || addr.type == ADDRESS_LO_SUM));
}
/* Return true if X is a legitimate address with a 14-bit offset shifted 2.
@@ -2001,6 +2119,9 @@ loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode)
&& LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset)));
}
+/* Return true if X is a legitimate address with base and index.
+ MODE is the mode of the value being accessed. */
+
bool
loongarch_base_index_address_p (rtx x, machine_mode mode)
{
@@ -2022,6 +2143,14 @@ loongarch_const_insns (rtx x)
switch (GET_CODE (x))
{
+ case HIGH:
+ if (!loongarch_symbolic_constant_p (XEXP (x, 0), &symbol_type)
+ || !loongarch_split_symbol_type (symbol_type))
+ return 0;
+
+ /* This is simply a PCALAU12I. */
+ return 1;
+
case CONST_INT:
return loongarch_integer_cost (INTVAL (x));
@@ -2082,6 +2211,8 @@ loongarch_split_const_insns (rtx x)
return low + high;
}
+static bool loongarch_split_move_insn_p (rtx dest, rtx src);
+
/* Return the number of instructions needed to implement INSN,
given that it loads from or stores to MEM. */
@@ -2199,6 +2330,15 @@ loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type)
return loongarch_unspec_address_offset (base, offset, symbol_type);
}
+/* Emit an instruction of the form (set TARGET SRC). */
+
+static rtx
+loongarch_emit_set (rtx target, rtx src)
+{
+ emit_insn (gen_rtx_SET (target, src));
+ return target;
+}
+
/* If OP is an UNSPEC address, return the address to which it refers,
otherwise return OP itself. */
@@ -2280,6 +2420,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
{
rtx loc, a0;
rtx_insn *insn;
+ rtx tmp = gen_reg_rtx (Pmode);
a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
@@ -2290,12 +2431,22 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
start_sequence ();
- if (type == SYMBOL_TLSLDM)
- emit_insn (loongarch_got_load_tls_ld (a0, loc));
- else if (type == SYMBOL_TLSGD)
- emit_insn (loongarch_got_load_tls_gd (a0, loc));
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ /* Split tls symbol to high and low. */
+ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (loc));
+ high = loongarch_force_temporary (tmp, high);
+ emit_insn (gen_tls_low (Pmode, a0, high, loc));
+ }
else
- gcc_unreachable ();
+ {
+ if (type == SYMBOL_TLSLDM)
+ emit_insn (loongarch_got_load_tls_ld (a0, loc));
+ else if (type == SYMBOL_TLSGD)
+ emit_insn (loongarch_got_load_tls_gd (a0, loc));
+ else
+ gcc_unreachable ();
+ }
insn = emit_call_insn (gen_call_value_internal (v0, loongarch_tls_symbol,
const0_rtx));
@@ -2315,7 +2466,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
static rtx
loongarch_legitimize_tls_address (rtx loc)
{
- rtx dest, tp, tmp;
+ rtx dest, tp, tmp, tmp1, tmp2, tmp3;
enum tls_model model = SYMBOL_REF_TLS_MODEL (loc);
rtx_insn *insn;
@@ -2336,21 +2487,45 @@ loongarch_legitimize_tls_address (rtx loc)
break;
case TLS_MODEL_INITIAL_EXEC:
- /* la.tls.ie; tp-relative add */
- tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
- tmp = gen_reg_rtx (Pmode);
- emit_insn (loongarch_got_load_tls_ie (tmp, loc));
- dest = gen_reg_rtx (Pmode);
- emit_insn (gen_add3_insn (dest, tmp, tp));
+ {
+ /* la.tls.ie; tp-relative add. */
+ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
+ tmp1 = gen_reg_rtx (Pmode);
+ dest = gen_reg_rtx (Pmode);
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_IE);
+ tmp3 = gen_reg_rtx (Pmode);
+ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2));
+ high = loongarch_force_temporary (tmp3, high);
+ emit_insn (gen_ld_from_got (Pmode, tmp1, high, tmp2));
+ }
+ else
+ emit_insn (loongarch_got_load_tls_ie (tmp1, loc));
+ emit_insn (gen_add3_insn (dest, tmp1, tp));
+ }
break;
case TLS_MODEL_LOCAL_EXEC:
- /* la.tls.le; tp-relative add */
- tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
- tmp = gen_reg_rtx (Pmode);
- emit_insn (loongarch_got_load_tls_le (tmp, loc));
- dest = gen_reg_rtx (Pmode);
- emit_insn (gen_add3_insn (dest, tmp, tp));
+ {
+ /* la.tls.le; tp-relative add. */
+ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
+ tmp1 = gen_reg_rtx (Pmode);
+ dest = gen_reg_rtx (Pmode);
+
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_LE);
+ tmp3 = gen_reg_rtx (Pmode);
+ rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2));
+ high = loongarch_force_temporary (tmp3, high);
+ emit_insn (gen_ori_l_lo12 (Pmode, tmp1, high, tmp2));
+ }
+ else
+ emit_insn (loongarch_got_load_tls_le (tmp1, loc));
+ emit_insn (gen_add3_insn (dest, tmp1, tp));
+
+ }
break;
default:
@@ -2399,6 +2574,68 @@ loongarch_force_address (rtx x, machine_mode mode)
return x;
}
+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
+ it appears in a MEM of that mode. Return true if ADDR is a legitimate
+ constant in that context and can be split into high and low parts.
+ If so, and if LOW_OUT is nonnull, emit the high part and store the
+ low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise.
+
+ Return false if build with '-mno-explicit-relocs'.
+
+ TEMP is as for loongarch_force_temporary and is used to load the high
+ part into a register.
+
+ When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be
+ a legitimize SET_SRC for an .md pattern, otherwise the low part
+ is guaranteed to be a legitimate address for mode MODE. */
+
+bool
+loongarch_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out)
+{
+ enum loongarch_symbol_type symbol_type;
+ rtx high;
+
+ /* If build with '-mno-explicit-relocs', don't split symbol. */
+ if (!TARGET_EXPLICIT_RELOCS)
+ return false;
+
+ if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE)
+ || !loongarch_symbolic_constant_p (addr, &symbol_type)
+ || loongarch_symbol_insns (symbol_type, mode) == 0
+ || !loongarch_split_symbol_type (symbol_type))
+ return false;
+
+ if (temp == NULL)
+ temp = gen_reg_rtx (Pmode);
+
+ /* Get the 12-31 bits of the address. */
+ high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+ high = loongarch_force_temporary (temp, high);
+
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_PCREL:
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
+
+ case SYMBOL_GOT_DISP:
+ /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */
+ {
+ rtx low = gen_rtx_LO_SUM (Pmode, high, addr);
+ rtx mem = gen_rtx_MEM (Pmode, low);
+ *low_out = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, mem),
+ UNSPEC_LOAD_FROM_GOT);
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return true;
+}
+
/* This function is used to implement LEGITIMIZE_ADDRESS. If X can
be legitimized in a way that the generic machinery might not expect,
return a new address, otherwise return NULL. MODE is the mode of
@@ -2414,6 +2651,10 @@ loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
if (loongarch_tls_symbol_p (x))
return loongarch_legitimize_tls_address (x);
+ /* See if the address can split into a high part and a LO_SUM. */
+ if (loongarch_split_symbol (NULL, x, mode, &addr))
+ return loongarch_force_address (addr, mode);
+
/* Handle BASE + OFFSET using loongarch_add_offset. */
loongarch_split_plus (x, &base, &offset);
if (offset != 0)
@@ -2501,6 +2742,13 @@ loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src)
return;
}
+ /* Split moves of symbolic constants into high and low. */
+ if (loongarch_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
+ {
+ loongarch_emit_set (dest, src);
+ return;
+ }
+
/* Generate the appropriate access sequences for TLS symbols. */
if (loongarch_tls_symbol_p (src))
{
@@ -3243,21 +3491,12 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_)
/* Return true if a move from SRC to DEST in INSN should be split. */
-bool
+static bool
loongarch_split_move_insn_p (rtx dest, rtx src)
{
return loongarch_split_move_p (dest, src);
}
-/* Split a move from SRC to DEST in INSN, given that
- loongarch_split_move_insn_p holds. */
-
-void
-loongarch_split_move_insn (rtx dest, rtx src, rtx insn)
-{
- loongarch_split_move (dest, src, insn);
-}
-
/* Implement TARGET_CONSTANT_ALIGNMENT. */
static HOST_WIDE_INT
@@ -3371,12 +3610,16 @@ loongarch_output_move (rtx dest, rtx src)
case 2:
return "st.h\t%z1,%0";
case 4:
- if (const_arith_operand (offset, Pmode))
+ /* Matching address type with a 12bit offset and
+ ADDRESS_LO_SUM. */
+ if (const_arith_operand (offset, Pmode)
+ || GET_CODE (offset) == LO_SUM)
return "st.w\t%z1,%0";
else
return "stptr.w\t%z1,%0";
case 8:
- if (const_arith_operand (offset, Pmode))
+ if (const_arith_operand (offset, Pmode)
+ || GET_CODE (offset) == LO_SUM)
return "st.d\t%z1,%0";
else
return "stptr.d\t%z1,%0";
@@ -3409,12 +3652,16 @@ loongarch_output_move (rtx dest, rtx src)
case 2:
return "ld.hu\t%0,%1";
case 4:
- if (const_arith_operand (offset, Pmode))
+ /* Matching address type with a 12bit offset and
+ ADDRESS_LO_SUM. */
+ if (const_arith_operand (offset, Pmode)
+ || GET_CODE (offset) == LO_SUM)
return "ld.w\t%0,%1";
else
return "ldptr.w\t%0,%1";
case 8:
- if (const_arith_operand (offset, Pmode))
+ if (const_arith_operand (offset, Pmode)
+ || GET_CODE (offset) == LO_SUM)
return "ld.d\t%0,%1";
else
return "ldptr.d\t%0,%1";
@@ -3423,6 +3670,21 @@ loongarch_output_move (rtx dest, rtx src)
}
}
+ if (src_code == HIGH)
+ {
+ rtx offset, x;
+ split_const (XEXP (src, 0), &x, &offset);
+ enum loongarch_symbol_type type = SYMBOL_PCREL;
+
+ if (UNSPEC_ADDRESS_P (x))
+ type = UNSPEC_ADDRESS_TYPE (x);
+
+ if (type == SYMBOL_TLS_LE)
+ return "lu12i.w\t%0,%h1";
+ else
+ return "pcalau12i\t%0,%h1";
+ }
+
if (src_code == CONST_INT)
{
if (LU12I_INT (src))
@@ -3436,56 +3698,17 @@ loongarch_output_move (rtx dest, rtx src)
else
gcc_unreachable ();
}
+ }
- if (symbolic_operand (src, VOIDmode))
- {
- if ((TARGET_CMODEL_TINY && (!loongarch_global_symbol_p (src)
- || loongarch_symbol_binds_local_p (src)))
- || (TARGET_CMODEL_TINY_STATIC && !loongarch_weak_symbol_p (src)))
- {
- /* The symbol must be aligned to 4 byte. */
- unsigned int align;
-
- if (LABEL_REF_P (src))
- align = 32 /* Whatever. */;
- else if (CONSTANT_POOL_ADDRESS_P (src))
- align = GET_MODE_ALIGNMENT (get_pool_mode (src));
- else if (TREE_CONSTANT_POOL_ADDRESS_P (src))
- {
- tree exp = SYMBOL_REF_DECL (src);
- align = TYPE_ALIGN (TREE_TYPE (exp));
- align = loongarch_constant_alignment (exp, align);
- }
- else if (SYMBOL_REF_DECL (src))
- align = DECL_ALIGN (SYMBOL_REF_DECL (src));
- else if (SYMBOL_REF_HAS_BLOCK_INFO_P (src)
- && SYMBOL_REF_BLOCK (src) != NULL)
- align = SYMBOL_REF_BLOCK (src)->alignment;
- else
- align = BITS_PER_UNIT;
-
- if (align % (4 * 8) == 0)
- return "pcaddi\t%0,%%pcrel(%1)>>2";
- }
- if (TARGET_CMODEL_TINY
- || TARGET_CMODEL_TINY_STATIC
- || TARGET_CMODEL_NORMAL
- || TARGET_CMODEL_LARGE)
- {
- if (!loongarch_global_symbol_p (src)
- || loongarch_symbol_binds_local_p (src))
- return "la.local\t%0,%1";
- else
- return "la.global\t%0,%1";
- }
- if (TARGET_CMODEL_EXTREME)
- {
- sorry ("Normal symbol loading not implemented in extreme mode.");
- gcc_unreachable ();
- }
-
- }
+ if (!TARGET_EXPLICIT_RELOCS
+ && dest_code == REG && symbolic_operand (src, VOIDmode))
+ {
+ if (loongarch_classify_symbol (src) == SYMBOL_PCREL)
+ return "la.local\t%0,%1";
+ else
+ return "la.global\t%0,%1";
}
+
if (src_code == REG && FP_REG_P (REGNO (src)))
{
if (dest_code == REG && FP_REG_P (REGNO (dest)))
@@ -3503,6 +3726,7 @@ loongarch_output_move (rtx dest, rtx src)
return dbl_p ? "fst.d\t%1,%0" : "fst.s\t%1,%0";
}
}
+
if (dest_code == REG && FP_REG_P (REGNO (dest)))
{
if (src_code == MEM)
@@ -3517,6 +3741,7 @@ loongarch_output_move (rtx dest, rtx src)
return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1";
}
}
+
gcc_unreachable ();
}
@@ -4345,29 +4570,75 @@ loongarch_memmodel_needs_release_fence (enum memmodel model)
}
}
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+ in context CONTEXT. HI_RELOC indicates a high-part reloc. */
+
+static void
+loongarch_print_operand_reloc (FILE *file, rtx op, bool hi_reloc)
+{
+ const char *reloc;
+
+ switch (loongarch_classify_symbolic_expression (op))
+ {
+ case SYMBOL_PCREL:
+ reloc = hi_reloc ? "%pc_hi20" : "%pc_lo12";
+ break;
+
+ case SYMBOL_GOT_DISP:
+ reloc = hi_reloc ? "%got_pc_hi20" : "%got_pc_lo12";
+ break;
+
+ case SYMBOL_TLS_IE:
+ reloc = hi_reloc ? "%ie_pc_hi20" : "%ie_pc_lo12";
+ break;
+
+ case SYMBOL_TLS_LE:
+ reloc = hi_reloc ? "%le_hi20" : "%le_lo12";
+ break;
+
+ case SYMBOL_TLSGD:
+ reloc = hi_reloc ? "%gd_pc_hi20" : "%got_pc_lo12";
+ break;
+
+ case SYMBOL_TLSLDM:
+ reloc = hi_reloc ? "%ld_pc_hi20" : "%got_pc_lo12";
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ fprintf (file, "%s(", reloc);
+ output_addr_const (file, loongarch_strip_unspec_address (op));
+ fputc (')', file);
+}
+
/* Implement TARGET_PRINT_OPERAND. The LoongArch-specific operand codes are:
- 'X' Print CONST_INT OP in hexadecimal format.
- 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
+ 'A' Print a _DB suffix if the memory model requires a release.
+ 'b' Print the address of a memory operand, without offset.
+ 'C' Print the integer branch condition for comparison OP.
'd' Print CONST_INT OP in decimal.
+ 'F' Print the FPU branch condition for comparison OP.
+ 'G' Print a DBAR insn if the memory model requires a release.
+ 'H' Print address 52-61bit relocation associated with OP.
+ 'h' Print the high-part relocation associated with OP.
+ 'i' Print i if the operand is not a register.
+ 'L' Print the low-part relocation associated with OP.
'm' Print one less than CONST_INT OP in decimal.
- 'y' Print exact log2 of CONST_INT OP in decimal.
- 'C' Print the integer branch condition for comparison OP.
'N' Print the inverse of the integer branch condition for comparison OP.
- 'F' Print the FPU branch condition for comparison OP.
- 'W' Print the inverse of the FPU branch condition for comparison OP.
'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...),
'z' for (eq:?I ...), 'n' for (ne:?I ...).
't' Like 'T', but with the EQ/NE cases reversed
- 'Y' Print loongarch_fp_conditions[INTVAL (OP)]
- 'Z' Print OP and a comma for 8CC, otherwise print nothing.
- 'z' Print $0 if OP is zero, otherwise print OP normally.
- 'b' Print the address of a memory operand, without offset.
'V' Print exact log2 of CONST_INT OP element 0 of a replicated
CONST_VECTOR in decimal.
- 'A' Print a _DB suffix if the memory model requires a release.
- 'G' Print a DBAR insn if the memory model requires a release.
- 'i' Print i if the operand is not a register. */
+ 'W' Print the inverse of the FPU branch condition for comparison OP.
+ 'X' Print CONST_INT OP in hexadecimal format.
+ 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
+ 'Y' Print loongarch_fp_conditions[INTVAL (OP)]
+ 'y' Print exact log2 of CONST_INT OP in decimal.
+ 'Z' Print OP and a comma for 8CC, otherwise print nothing.
+ 'z' Print $0 if OP is zero, otherwise print OP normally. */
static void
loongarch_print_operand (FILE *file, rtx op, int letter)
@@ -4385,18 +4656,13 @@ loongarch_print_operand (FILE *file, rtx op, int letter)
switch (letter)
{
- case 'X':
- if (CONST_INT_P (op))
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
- else
- output_operand_lossage ("invalid use of '%%%c'", letter);
+ case 'A':
+ if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op)))
+ fputs ("_db", file);
break;
- case 'x':
- if (CONST_INT_P (op))
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
- else
- output_operand_lossage ("invalid use of '%%%c'", letter);
+ case 'C':
+ loongarch_print_int_branch_condition (file, code, letter);
break;
case 'd':
@@ -4406,6 +4672,30 @@ loongarch_print_operand (FILE *file, rtx op, int letter)
output_operand_lossage ("invalid use of '%%%c'", letter);
break;
+ case 'F':
+ loongarch_print_float_branch_condition (file, code, letter);
+ break;
+
+ case 'G':
+ if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op)))
+ fputs ("dbar\t0", file);
+ break;
+
+ case 'h':
+ if (code == HIGH)
+ op = XEXP (op, 0);
+ loongarch_print_operand_reloc (file, op, true /* hi_reloc */);
+ break;
+
+ case 'i':
+ if (code != REG)
+ fputs ("i", file);
+ break;
+
+ case 'L':
+ loongarch_print_operand_reloc (file, op, false /* lo_reloc */);
+ break;
+
case 'm':
if (CONST_INT_P (op))
fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
@@ -4413,17 +4703,17 @@ loongarch_print_operand (FILE *file, rtx op, int letter)
output_operand_lossage ("invalid use of '%%%c'", letter);
break;
- case 'y':
- if (CONST_INT_P (op))
- {
- int val = exact_log2 (INTVAL (op));
- if (val != -1)
- fprintf (file, "%d", val);
- else
- output_operand_lossage ("invalid use of '%%%c'", letter);
- }
- else
- output_operand_lossage ("invalid use of '%%%c'", letter);
+ case 'N':
+ loongarch_print_int_branch_condition (file, reverse_condition (code),
+ letter);
+ break;
+
+ case 't':
+ case 'T':
+ {
+ int truth = (code == NE) == (letter == 'T');
+ fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file);
+ }
break;
case 'V':
@@ -4441,30 +4731,36 @@ loongarch_print_operand (FILE *file, rtx op, int letter)
output_operand_lossage ("invalid use of '%%%c'", letter);
break;
- case 'C':
- loongarch_print_int_branch_condition (file, code, letter);
- break;
-
- case 'N':
- loongarch_print_int_branch_condition (file, reverse_condition (code),
- letter);
+ case 'W':
+ loongarch_print_float_branch_condition (file, reverse_condition (code),
+ letter);
break;
- case 'F':
- loongarch_print_float_branch_condition (file, code, letter);
+ case 'x':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
break;
- case 'W':
- loongarch_print_float_branch_condition (file, reverse_condition (code),
- letter);
+ case 'X':
+ if (CONST_INT_P (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
break;
- case 'T':
- case 't':
- {
- int truth = (code == NE) == (letter == 'T');
- fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file);
- }
+ case 'y':
+ if (CONST_INT_P (op))
+ {
+ int val = exact_log2 (INTVAL (op));
+ if (val != -1)
+ fprintf (file, "%d", val);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ }
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
break;
case 'Y':
@@ -4481,21 +4777,6 @@ loongarch_print_operand (FILE *file, rtx op, int letter)
fputc (',', file);
break;
- case 'A':
- if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op)))
- fputs ("_db", file);
- break;
-
- case 'G':
- if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op)))
- fputs ("dbar\t0", file);
- break;
-
- case 'i':
- if (code != REG)
- fputs ("i", file);
- break;
-
default:
switch (code)
{
@@ -4555,6 +4836,11 @@ loongarch_print_operand_address (FILE *file, machine_mode /* mode */, rtx x)
reg_names[REGNO (addr.offset)]);
return;
+ case ADDRESS_LO_SUM:
+ fprintf (file, "%s,", reg_names[REGNO (addr.reg)]);
+ loongarch_print_operand_reloc (file, addr.offset, false /* hi_reloc */);
+ return;
+
case ADDRESS_CONST_INT:
fprintf (file, "%s,", reg_names[GP_REG_FIRST]);
output_addr_const (file, x);
@@ -5929,6 +6215,12 @@ loongarch_starting_frame_offset (void)
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init
+#undef TARGET_MIN_ANCHOR_OFFSET
+#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2)
+
+#undef TARGET_MAX_ANCHOR_OFFSET
+#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1)
+
#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv
diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h
index f9de9a6..89a5bd7 100644
--- a/gcc/config/loongarch/loongarch.h
+++ b/gcc/config/loongarch/loongarch.h
@@ -614,7 +614,7 @@ enum reg_class
#define LU12I_INT(X) LU12I_OPERAND (INTVAL (X))
#define LU32I_INT(X) LU32I_OPERAND (INTVAL (X))
#define LU52I_INT(X) LU52I_OPERAND (INTVAL (X))
-#define LARCH_U12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047))
+#define LARCH_12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047))
#define LARCH_9BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -256, 255))
#define LARCH_16BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -32768, 32767))
#define LARCH_SHIFT_2_OFFSET_P(OFFSET) (((OFFSET) & 0x3) == 0)
diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
index 5c0445d..6b6df22 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -57,6 +57,10 @@
;; CRC
UNSPEC_CRC
UNSPEC_CRCC
+
+ UNSPEC_LOAD_FROM_GOT
+ UNSPEC_ORI_L_LO12
+ UNSPEC_TLS_LOW
])
(define_c_enum "unspecv" [
@@ -1743,73 +1747,6 @@
[(set_attr "move_type" "move,load,store")
(set_attr "mode" "DF")])
-
-;; 128-bit integer moves
-
-(define_expand "movti"
- [(set (match_operand:TI 0)
- (match_operand:TI 1))]
- "TARGET_64BIT"
-{
- if (loongarch_legitimize_move (TImode, operands[0], operands[1]))
- DONE;
-})
-
-(define_insn "*movti"
- [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,r,m")
- (match_operand:TI 1 "move_operand" "r,i,m,rJ"))]
- "TARGET_64BIT
- && (register_operand (operands[0], TImode)
- || reg_or_0_operand (operands[1], TImode))"
- { return loongarch_output_move (operands[0], operands[1]); }
- [(set_attr "move_type" "move,const,load,store")
- (set (attr "mode")
- (if_then_else (eq_attr "move_type" "imul")
- (const_string "SI")
- (const_string "TI")))])
-
-;; 128-bit floating point moves
-
-(define_expand "movtf"
- [(set (match_operand:TF 0)
- (match_operand:TF 1))]
- "TARGET_64BIT"
-{
- if (loongarch_legitimize_move (TFmode, operands[0], operands[1]))
- DONE;
-})
-
-;; This pattern handles both hard- and soft-float cases.
-(define_insn "*movtf"
- [(set (match_operand:TF 0 "nonimmediate_operand" "=r,r,m,f,r,f,m")
- (match_operand:TF 1 "move_operand" "rG,m,rG,rG,f,m,f"))]
- "TARGET_64BIT
- && (register_operand (operands[0], TFmode)
- || reg_or_0_operand (operands[1], TFmode))"
- "#"
- [(set_attr "move_type" "move,load,store,mgtf,mftg,fpload,fpstore")
- (set_attr "mode" "TF")])
-
-(define_split
- [(set (match_operand:MOVE64 0 "nonimmediate_operand")
- (match_operand:MOVE64 1 "move_operand"))]
- "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])"
- [(const_int 0)]
-{
- loongarch_split_move_insn (operands[0], operands[1], curr_insn);
- DONE;
-})
-
-(define_split
- [(set (match_operand:MOVE128 0 "nonimmediate_operand")
- (match_operand:MOVE128 1 "move_operand"))]
- "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])"
- [(const_int 0)]
-{
- loongarch_split_move_insn (operands[0], operands[1], curr_insn);
- DONE;
-})
-
;; Emit a doubleword move in which exactly one of the operands is
;; a floating-point register. We can't just emit two normal moves
;; because of the constraints imposed by the FPU register model;
@@ -1938,6 +1875,57 @@
[(set_attr "type" "arith")
(set_attr "mode" "DI")])
+;; Instructions for adding the low 12 bits of an address to a register.
+;; Operand 2 is the address: loongarch_print_operand works out which relocation
+;; should be applied.
+
+(define_insn "*low<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (lo_sum:P (match_operand:P 1 "register_operand" " r")
+ (match_operand:P 2 "symbolic_operand" "")))]
+ "TARGET_EXPLICIT_RELOCS"
+ "addi.<d>\t%0,%1,%L2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+(define_insn "@tls_low<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P [(mem:P (lo_sum:P (match_operand:P 1 "register_operand" "r")
+ (match_operand:P 2 "symbolic_operand" "")))]
+ UNSPEC_TLS_LOW))]
+ "TARGET_EXPLICIT_RELOCS"
+ "addi.<d>\t%0,%1,%L2"
+ [(set_attr "type" "arith")
+ (set_attr "mode" "<MODE>")])
+
+;; Instructions for loading address from GOT entry.
+;; operands[1] is pc plus the high half of the address difference with the got
+;; entry;
+;; operands[2] is low 12 bits for low 12 bit of the address difference with the
+;; got entry.
+;; loongarch_print_operand works out which relocation should be applied.
+
+(define_insn "@ld_from_got<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P [(mem:P (lo_sum:P
+ (match_operand:P 1 "register_operand" "r")
+ (match_operand:P 2 "symbolic_operand")))]
+ UNSPEC_LOAD_FROM_GOT))]
+ "TARGET_EXPLICIT_RELOCS"
+ "ld.<d>\t%0,%1,%L2"
+ [(set_attr "type" "move")]
+)
+
+(define_insn "@ori_l_lo12<mode>"
+ [(set (match_operand:P 0 "register_operand" "=r")
+ (unspec:P [(match_operand:P 1 "register_operand" "r")
+ (match_operand:P 2 "symbolic_operand")]
+ UNSPEC_ORI_L_LO12))]
+ ""
+ "ori\t%0,%1,%L2"
+ [(set_attr "type" "move")]
+)
+
;; Convert floating-point numbers to integers
(define_insn "frint_<fmt>"
[(set (match_operand:ANYF 0 "register_operand" "=f")
@@ -2844,48 +2832,14 @@
})
(define_insn "sibcall_internal"
- [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,a,t,h"))
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,b"))
(match_operand 1 "" ""))]
"SIBLING_CALL_P (insn)"
-{
- switch (which_alternative)
- {
- case 0:
- return "jr\t%0";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,(%%pcrel(%0+0x20000))>>18\n\t"
- "jirl\t$r0,$r12,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r12,$r13,%0\n\tjr\t$r12";
- else
- return "b\t%0";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "b\t%0";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
- else
- return "la.global\t$r12,%0\n\tjr\t$r12";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%0\n\tjr\t$r12";
- else
- return "la.global\t$r12,%0\n\tjr\t$r12";
- case 4:
- if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return "b\t%%plt(%0)";
- else if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,(%%plt(%0)+0x20000)>>18\n\t"
- "jirl\t$r0,$r12,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+ "@
+ jr\t%0
+ b\t%0
+ b\t%%plt(%0)"
+ [(set_attr "jirl" "indirect,direct,direct")])
(define_expand "sibcall_value"
[(parallel [(set (match_operand 0 "")
@@ -2920,96 +2874,28 @@
(define_insn "sibcall_value_internal"
[(set (match_operand 0 "register_operand" "")
- (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b"))
(match_operand 2 "" "")))]
"SIBLING_CALL_P (insn)"
-{
- switch (which_alternative)
- {
- case 0:
- return "jr\t%1";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
- "jirl\t$r0,$r12,%%pcrel(%1+4)-((%%pcrel(%1+4+0x20000))>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "b\t%1";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "b\t%1";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "la.global\t$r12,%1\n\tjr\t$r12";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "la.global\t$r12,%1\n\tjr\t$r12";
- case 4:
- if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return " b\t%%plt(%1)";
- else if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
- "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+ "@
+ jr\t%1
+ b\t%1
+ b\t%%plt(%1)"
+ [(set_attr "jirl" "indirect,direct,direct")])
(define_insn "sibcall_value_multiple_internal"
[(set (match_operand 0 "register_operand" "")
- (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h"))
+ (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b"))
(match_operand 2 "" "")))
(set (match_operand 3 "register_operand" "")
(call (mem:SI (match_dup 1))
(match_dup 2)))]
"SIBLING_CALL_P (insn)"
-{
- switch (which_alternative)
- {
- case 0:
- return "jr\t%1";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t"
- "jirl\t$r0,$r12,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "b\t%1";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "b\t%1";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "la.global\t$r12,%1\n\tjr\t$r12";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r12,$r13,%1\n\tjr\t$r12";
- else
- return "la.global\t$r12,%1\n\tjr\t$r12";
- case 4:
- if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return "b\t%%plt(%1)";
- else if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t"
- "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")])
+ "@
+ jr\t%1
+ b\t%1
+ b\t%%plt(%1)"
+ [(set_attr "jirl" "indirect,direct,direct")])
(define_expand "call"
[(parallel [(call (match_operand 0 "")
@@ -3025,50 +2911,15 @@
})
(define_insn "call_internal"
- [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,a,t,h"))
+ [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,b"))
(match_operand 1 "" ""))
(clobber (reg:SI RETURN_ADDR_REGNUM))]
""
-{
- switch (which_alternative)
- {
- case 0:
- return "jirl\t$r1,%0,0";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,%%pcrel(%0+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
- else
- return "bl\t%0";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "bl\t%0";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
- else
- return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0";
- else
- return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0";
- case 4:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,(%%plt(%0)+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)";
- else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return "bl\t%%plt(%0)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")
- (set_attr "insn_count" "1,2,3,3,2")])
+ "@
+ jirl\t$r1,%0,0
+ bl\t%0
+ bl\t%%plt(%0)"
+ [(set_attr "jirl" "indirect,direct,direct")])
(define_expand "call_value"
[(parallel [(set (match_operand 0 "")
@@ -3101,100 +2952,30 @@
(define_insn "call_value_internal"
[(set (match_operand 0 "register_operand" "")
- (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b"))
(match_operand 2 "" "")))
(clobber (reg:SI RETURN_ADDR_REGNUM))]
""
-{
- switch (which_alternative)
- {
- case 0:
- return "jirl\t$r1,%1,0";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
- else
- return "bl\t%1";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "bl\t%1";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
- else
- return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
- else
- return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
- case 4:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
- else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return "bl\t%%plt(%1)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")
- (set_attr "insn_count" "1,2,3,3,2")])
+ "@
+ jirl\t$r1,%1,0
+ bl\t%1
+ bl\t%%plt(%1)"
+ [(set_attr "jirl" "indirect,direct,direct")])
(define_insn "call_value_multiple_internal"
[(set (match_operand 0 "register_operand" "")
- (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h"))
+ (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b"))
(match_operand 2 "" "")))
(set (match_operand 3 "register_operand" "")
(call (mem:SI (match_dup 1))
(match_dup 2)))
(clobber (reg:SI RETURN_ADDR_REGNUM))]
""
-{
- switch (which_alternative)
- {
- case 0:
- return "jirl\t$r1,%1,0";
- case 1:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)";
- else if (TARGET_CMODEL_EXTREME)
- return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
- else
- return "bl\t%1";
- case 2:
- if (TARGET_CMODEL_TINY_STATIC)
- return "bl\t%1";
- else if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0 ";
- else
- return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
- case 3:
- if (TARGET_CMODEL_EXTREME)
- return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0";
- else
- return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0";
- case 4:
- if (TARGET_CMODEL_LARGE)
- return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t"
- "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)";
- else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY)
- return "bl\t%%plt(%1)";
- else
- /* Cmodel extreme and tiny static not support plt. */
- gcc_unreachable ();
- default:
- gcc_unreachable ();
- }
-}
- [(set_attr "jirl" "indirect,direct,direct,direct,direct")
- (set_attr "insn_count" "1,2,3,3,2")])
+ "@
+ jirl\t$r1,%1,0
+ bl\t%1
+ bl\t%%plt(%1)"
+ [(set_attr "jirl" "indirect,direct,direct")])
;; Call subroutine returning any type.
diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
index 3ff0d86..7a8c5b4 100644
--- a/gcc/config/loongarch/loongarch.opt
+++ b/gcc/config/loongarch/loongarch.opt
@@ -161,6 +161,10 @@ mmax-inline-memcpy-size=
Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024)
-mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024.
+mexplicit-relocs
+Target Var(TARGET_EXPLICIT_RELOCS) Init(1)
+Use %reloc() assembly operators.
+
; The code model option names for -mcmodel.
Enum
Name(cmodel) Type(int)
diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
index edd74d4..cd3528c 100644
--- a/gcc/config/loongarch/predicates.md
+++ b/gcc/config/loongarch/predicates.md
@@ -110,21 +110,30 @@
(define_predicate "const_call_insn_operand"
(match_code "const,symbol_ref,label_ref")
{
+ /* Split symbol to high and low if return false.
+ If defined TARGET_CMODEL_LARGE, all symbol would be splited,
+ else if offset is not zero, the symbol would be splited. */
+
enum loongarch_symbol_type symbol_type;
+ loongarch_symbolic_constant_p (op, &symbol_type);
+
+ rtx offset, x = op;
+ split_const (x, &x, &offset);
- if (!loongarch_symbolic_constant_p (op, &symbol_type))
+ if (offset != const0_rtx)
return false;
switch (symbol_type)
{
- case SYMBOL_GOT_DISP:
- /* Without explicit relocs, there is no special syntax for
- loading the address of a call destination into a register.
- Using "la.global JIRL_REGS,foo; jirl JIRL_REGS" would prevent the lazy
- binding of "foo", so keep the address of global symbols with the jirl
- macro. */
+ case SYMBOL_PCREL:
return 1;
+ case SYMBOL_GOT_DISP:
+ if (TARGET_CMODEL_LARGE || !flag_plt)
+ return false;
+ else
+ return 1;
+
default:
return false;
}
@@ -140,22 +149,11 @@
(match_test "loongarch_symbol_binds_local_p (op) != 0"))
(match_test "CONSTANT_P (op)")))
-(define_predicate "is_const_call_weak_symbol"
- (and (match_operand 0 "const_call_insn_operand")
- (not (match_operand 0 "is_const_call_local_symbol"))
- (match_test "loongarch_weak_symbol_p (op) != 0")
- (match_test "CONSTANT_P (op)")))
-
-(define_predicate "is_const_call_plt_symbol"
- (and (match_operand 0 "const_call_insn_operand")
- (match_test "flag_plt != 0")
- (match_test "loongarch_global_symbol_noweak_p (op) != 0")
- (match_test "CONSTANT_P (op)")))
-
-(define_predicate "is_const_call_global_noplt_symbol"
+(define_predicate "is_const_call_no_local_symbol"
(and (match_operand 0 "const_call_insn_operand")
- (match_test "flag_plt == 0")
- (match_test "loongarch_global_symbol_noweak_p (op) != 0")
+ (ior (match_test "loongarch_global_symbol_p (op) != 0")
+ (match_test "loongarch_symbol_binds_local_p (op) == 0")
+ (match_test "loongarch_weak_symbol_p (op) != 0"))
(match_test "CONSTANT_P (op)")))
;; A legitimate CONST_INT operand that takes more than one instruction
@@ -219,7 +217,19 @@
case CONST:
case SYMBOL_REF:
case LABEL_REF:
- return (loongarch_symbolic_constant_p (op, &symbol_type));
+ return (loongarch_symbolic_constant_p (op, &symbol_type)
+ && (!TARGET_EXPLICIT_RELOCS
+ || !loongarch_split_symbol_type (symbol_type)));
+
+ case HIGH:
+ /* '-mno-explicit-relocs' don't generate high/low pairs. */
+ if (!TARGET_EXPLICIT_RELOCS)
+ return false;
+
+ op = XEXP (op, 0);
+ return (loongarch_symbolic_constant_p (op, &symbol_type)
+ && loongarch_split_symbol_type (symbol_type));
+
default:
return true;
}
diff --git a/gcc/config/rs6000/rs6000-p8swap.cc b/gcc/config/rs6000/rs6000-p8swap.cc
index 275702f..19fbbfb 100644
--- a/gcc/config/rs6000/rs6000-p8swap.cc
+++ b/gcc/config/rs6000/rs6000-p8swap.cc
@@ -1690,7 +1690,15 @@ replace_swapped_aligned_store (swap_web_entry *insn_entry,
gcc_assert ((GET_CODE (new_body) == SET)
&& MEM_P (SET_DEST (new_body)));
- set_block_for_insn (new_insn, BLOCK_FOR_INSN (store_insn));
+ basic_block bb = BLOCK_FOR_INSN (store_insn);
+ set_block_for_insn (new_insn, bb);
+ /* Handle REG_EH_REGION note. */
+ if (cfun->can_throw_non_call_exceptions && BB_END (bb) == store_insn)
+ {
+ rtx note = find_reg_note (store_insn, REG_EH_REGION, NULL_RTX);
+ if (note)
+ add_reg_note (new_insn, REG_EH_REGION, XEXP (note, 0));
+ }
df_insn_rescan (new_insn);
df_insn_delete (store_insn);
@@ -1784,7 +1792,15 @@ replace_swapped_aligned_load (swap_web_entry *insn_entry, rtx swap_insn)
gcc_assert ((GET_CODE (new_body) == SET)
&& MEM_P (SET_SRC (new_body)));
- set_block_for_insn (new_insn, BLOCK_FOR_INSN (def_insn));
+ basic_block bb = BLOCK_FOR_INSN (def_insn);
+ set_block_for_insn (new_insn, bb);
+ /* Handle REG_EH_REGION note. */
+ if (cfun->can_throw_non_call_exceptions && BB_END (bb) == def_insn)
+ {
+ rtx note = find_reg_note (def_insn, REG_EH_REGION, NULL_RTX);
+ if (note)
+ add_reg_note (new_insn, REG_EH_REGION, XEXP (note, 0));
+ }
df_insn_rescan (new_insn);
df_insn_delete (def_insn);
diff --git a/gcc/config/rs6000/rtems.h b/gcc/config/rs6000/rtems.h
index 8aa41d5..d529e22 100644
--- a/gcc/config/rs6000/rtems.h
+++ b/gcc/config/rs6000/rtems.h
@@ -23,6 +23,9 @@
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
+/* Undef gnu-user.h macro we don't want. */
+#undef CPLUSPLUS_CPP_SPEC
+
/* Copy and paste from linux64.h and freebsd64.h */
#ifdef IN_LIBGCC2
#undef TARGET_64BIT
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index f5ff8de..208275e 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@
+2022-07-26 Jason Merrill <jason@redhat.com>
+
+ PR c++/106230
+ * semantics.cc (finish_for_stmt): Check stmts_are_full_exprs_p.
+
+2022-07-25 Jason Merrill <jason@redhat.com>
+
+ PR c++/87729
+ * class.cc (warn_hidden): Remove shortcut.
+
2022-07-22 Patrick Palka <ppalka@redhat.com>
PR c++/106366
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index eb69e7f..a12d367 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -3040,22 +3040,25 @@ warn_hidden (tree t)
bool seen_non_override = false;
for (tree fndecl : ovl_range (fns))
{
+ bool any_override = false;
if (TREE_CODE (fndecl) == FUNCTION_DECL
&& DECL_VINDEX (fndecl))
{
/* If the method from the base class has the same
signature as the method from the derived class, it
- has been overridden. */
+ has been overridden. Note that we can't move on
+ after finding one match: fndecl might override
+ multiple base fns. */
for (size_t k = 0; k < base_fndecls.length (); k++)
if (base_fndecls[k]
&& same_signature_p (fndecl, base_fndecls[k]))
{
base_fndecls[k] = NULL_TREE;
- goto next;
+ any_override = true;
}
}
- seen_non_override = true;
- next:;
+ if (!any_override)
+ seen_non_override = true;
}
if (!seen_non_override && warn_overloaded_virtual == 1)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 96037c2..16dea05 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1411,6 +1411,11 @@ finish_for_stmt (tree for_stmt)
add_stmt (do_poplevel (scope));
+ /* If we're being called from build_vec_init, don't mess with the names of
+ the variables for an enclosing range-for. */
+ if (!stmts_are_full_exprs_p ())
+ return;
+
for (int i = 0; i < 3; i++)
if (range_for_decl[i])
DECL_NAME (range_for_decl[i])
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 0b48bdb..e190b93 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -534,7 +534,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
int i = 0;
if (aref.offmax[i] < -aref.sizrng[1] || aref.offmax[i = 1] > ubound)
{
- HOST_WIDE_INT tmpidx = aref.offmax[i].to_shwi () / eltsize.to_shwi ();
+ HOST_WIDE_INT tmpidx = (aref.offmax[i] / eltsize).to_shwi ();
if (warning_at (location, OPT_Warray_bounds,
"intermediate array offset %wi is outside array bounds "
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index 7ac4830..eb347ee 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -201,7 +201,9 @@ bool
gimple_ranger::range_on_edge (vrange &r, edge e, tree name)
{
Value_Range edge_range (TREE_TYPE (name));
- gcc_checking_assert (r.supports_type_p (TREE_TYPE (name)));
+
+ if (!r.supports_type_p (TREE_TYPE (name)))
+ return false;
// Do not process values along abnormal edges.
if (e->flags & EDGE_ABNORMAL)
diff --git a/gcc/match.pd b/gcc/match.pd
index 9736393..330c1db 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1946,7 +1946,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
/* ~(a ^ b) is a == b for truth valued a and b. */
(simplify
(bit_not (bit_xor:s truth_valued_p@0 truth_valued_p@1))
- (if (!VECTOR_TYPE_P (type))
+ (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+ && TYPE_PRECISION (TREE_TYPE (@0)) == 1)
(convert (eq @0 @1))))
/* (x & ~m) | (y & m) -> ((x ^ y) & m) ^ x */
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
new file mode 100644
index 0000000..8e9d83e
--- /dev/null
+++ b/gcc/range-op-float.cc
@@ -0,0 +1,206 @@
+/* Floating point range operators.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ Contributed by Aldy Hernandez <aldyh@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 "insn-codes.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.h"
+#include "cfghooks.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "optabs-tree.h"
+#include "gimple-pretty-print.h"
+#include "diagnostic-core.h"
+#include "flags.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "cfganal.h"
+#include "gimple-iterator.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-walk.h"
+#include "tree-cfg.h"
+#include "wide-int.h"
+#include "value-relation.h"
+#include "range-op.h"
+
+// Default definitions for floating point operators.
+
+bool
+range_operator_float::fold_range (frange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lh ATTRIBUTE_UNUSED,
+ const frange &rh ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+bool
+range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lh ATTRIBUTE_UNUSED,
+ const frange &rh ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+bool
+range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+bool
+range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const irange &lhs ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+bool
+range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+bool
+range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const irange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ relation_kind rel ATTRIBUTE_UNUSED) const
+{
+ return false;
+}
+
+relation_kind
+range_operator_float::lhs_op1_relation (const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
+{
+ return VREL_VARYING;
+}
+
+relation_kind
+range_operator_float::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
+{
+ return VREL_VARYING;
+}
+
+relation_kind
+range_operator_float::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
+{
+ return VREL_VARYING;
+}
+
+relation_kind
+range_operator_float::lhs_op2_relation (const frange &lhs ATTRIBUTE_UNUSED,
+ const frange &op1 ATTRIBUTE_UNUSED,
+ const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const
+{
+ return VREL_VARYING;
+}
+
+relation_kind
+range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const
+{
+ return VREL_VARYING;
+}
+
+class foperator_identity : public range_operator_float
+{
+ using range_operator_float::fold_range;
+ using range_operator_float::op1_range;
+
+ bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED,
+ const frange &op1, const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const final override
+ {
+ r = op1;
+ return true;
+ }
+ bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED,
+ const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED,
+ relation_kind) const final override
+ {
+ r = lhs;
+ return true;
+ }
+public:
+} fop_identity;
+
+
+// Instantiate a range_op_table for floating point operations.
+static floating_op_table global_floating_table;
+
+// Pointer to the float table so the dispatch code can access it.
+floating_op_table *floating_tree_table = &global_floating_table;
+
+floating_op_table::floating_op_table ()
+{
+ set (SSA_NAME, fop_identity);
+ set (PAREN_EXPR, fop_identity);
+ set (OBJ_TYPE_REF, fop_identity);
+ set (REAL_CST, fop_identity);
+}
+
+// Return a pointer to the range_operator_float instance, if there is
+// one associated with tree_code CODE.
+
+range_operator_float *
+floating_op_table::operator[] (enum tree_code code)
+{
+ return m_range_tree[code];
+}
+
+// Add OP to the handler table for CODE.
+
+void
+floating_op_table::set (enum tree_code code, range_operator_float &op)
+{
+ gcc_checking_assert (m_range_tree[code] == NULL);
+ m_range_tree[code] = &op;
+}
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index e184129..dfdd971 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -4152,28 +4152,56 @@ get_handler (enum tree_code code, tree type)
return NULL;
}
+// Return the floating point operator for CODE or NULL if none available.
+
+static inline range_operator_float *
+get_float_handler (enum tree_code code, tree)
+{
+ return (*floating_tree_table)[code];
+}
+
range_op_handler::range_op_handler (tree_code code, tree type)
+ : m_code (code), m_type (type)
{
- m_op = get_handler (code, type);
}
range_op_handler::range_op_handler (const gimple *s)
{
if (const gassign *ass = dyn_cast<const gassign *> (s))
{
- enum tree_code code = gimple_assign_rhs_code (ass);
+ m_code = gimple_assign_rhs_code (ass);
// The LHS of a comparison is always an int, so we must look at
// the operands.
- if (TREE_CODE_CLASS (code) == tcc_comparison)
- m_op = get_handler (code, TREE_TYPE (gimple_assign_rhs1 (ass)));
+ if (TREE_CODE_CLASS (m_code) == tcc_comparison)
+ m_type = TREE_TYPE (gimple_assign_rhs1 (ass));
else
- m_op = get_handler (code, TREE_TYPE (gimple_assign_lhs (ass)));
+ m_type = TREE_TYPE (gimple_assign_lhs (ass));
}
else if (const gcond *cond = dyn_cast<const gcond *> (s))
- m_op = get_handler (gimple_cond_code (cond),
- TREE_TYPE (gimple_cond_lhs (cond)));
+ {
+ m_code = gimple_cond_code (cond);
+ m_type = TREE_TYPE (gimple_cond_lhs (cond));
+ }
else
- m_op = NULL;
+ {
+ // A null type means there is no handler for this combination,
+ // but the decision whether there is one or not, is delayed
+ // until operator bool below is queried.
+ m_code = NOP_EXPR;
+ m_type = nullptr;
+ }
+}
+
+// Return TRUE if there is a handler available for the current
+// combination of tree_code and type.
+
+range_op_handler::operator bool () const
+{
+ if (!m_type)
+ return false;
+ if (frange::supports_p (m_type))
+ return get_float_handler (m_code, m_type);
+ return get_handler (m_code, m_type);
}
bool
@@ -4182,10 +4210,24 @@ range_op_handler::fold_range (vrange &r, tree type,
const vrange &rh,
relation_kind rel) const
{
- if (is_a <irange> (lh))
- return m_op->fold_range (as_a <irange> (r), type,
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->fold_range (as_a <irange> (r), type,
as_a <irange> (lh),
as_a <irange> (rh), rel);
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ if (is_a <irange> (r))
+ return op->fold_range (as_a <irange> (r), type,
+ as_a <frange> (lh),
+ as_a <frange> (rh), rel);
+ return op->fold_range (as_a <frange> (r), type,
+ as_a <frange> (lh),
+ as_a <frange> (rh), rel);
+ }
gcc_unreachable ();
return false;
}
@@ -4196,10 +4238,24 @@ range_op_handler::op1_range (vrange &r, tree type,
const vrange &op2,
relation_kind rel) const
{
- if (is_a <irange> (r))
- return m_op->op1_range (as_a <irange> (r), type,
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->op1_range (as_a <irange> (r), type,
as_a <irange> (lhs),
as_a <irange> (op2), rel);
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ if (is_a <irange> (lhs))
+ return op->op1_range (as_a <frange> (r), type,
+ as_a <irange> (lhs),
+ as_a <frange> (op2), rel);
+ return op->op1_range (as_a <frange> (r), type,
+ as_a <frange> (lhs),
+ as_a <frange> (op2), rel);
+ }
gcc_unreachable ();
return false;
}
@@ -4210,10 +4266,24 @@ range_op_handler::op2_range (vrange &r, tree type,
const vrange &op1,
relation_kind rel) const
{
- if (is_a <irange> (r))
- return m_op->op2_range (as_a <irange> (r), type,
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->op2_range (as_a <irange> (r), type,
as_a <irange> (lhs),
as_a <irange> (op1), rel);
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ if (is_a <irange> (lhs))
+ return op->op2_range (as_a <frange> (r), type,
+ as_a <irange> (lhs),
+ as_a <frange> (op1), rel);
+ return op->op2_range (as_a <frange> (r), type,
+ as_a <frange> (lhs),
+ as_a <frange> (op1), rel);
+ }
gcc_unreachable ();
return false;
}
@@ -4224,9 +4294,24 @@ range_op_handler::lhs_op1_relation (const vrange &lhs,
const vrange &op2,
relation_kind rel) const
{
- if (is_a <irange> (op1))
- return m_op->lhs_op1_relation (as_a <irange> (lhs),
- as_a <irange> (op1), as_a <irange> (op2), rel);
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->lhs_op1_relation (as_a <irange> (lhs),
+ as_a <irange> (op1),
+ as_a <irange> (op2), rel);
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ if (is_a <irange> (lhs))
+ return op->lhs_op1_relation (as_a <irange> (lhs),
+ as_a <frange> (op1),
+ as_a <frange> (op2), rel);
+ return op->lhs_op1_relation (as_a <frange> (lhs),
+ as_a <frange> (op1),
+ as_a <frange> (op2), rel);
+ }
gcc_unreachable ();
return VREL_VARYING;
}
@@ -4237,9 +4322,24 @@ range_op_handler::lhs_op2_relation (const vrange &lhs,
const vrange &op2,
relation_kind rel) const
{
- if (is_a <irange> (op1))
- return m_op->lhs_op2_relation (as_a <irange> (lhs),
- as_a <irange> (op1), as_a <irange> (op2), rel);
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->lhs_op2_relation (as_a <irange> (lhs),
+ as_a <irange> (op1),
+ as_a <irange> (op2), rel);
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ if (is_a <irange> (lhs))
+ return op->lhs_op2_relation (as_a <irange> (lhs),
+ as_a <frange> (op1),
+ as_a <frange> (op2), rel);
+ return op->lhs_op2_relation (as_a <frange> (lhs),
+ as_a <frange> (op1),
+ as_a <frange> (op2), rel);
+ }
gcc_unreachable ();
return VREL_VARYING;
}
@@ -4247,7 +4347,18 @@ range_op_handler::lhs_op2_relation (const vrange &lhs,
relation_kind
range_op_handler::op1_op2_relation (const vrange &lhs) const
{
- return m_op->op1_op2_relation (as_a <irange> (lhs));
+ if (irange::supports_p (m_type))
+ {
+ range_operator *op = get_handler (m_code, m_type);
+ return op->op1_op2_relation (as_a <irange> (lhs));
+ }
+ if (frange::supports_p (m_type))
+ {
+ range_operator_float *op = get_float_handler (m_code, m_type);
+ return op->op1_op2_relation (as_a <irange> (lhs));
+ }
+ gcc_unreachable ();
+ return VREL_VARYING;
}
// Cast the range in R to TYPE.
diff --git a/gcc/range-op.h b/gcc/range-op.h
index 262c796..37d9aa9 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -108,12 +108,61 @@ protected:
const wide_int &rh_ub) const;
};
+// Like range_operator above, but for floating point operators.
+
+class range_operator_float
+{
+public:
+ virtual bool fold_range (frange &r, tree type,
+ const frange &lh,
+ const frange &rh,
+ relation_kind rel = VREL_VARYING) const;
+ virtual bool fold_range (irange &r, tree type,
+ const frange &lh,
+ const frange &rh,
+ relation_kind rel = VREL_VARYING) const;
+ virtual bool op1_range (frange &r, tree type,
+ const frange &lhs,
+ const frange &op2,
+ relation_kind rel = VREL_VARYING) const;
+ virtual bool op1_range (frange &r, tree type,
+ const irange &lhs,
+ const frange &op2,
+ relation_kind rel = VREL_VARYING) const;
+ virtual bool op2_range (frange &r, tree type,
+ const frange &lhs,
+ const frange &op1,
+ relation_kind rel = VREL_VARYING) const;
+ virtual bool op2_range (frange &r, tree type,
+ const irange &lhs,
+ const frange &op1,
+ relation_kind rel = VREL_VARYING) const;
+
+ virtual relation_kind lhs_op1_relation (const frange &lhs,
+ const frange &op1,
+ const frange &op2,
+ relation_kind = VREL_VARYING) const;
+ virtual relation_kind lhs_op1_relation (const irange &lhs,
+ const frange &op1,
+ const frange &op2,
+ relation_kind = VREL_VARYING) const;
+ virtual relation_kind lhs_op2_relation (const frange &lhs,
+ const frange &op1,
+ const frange &op2,
+ relation_kind = VREL_VARYING) const;
+ virtual relation_kind lhs_op2_relation (const irange &lhs,
+ const frange &op1,
+ const frange &op2,
+ relation_kind = VREL_VARYING) const;
+ virtual relation_kind op1_op2_relation (const irange &lhs) const;
+};
+
class range_op_handler
{
public:
range_op_handler (enum tree_code code, tree type);
range_op_handler (const gimple *s);
- operator bool () const { return m_op; }
+ operator bool () const;
bool fold_range (vrange &r, tree type,
const vrange &lh,
@@ -137,7 +186,8 @@ public:
relation_kind = VREL_VARYING) const;
relation_kind op1_op2_relation (const vrange &lhs) const;
private:
- range_operator *m_op;
+ enum tree_code m_code;
+ tree m_type;
};
extern bool range_cast (vrange &, tree type);
@@ -218,4 +268,19 @@ private:
range_operator *m_range_tree[MAX_TREE_CODES];
};
+// Like above, but for floating point operators.
+
+class floating_op_table
+{
+public:
+ floating_op_table ();
+ range_operator_float *operator[] (enum tree_code code);
+private:
+ void set (enum tree_code code, range_operator_float &op);
+ range_operator_float *m_range_tree[MAX_TREE_CODES];
+};
+
+// This holds the range op table for floating point operations.
+extern floating_op_table *floating_tree_table;
+
#endif // GCC_RANGE_OP_H
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 054c92f..18e136c 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,45 @@
+2022-07-26 Jason Merrill <jason@redhat.com>
+
+ PR c++/106230
+ * g++.dg/cpp0x/range-for38.C: New test.
+
+2022-07-25 Martin Liska <mliska@suse.cz>
+
+ * gcc.dg/analyzer/fd-2.c: Convert Windows endlines to Unix
+ style.
+ * gcc.dg/analyzer/fd-3.c: Likewise.
+ * gcc.dg/analyzer/fd-4.c: Likewise.
+ * gcc.dg/analyzer/fd-5.c: Likewise.
+ * c-c++-common/attr-fd.c: Likewise.
+
+2022-07-25 Roger Sayle <roger@nextmovesoftware.com>
+ Uroš Bizjak <ubizjak@gmail.com>
+
+ PR target/91681
+ * g++.target/i386/pr91681.C: New test case (from the PR).
+ * gcc.target/i386/pr91681-1.c: New int128 test case.
+ * gcc.target/i386/pr91681-2.c: Likewise.
+ * gcc.target/i386/pr91681-3.c: Likewise, but for ia32.
+
+2022-07-25 Jason Merrill <jason@redhat.com>
+
+ PR c++/87729
+ * g++.dg/warn/Woverloaded-virt4.C: New test.
+
+2022-07-25 Richard Biener <rguenther@suse.de>
+
+ PR middle-end/106414
+ * gcc.dg/torture/pr106414-1.c: New testcase.
+ * gcc.dg/torture/pr106414-2.c: Likewise.
+
+2022-07-25 Andre Vieira <andre.simoesdiasvieira@arm.com>
+
+ * gcc.target/aarch64/acle/data-intrinsics.c: New test.
+
+2022-07-25 Jason Merrill <jason@redhat.com>
+
+ * g++.dg/cpp0x/nsdmi-union7.C: Fix PR number.
+
2022-07-24 Roger Sayle <roger@nextmovesoftware.com>
H.J. Lu <hjl.tools@gmail.com>
diff --git a/gcc/testsuite/c-c++-common/attr-fd.c b/gcc/testsuite/c-c++-common/attr-fd.c
index e4bb4ed..9f12093 100644
--- a/gcc/testsuite/c-c++-common/attr-fd.c
+++ b/gcc/testsuite/c-c++-common/attr-fd.c
@@ -1,18 +1,18 @@
-
-int not_a_fn __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute only applies to function types" } */
-
-void f (char *p) __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
-
-
-int not_a_fn_b __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute only applies to function types" } */
-
-void g (char *p) __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
-
-
-int not_a_fn_c __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute only applies to function types" } */
-
-void f (char *p) __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
-
-
-void fn_a (int fd) __attribute__ ((fd_arg(0))); /* { dg-warning "'fd_arg' attribute argument value '0' does not refer to a function parameter" } */
-void fd_a_1 (int fd) __attribute__ ((fd_arg("notint"))); /* { dg-warning "'fd_arg' attribute argument has type ('char\\\[7\\\]'|'const char\\\*')" } */
+
+int not_a_fn __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute only applies to function types" } */
+
+void f (char *p) __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
+
+
+int not_a_fn_b __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute only applies to function types" } */
+
+void g (char *p) __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
+
+
+int not_a_fn_c __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute only applies to function types" } */
+
+void f (char *p) __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */
+
+
+void fn_a (int fd) __attribute__ ((fd_arg(0))); /* { dg-warning "'fd_arg' attribute argument value '0' does not refer to a function parameter" } */
+void fd_a_1 (int fd) __attribute__ ((fd_arg("notint"))); /* { dg-warning "'fd_arg' attribute argument has type ('char\\\[7\\\]'|'const char\\\*')" } */
diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for38.C b/gcc/testsuite/g++.dg/cpp0x/range-for38.C
new file mode 100644
index 0000000..39845b9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/range-for38.C
@@ -0,0 +1,16 @@
+// PR c++/106230
+// { dg-do compile { target c++11 } }
+
+struct A {
+ A();
+ operator int();
+};
+template <int N> struct array {
+ A elts[N];
+ A *begin();
+ A *end();
+};
+void fn() {
+ for (int i : array<4>{})
+ ;
+}
diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C
new file mode 100644
index 0000000..b4d8668
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C
@@ -0,0 +1,7 @@
+// PR c++/87729
+// { dg-additional-options -Woverloaded-virtual }
+
+struct S1 { virtual void f(); };
+struct S2: S1 {};
+struct S3: S1 {};
+struct S4: S2, S3 { void f(); };
diff --git a/gcc/testsuite/g++.target/i386/pr91681.C b/gcc/testsuite/g++.target/i386/pr91681.C
new file mode 100644
index 0000000..0271e43
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr91681.C
@@ -0,0 +1,20 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2" } */
+
+void multiply128x64x2_3 (
+ const unsigned long a,
+ const unsigned long b,
+ const unsigned long c,
+ const unsigned long d,
+ __uint128_t o[2])
+{
+ __uint128_t B0 = (__uint128_t) b * c;
+ __uint128_t B2 = (__uint128_t) a * c;
+ __uint128_t B1 = (__uint128_t) b * d;
+ __uint128_t B3 = (__uint128_t) a * d;
+
+ o[0] = B2 + (B0 >> 64);
+ o[1] = B3 + (B1 >> 64);
+}
+
+/* { dg-final { scan-assembler-not "xor" } } */
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-2.c b/gcc/testsuite/gcc.dg/analyzer/fd-2.c
index 96ccf2f..d794b46 100644
--- a/gcc/testsuite/gcc.dg/analyzer/fd-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-2.c
@@ -1,49 +1,49 @@
-int open(const char *, int mode);
-void close(int fd);
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define STDIN 0
-
-void
-test_1 (const char *path)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- close (fd); /* { dg-message "\\(2\\) first 'close' here" "event1" } */
- close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
- /* { dg-message "\\(3\\) second 'close' here; first 'close' was at \\(2\\)" "event2" { target *-*-* } .-1 } */
-}
-
-void
-test_2 (const char *path)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- if (fd < 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
- /* { dg-message "\\(3\\) following 'false' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
- return;
- close (fd); /* { dg-message "\\(4\\) ...to here" "event1" } */
- /* { dg-message "\\(5\\) first 'close' here" "event2" { target *-*-* } .-1 } */
- close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
- /* {dg-message "\\(6\\) second 'close' here; first was at \\(5\\)" "" { target *-*-* } .-1 } */
-}
-
-void
-test_3 ()
-{
- /* FD 0 is stdin at the entry to "main" and thus read-only, but we have no
- guarantees here that it hasn't been closed and then reopened for
- writing, so we can't issue a warning */
-
- int fd = STDIN;
- close(fd); /* { dg-message "\\(1\\) first 'close' here" } */
- close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
- /* { dg-message "\\(2\\) second 'close' here; first 'close' was at \\(1\\)" "event2" { target *-*-* } .-1 } */
-}
-
-void
-test_4 ()
-{
- int fd = -1;
- close(fd);
- close(fd);
+int open(const char *, int mode);
+void close(int fd);
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+#define STDIN 0
+
+void
+test_1 (const char *path)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ close (fd); /* { dg-message "\\(2\\) first 'close' here" "event1" } */
+ close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
+ /* { dg-message "\\(3\\) second 'close' here; first 'close' was at \\(2\\)" "event2" { target *-*-* } .-1 } */
+}
+
+void
+test_2 (const char *path)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ if (fd < 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
+ /* { dg-message "\\(3\\) following 'false' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
+ return;
+ close (fd); /* { dg-message "\\(4\\) ...to here" "event1" } */
+ /* { dg-message "\\(5\\) first 'close' here" "event2" { target *-*-* } .-1 } */
+ close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
+ /* {dg-message "\\(6\\) second 'close' here; first was at \\(5\\)" "" { target *-*-* } .-1 } */
+}
+
+void
+test_3 ()
+{
+ /* FD 0 is stdin at the entry to "main" and thus read-only, but we have no
+ guarantees here that it hasn't been closed and then reopened for
+ writing, so we can't issue a warning */
+
+ int fd = STDIN;
+ close(fd); /* { dg-message "\\(1\\) first 'close' here" } */
+ close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */
+ /* { dg-message "\\(2\\) second 'close' here; first 'close' was at \\(1\\)" "event2" { target *-*-* } .-1 } */
+}
+
+void
+test_4 ()
+{
+ int fd = -1;
+ close(fd);
+ close(fd);
} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-3.c b/gcc/testsuite/gcc.dg/analyzer/fd-3.c
index 40fc8af..55e84e3 100644
--- a/gcc/testsuite/gcc.dg/analyzer/fd-3.c
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-3.c
@@ -1,85 +1,85 @@
-int open(const char *, int mode);
-void close(int fd);
-int write (int fd, void *buf, int nbytes);
-int read (int fd, void *buf, int nbytes);
-int some_condition();
-
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-#define STDIN 0
-#define O_NOATIME 262144
-
-void
-test_1 (const char *path, void *buf)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- write (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */
- /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */
- close(fd);
-}
-
-void
-test_2 (const char *path, void *buf)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- read (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */
- /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */
- close (fd);
-}
-
-void
-test_3 (void *buf)
-{
- int fd = -1;
- read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */
- /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */
-}
-
-void
-test_4 (void *buf)
-{
- int fd = STDIN;
- read (fd, buf, 1);
- close(fd);
-}
-
-void
-test_5 (char *path, void *buf)
-{
- int flags = O_RDONLY;
- if (some_condition())
- flags |= O_NOATIME;
- int fd = open (path, flags);
- read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */
- /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */
- close (fd);
-}
-
-
-void
-test_6 (char *path, void *buf)
-{
- int fd = open (path, O_RDONLY);
- if (fd != -1)
- {
- read (fd, buf, 1);
- }
- close (fd);
-}
-
-
-void
-test_7 (char *path, void *buf)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- if (fd != -1) /* { dg-message "\\(2\\) assuming 'fd' is an invalid file descriptor \\(< 0\\)" } */
- {
- read (fd, buf, 1);
- } else
- {
- write (fd, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" } */
-
- }
- close(fd);
+int open(const char *, int mode);
+void close(int fd);
+int write (int fd, void *buf, int nbytes);
+int read (int fd, void *buf, int nbytes);
+int some_condition();
+
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+#define STDIN 0
+#define O_NOATIME 262144
+
+void
+test_1 (const char *path, void *buf)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ write (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */
+ /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */
+ close(fd);
+}
+
+void
+test_2 (const char *path, void *buf)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ read (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */
+ /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */
+ close (fd);
+}
+
+void
+test_3 (void *buf)
+{
+ int fd = -1;
+ read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */
+ /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */
+}
+
+void
+test_4 (void *buf)
+{
+ int fd = STDIN;
+ read (fd, buf, 1);
+ close(fd);
+}
+
+void
+test_5 (char *path, void *buf)
+{
+ int flags = O_RDONLY;
+ if (some_condition())
+ flags |= O_NOATIME;
+ int fd = open (path, flags);
+ read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */
+ /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */
+ close (fd);
+}
+
+
+void
+test_6 (char *path, void *buf)
+{
+ int fd = open (path, O_RDONLY);
+ if (fd != -1)
+ {
+ read (fd, buf, 1);
+ }
+ close (fd);
+}
+
+
+void
+test_7 (char *path, void *buf)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ if (fd != -1) /* { dg-message "\\(2\\) assuming 'fd' is an invalid file descriptor \\(< 0\\)" } */
+ {
+ read (fd, buf, 1);
+ } else
+ {
+ write (fd, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" } */
+
+ }
+ close(fd);
} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-4.c b/gcc/testsuite/gcc.dg/analyzer/fd-4.c
index 4126346..ecd787c 100644
--- a/gcc/testsuite/gcc.dg/analyzer/fd-4.c
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-4.c
@@ -1,72 +1,72 @@
-#include <stdio.h>
-
-int open(const char *, int mode);
-void close(int fd);
-int write (int fd, void *buf, int nbytes);
-int read (int fd, void *buf, int nbytes);
-
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-
-
-void
-test_1 (const char *path, void *buf)
-{
- int fd = open (path, O_RDONLY); /* { dg-message "opened here as read-only" } */
- if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
- /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
- {
- write (fd, buf, 1); /* { dg-warning "'write' on read-only file descriptor 'fd'" "warning" } */
- /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */
- /* { dg-message "\\(5\\) 'write' on read-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */
- close (fd);
- }
-}
-
-void
-test_2 (const char *path, void *buf)
-{
- int fd = open (path, O_WRONLY); /* { dg-message "opened here as write-only" } */
- if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
- /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
- {
- read (fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd'" "warning" } */
- /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */
- /* { dg-message "\\(5\\) 'read' on write-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */
- close (fd);
- }
-}
-
-
-void
-test_3 (const char *path, void *buf)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- if (fd >= 0)
- {
- close(fd); /* {dg-message "\\(2\\) closed here"} */
- read(fd, buf, 1); /* { dg-warning "'read' on closed file descriptor 'fd'" } */
- /* {dg-message "\\(3\\) 'read' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */
- }
-}
-
-void
-test_4 (const char *path, void *buf)
-{
- int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
- if (fd >= 0)
- {
- close(fd); /* {dg-message "\\(2\\) closed here"} */
- write(fd, buf, 1); /* { dg-warning "'write' on closed file descriptor 'fd'" } */
- /* {dg-message "\\(3\\) 'write' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */
- }
-}
-
-void
-test_5 (const char *path)
-{
- int fd = open (path, O_RDWR);
- close(fd);
- printf("%d", fd); /* { dg-bogus "'printf' on a closed file descriptor 'fd'" } */
+#include <stdio.h>
+
+int open(const char *, int mode);
+void close(int fd);
+int write (int fd, void *buf, int nbytes);
+int read (int fd, void *buf, int nbytes);
+
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+
+
+void
+test_1 (const char *path, void *buf)
+{
+ int fd = open (path, O_RDONLY); /* { dg-message "opened here as read-only" } */
+ if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
+ /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
+ {
+ write (fd, buf, 1); /* { dg-warning "'write' on read-only file descriptor 'fd'" "warning" } */
+ /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */
+ /* { dg-message "\\(5\\) 'write' on read-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */
+ close (fd);
+ }
+}
+
+void
+test_2 (const char *path, void *buf)
+{
+ int fd = open (path, O_WRONLY); /* { dg-message "opened here as write-only" } */
+ if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */
+ /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */
+ {
+ read (fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd'" "warning" } */
+ /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */
+ /* { dg-message "\\(5\\) 'read' on write-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */
+ close (fd);
+ }
+}
+
+
+void
+test_3 (const char *path, void *buf)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ if (fd >= 0)
+ {
+ close(fd); /* {dg-message "\\(2\\) closed here"} */
+ read(fd, buf, 1); /* { dg-warning "'read' on closed file descriptor 'fd'" } */
+ /* {dg-message "\\(3\\) 'read' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */
+ }
+}
+
+void
+test_4 (const char *path, void *buf)
+{
+ int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */
+ if (fd >= 0)
+ {
+ close(fd); /* {dg-message "\\(2\\) closed here"} */
+ write(fd, buf, 1); /* { dg-warning "'write' on closed file descriptor 'fd'" } */
+ /* {dg-message "\\(3\\) 'write' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */
+ }
+}
+
+void
+test_5 (const char *path)
+{
+ int fd = open (path, O_RDWR);
+ close(fd);
+ printf("%d", fd); /* { dg-bogus "'printf' on a closed file descriptor 'fd'" } */
} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-5.c b/gcc/testsuite/gcc.dg/analyzer/fd-5.c
index 8f29c11..c18b2ad 100644
--- a/gcc/testsuite/gcc.dg/analyzer/fd-5.c
+++ b/gcc/testsuite/gcc.dg/analyzer/fd-5.c
@@ -1,53 +1,53 @@
-int open(const char *, int mode);
-void close(int fd);
-int write (int fd, void *buf, int nbytes);
-int read (int fd, void *buf, int nbytes);
-
-#define O_RDONLY 0
-#define O_WRONLY 1
-#define O_RDWR 2
-
-void f (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'f' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */
-
-void
-test_1 (const char *path)
-{
- int fd = open (path, O_RDWR);
- close(fd);
- f(fd); /* { dg-warning "'f' on closed file descriptor 'fd'" } */
- /* { dg-message "\\(3\\) 'f' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" { target *-*-* } .-1 } */
-}
-
-void g (int fd) __attribute__((fd_arg_read(1))); /* { dg-message "argument 1 of 'g' must be a readable file descriptor, due to '__attribute__\\(\\(fd_arg_read\\(1\\)\\)\\)'" } */
-
-void
-test_2 (const char *path)
-{
- int fd = open (path, O_WRONLY);
- if (fd != -1)
- {
- g (fd); /* { dg-warning "'g' on write-only file descriptor 'fd'" } */
- }
- close (fd);
-}
-
-void h (int fd) __attribute__((fd_arg_write(1))); /* { dg-message "argument 1 of 'h' must be a writable file descriptor, due to '__attribute__\\(\\(fd_arg_write\\(1\\)\\)\\)'" } */
-void
-test_3 (const char *path)
-{
- int fd = open (path, O_RDONLY);
- if (fd != -1)
- {
- h (fd); /* { dg-warning "'h' on read-only file descriptor 'fd'" } */
- }
- close(fd);
-}
-
-void ff (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'ff' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */
-
-void test_4 (const char *path)
-{
- int fd = open (path, O_RDWR);
- ff (fd); /* { dg-warning "'ff' on possibly invalid file descriptor 'fd'" } */
- close(fd);
+int open(const char *, int mode);
+void close(int fd);
+int write (int fd, void *buf, int nbytes);
+int read (int fd, void *buf, int nbytes);
+
+#define O_RDONLY 0
+#define O_WRONLY 1
+#define O_RDWR 2
+
+void f (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'f' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */
+
+void
+test_1 (const char *path)
+{
+ int fd = open (path, O_RDWR);
+ close(fd);
+ f(fd); /* { dg-warning "'f' on closed file descriptor 'fd'" } */
+ /* { dg-message "\\(3\\) 'f' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" { target *-*-* } .-1 } */
+}
+
+void g (int fd) __attribute__((fd_arg_read(1))); /* { dg-message "argument 1 of 'g' must be a readable file descriptor, due to '__attribute__\\(\\(fd_arg_read\\(1\\)\\)\\)'" } */
+
+void
+test_2 (const char *path)
+{
+ int fd = open (path, O_WRONLY);
+ if (fd != -1)
+ {
+ g (fd); /* { dg-warning "'g' on write-only file descriptor 'fd'" } */
+ }
+ close (fd);
+}
+
+void h (int fd) __attribute__((fd_arg_write(1))); /* { dg-message "argument 1 of 'h' must be a writable file descriptor, due to '__attribute__\\(\\(fd_arg_write\\(1\\)\\)\\)'" } */
+void
+test_3 (const char *path)
+{
+ int fd = open (path, O_RDONLY);
+ if (fd != -1)
+ {
+ h (fd); /* { dg-warning "'h' on read-only file descriptor 'fd'" } */
+ }
+ close(fd);
+}
+
+void ff (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'ff' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */
+
+void test_4 (const char *path)
+{
+ int fd = open (path, O_RDWR);
+ ff (fd); /* { dg-warning "'ff' on possibly invalid file descriptor 'fd'" } */
+ close(fd);
} \ No newline at end of file
diff --git a/gcc/testsuite/gcc.dg/pr106189.c b/gcc/testsuite/gcc.dg/pr106189.c
new file mode 100644
index 0000000..0eca834
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr106189.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Warray-bounds=2 -w" } */
+
+int a_n_0_0_a[][0];
+void a_n_0_0() { T(((char *)a_n_0_0_a)[1]); }
diff --git a/gcc/testsuite/gcc.dg/torture/pr106414-1.c b/gcc/testsuite/gcc.dg/torture/pr106414-1.c
new file mode 100644
index 0000000..0974716
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr106414-1.c
@@ -0,0 +1,12 @@
+/* { dg-do run } */
+
+int a, c, e;
+const int b = 1;
+char d;
+int main() {
+ a = ~(e || 0) ^ b & ~d;
+ d = ~(a | ~2);
+ if (d)
+ __builtin_abort();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr106414-2.c b/gcc/testsuite/gcc.dg/torture/pr106414-2.c
new file mode 100644
index 0000000..bed6a40
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr106414-2.c
@@ -0,0 +1,12 @@
+/* { dg-do run } */
+
+int a, b, c, d;
+unsigned e;
+int main() {
+ c = e = -((a && 1) ^ ~(b || 0));
+ if (e < -1)
+ d = c;
+ if (!d)
+ __builtin_abort();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c b/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c
new file mode 100644
index 0000000..e067ef2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c
@@ -0,0 +1,468 @@
+/* Test the ACLE data intrinsics. */
+/* { dg-do assemble } */
+/* { dg-additional-options "--save-temps -O1" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include "arm_acle.h"
+
+/*
+** test_clz:
+** clz w0, w0
+** ret
+*/
+
+unsigned int test_clz (uint32_t a)
+{
+ return __clz (a);
+}
+
+/*
+** test_clzl:
+** clz [wx]0, [wx]0
+** ret
+*/
+
+unsigned int test_clzl (unsigned long a)
+{
+ return __clzl (a);
+}
+
+/*
+** test_clzll:
+** clz x0, x0
+** ret
+*/
+
+unsigned int test_clzll (uint64_t a)
+{
+ return __clzll (a);
+}
+
+/*
+** test_cls:
+** cls w0, w0
+** ret
+*/
+
+unsigned int test_cls (uint32_t a)
+{
+ return __cls (a);
+}
+
+/*
+** test_clsl:
+** cls [wx]0, [wx]0
+** ret
+*/
+
+unsigned int test_clsl (unsigned long a)
+{
+ return __clsl (a);
+}
+
+/*
+** test_clsll:
+** cls x0, x0
+** ret
+*/
+
+unsigned int test_clsll (uint64_t a)
+{
+ return __clsll (a);
+}
+
+/*
+** test_rbit:
+** rbit w0, w0
+** ret
+*/
+
+uint32_t test_rbit (uint32_t a)
+{
+ return __rbit (a);
+}
+
+/*
+** test_rbitl:
+** rbit [wx]0, [wx]0
+** ret
+*/
+
+unsigned long test_rbitl (unsigned long a)
+{
+ return __rbitl (a);
+}
+
+/*
+** test_rbitll:
+** rbit x0, x0
+** ret
+*/
+
+uint64_t test_rbitll (uint64_t a)
+{
+ return __rbitll (a);
+}
+
+/*
+** test_rev:
+** rev w0, w0
+** ret
+*/
+
+uint32_t test_rev (uint32_t a)
+{
+ return __rev (a);
+}
+
+/*
+** test_revl:
+** rev [wx]0, [wx]0
+** ret
+*/
+
+unsigned long test_revl (unsigned long a)
+{
+ return __revl (a);
+}
+
+/*
+** test_revll:
+** rev x0, x0
+** ret
+*/
+
+uint64_t test_revll (uint64_t a)
+{
+ return __revll (a);
+}
+
+/*
+** test_rev16:
+** rev16 w0, w0
+** ret
+*/
+
+uint32_t test_rev16 (uint32_t a)
+{
+ return __rev16 (a);
+}
+
+/*
+** test_rev16l:
+** rev16 [wx]0, [wx]0
+** ret
+*/
+
+unsigned long test_rev16l (unsigned long a)
+{
+ return __rev16l (a);
+}
+
+/*
+** test_rev16ll:
+** rev16 x0, x0
+** ret
+*/
+
+uint64_t test_rev16ll (uint64_t a)
+{
+ return __rev16ll (a);
+}
+
+/*
+** test_ror:
+** ror w0, w0, w1
+** ret
+*/
+
+uint32_t test_ror (uint32_t a, uint32_t r)
+{
+ return __ror (a, r);
+}
+
+/*
+** test_rorl:
+** ror [wx]0, [wx]0, [wx]1
+** ret
+*/
+
+unsigned long test_rorl (unsigned long a, uint32_t r)
+{
+ return __rorl (a, r);
+}
+
+/*
+** test_rorll:
+** ror x0, x0, x1
+** ret
+*/
+
+uint64_t test_rorll (uint64_t a, uint32_t r)
+{
+ return __rorll (a, r);
+}
+
+/*
+** test_revsh:
+** rev16 w0, w0
+** ret
+*/
+
+int16_t test_revsh (int16_t a)
+{
+ return __revsh (a);
+}
+
+uint32_t *g32;
+unsigned long *gul;
+uint64_t *g64;
+unsigned int *gui;
+int16_t *g16;
+
+/*
+** test_clz_mem:
+** ...
+** clz w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_clz_mem (uint32_t *a)
+{
+ *gui = __clz (*a);
+}
+
+/*
+** test_clzl_mem:
+** ...
+** clz [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_clzl_mem (unsigned long *a)
+{
+ *gui = __clzl (*a);
+}
+
+/*
+** test_clzll_mem:
+** ...
+** clz x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_clzll_mem (uint64_t *a)
+{
+ *gui = __clzll (*a);
+}
+
+/*
+** test_cls_mem:
+** ...
+** cls w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_cls_mem (uint32_t *a)
+{
+ *gui = __cls (*a);
+}
+
+/*
+** test_clsl_mem:
+** ...
+** cls [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_clsl_mem (unsigned long *a)
+{
+ *gui = __clsl (*a);
+}
+
+/*
+** test_clsll_mem:
+** ...
+** cls x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_clsll_mem (uint64_t *a)
+{
+ *gui = __clsll (*a);
+}
+
+/*
+** test_rbit_mem:
+** ...
+** rbit w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_rbit_mem (uint32_t *a)
+{
+ *g32 = __rbit (*a);
+}
+
+/*
+** test_rbitl_mem:
+** ...
+** rbit [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_rbitl_mem (unsigned long *a)
+{
+ *gul = __rbitl (*a);
+}
+
+/*
+** test_rbitll_mem:
+** ...
+** rbit x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_rbitll_mem (uint64_t *a)
+{
+ *g64 = __rbitll (*a);
+}
+
+/*
+** test_rev_mem:
+** ...
+** rev w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_rev_mem (uint32_t *a)
+{
+ *g32 = __rev (*a);
+}
+
+/*
+** test_revl_mem:
+** ...
+** rev [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_revl_mem (unsigned long *a)
+{
+ *gul = __revl (*a);
+}
+
+/*
+** test_revll_mem:
+** ...
+** rev x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_revll_mem (uint64_t *a)
+{
+ *g64 = __revll (*a);
+}
+
+/*
+** test_rev16_mem:
+** ...
+** rev16 w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_rev16_mem (uint32_t *a)
+{
+ *g32 = __rev16 (*a);
+}
+
+/*
+** test_rev16l_mem:
+** ...
+** rev16 [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_rev16l_mem (unsigned long *a)
+{
+ *gul = __rev16l (*a);
+}
+
+/*
+** test_rev16ll_mem:
+** ...
+** rev16 x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_rev16ll_mem (uint64_t *a)
+{
+ *g64 = __rev16ll (*a);
+}
+
+/*
+** test_ror_mem:
+** ...
+** ror w[0-9]+, w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_ror_mem (uint32_t *a, uint32_t *r)
+{
+ *g32 = __ror (*a, *r);
+}
+
+/*
+** test_rorl_mem:
+** ...
+** ror [wx][0-9]+, [wx][0-9]+, [wx][0-9]+
+** ...
+** ret
+*/
+
+void test_rorl_mem (unsigned long *a, uint32_t *r)
+{
+ *gul = __rorl (*a, *r);
+}
+
+/*
+** test_rorll_mem:
+** ...
+** ror x[0-9]+, x[0-9]+, x[0-9]+
+** ...
+** ret
+*/
+
+void test_rorll_mem (uint64_t *a, uint32_t *r)
+{
+ *g64 = __rorll (*a, *r);
+}
+
+/*
+** test_revsh_mem:
+** ...
+** rev16 w[0-9]+, w[0-9]+
+** ...
+** ret
+*/
+
+void test_revsh_mem (int16_t *a)
+{
+ *g16 = __revsh (*a);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr91681-1.c b/gcc/testsuite/gcc.target/i386/pr91681-1.c
new file mode 100644
index 0000000..ab83cc4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr91681-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2" } */
+unsigned __int128 m;
+
+unsigned __int128 foo(unsigned __int128 x, unsigned long long y)
+{
+ return x + y;
+}
+
+void bar(unsigned __int128 x, unsigned long long y)
+{
+ m = x + y;
+}
+
+void baz(unsigned long long y)
+{
+ m += y;
+}
+
+/* { dg-final { scan-assembler-not "xor" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr91681-2.c b/gcc/testsuite/gcc.target/i386/pr91681-2.c
new file mode 100644
index 0000000..ea52c72
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr91681-2.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options "-O2" } */
+unsigned __int128 m;
+
+unsigned __int128 foo(unsigned __int128 x, unsigned long long y)
+{
+ return x - y;
+}
+
+void bar(unsigned __int128 x, unsigned long long y)
+{
+ m = x - y;
+}
+
+void baz(unsigned long long y)
+{
+ m -= y;
+}
+
+/* { dg-final { scan-assembler-not "xor" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr91681-3.c b/gcc/testsuite/gcc.target/i386/pr91681-3.c
new file mode 100644
index 0000000..22a03c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr91681-3.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target ia32 } } */
+/* { dg-options "-O2" } */
+
+unsigned long long m;
+
+unsigned long long foo(unsigned long long x, unsigned int y)
+{
+ return x - y;
+}
+
+void bar(unsigned long long x, unsigned int y)
+{
+ m = x - y;
+}
+
+/* { dg-final { scan-assembler-not "xor" } } */
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-1.c
new file mode 100644
index 0000000..01b8ea2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-1.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */
+/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-2.c
new file mode 100644
index 0000000..4565baa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-2.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */
+/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-3.c
new file mode 100644
index 0000000..4f669a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-3.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-4.c
new file mode 100644
index 0000000..943adb6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-4.c
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-5.c
new file mode 100644
index 0000000..2c2a1c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-5.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */
+/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-6.c
new file mode 100644
index 0000000..4b0e426
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-6.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */
+/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-7.c
new file mode 100644
index 0000000..5179271
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-7.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-8.c
new file mode 100644
index 0000000..330140d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-8.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */
+/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+ g ();
+}
+
+void
+test1 (void)
+{
+ f ();
+}
+
+void
+test2 (void)
+{
+ l ();
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c
new file mode 100644
index 0000000..bfcc9bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -mexplicit-relocs -fno-pic -O2" } */
+/* { dg-final { scan-assembler "pcalau12i.*%pc_hi20\\(\.LANCHOR0\\)\n" } } */
+/* { dg-final { scan-assembler "addi\.d.*%pc_lo12\\(\.LANCHOR0\\)\n" } } */
+/* { dg-final { scan-assembler "ldptr.d\t\\\$r4,.*,0\n" } } */
+/* { dg-final { scan-assembler "ld.d\t\\\$r5,.*,8\n" } } */
+/* { dg-final { scan-assembler-not "\.LANCHOR0+8" } } */
+
+
+struct S
+{
+ char *a;
+ unsigned short int b;
+};
+
+struct S s1;
+
+void test(struct S);
+void test1(void)
+{
+ test(s1);
+}
+
diff --git a/gcc/testsuite/gcc.target/powerpc/pr106091.c b/gcc/testsuite/gcc.target/powerpc/pr106091.c
new file mode 100644
index 0000000..61ce8cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr106091.c
@@ -0,0 +1,15 @@
+/* { dg-options "-O -fnon-call-exceptions -fno-tree-dce -fno-tree-forwprop -w" } */
+
+/* Verify there is no ICE. */
+
+typedef short __attribute__ ((__vector_size__ (64))) V;
+V v, w;
+
+inline V foo (V a, V b);
+
+V
+foo (V a, V b)
+{
+ b &= v < b;
+ return (V){foo (b, w)[3], (V){}[3]};
+}
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index decf5aa..4af8eca 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -211,6 +211,7 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt)
switch (TREE_CODE (expr))
{
case INTEGER_CST:
+ case REAL_CST:
if (TREE_OVERFLOW_P (expr))
expr = drop_tree_overflow (expr);
r.set (expr, expr);