diff options
author | Martin Liska <mliska@suse.cz> | 2022-07-26 10:04:01 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-07-26 10:04:01 +0200 |
commit | 8f694e3f186d734a20ab870cd72e98646142810a (patch) | |
tree | 082a4a95e89bfb0dd888876209099060c715709d | |
parent | 9630093982a263fd86dc7c87afbb0afed89c9d47 (diff) | |
parent | bb04f9f23ac0dee2c003118c85372ece50a52220 (diff) | |
download | gcc-8f694e3f186d734a20ab870cd72e98646142810a.zip gcc-8f694e3f186d734a20ab870cd72e98646142810a.tar.gz gcc-8f694e3f186d734a20ab870cd72e98646142810a.tar.bz2 |
Merge branch 'master' into devel/sphinx
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); |