aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/Makefile.in3
-rw-r--r--gcc/aclocal.m41
-rw-r--r--gcc/analyzer/ana-state-to-diagnostic-state.cc163
-rw-r--r--gcc/analyzer/ana-state-to-diagnostic-state.h23
-rw-r--r--gcc/analyzer/checker-event.cc9
-rw-r--r--gcc/analyzer/sm-malloc.cc29
-rw-r--r--gcc/c-family/c-attribs.cc30
-rw-r--r--gcc/c/c-decl.cc118
-rw-r--r--gcc/c/c-parser.cc7
-rw-r--r--gcc/cfghooks.cc2
-rw-r--r--gcc/config/riscv/predicates.md11
-rw-r--r--gcc/config/riscv/riscv.md39
-rwxr-xr-xgcc/configure407
-rw-r--r--gcc/configure.ac2
-rw-r--r--gcc/custom-sarif-properties/digraphs.cc28
-rw-r--r--gcc/custom-sarif-properties/digraphs.h37
-rw-r--r--gcc/custom-sarif-properties/state-graphs.cc157
-rw-r--r--gcc/custom-sarif-properties/state-graphs.h97
-rw-r--r--gcc/diagnostics/diagnostics-selftests.cc1
-rw-r--r--gcc/diagnostics/diagnostics-selftests.h1
-rw-r--r--gcc/diagnostics/digraphs.cc199
-rw-r--r--gcc/diagnostics/digraphs.h62
-rw-r--r--gcc/diagnostics/html-sink.cc62
-rw-r--r--gcc/diagnostics/html-sink.h9
-rw-r--r--gcc/diagnostics/output-spec.cc589
-rw-r--r--gcc/diagnostics/output-spec.h88
-rw-r--r--gcc/diagnostics/state-graphs-to-dot.cc139
-rw-r--r--gcc/diagnostics/state-graphs.cc156
-rw-r--r--gcc/diagnostics/state-graphs.h108
-rw-r--r--gcc/doc/invoke.texi8
-rw-r--r--gcc/doc/passes.texi6
-rw-r--r--gcc/fortran/decl.cc10
-rw-r--r--gcc/gimple-fold.cc131
-rw-r--r--gcc/gimple-isel.cc956
-rw-r--r--gcc/json.cc25
-rw-r--r--gcc/json.h82
-rw-r--r--gcc/libgdiagnostics.cc3
-rw-r--r--gcc/m2/gm2-compiler/M2GCCDeclare.mod13
-rw-r--r--gcc/m2/gm2-gcc/m2type.cc2
-rw-r--r--gcc/m2/gm2-gcc/m2type.def2
-rw-r--r--gcc/m2/gm2-gcc/m2type.h2
-rw-r--r--gcc/m2/gm2-libs/M2WIDESET.mod116
-rw-r--r--gcc/opts-diagnostic.cc9
-rw-r--r--gcc/passes.def8
-rw-r--r--gcc/simplify-rtx.cc4
-rw-r--r--gcc/testsuite/gcc.dg/builtin-unreachable-5.c10
-rw-r--r--gcc/testsuite/gcc.dg/builtin-unreachable-6.c6
-rw-r--r--gcc/testsuite/gcc.dg/builtin-unreachable-6a.c6
-rw-r--r--gcc/testsuite/gcc.dg/builtin-unreachable-7.c8
-rw-r--r--gcc/testsuite/gcc.dg/c2y-init-2.c33
-rw-r--r--gcc/testsuite/gcc.dg/c2y-init-3.c106
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc5
-rw-r--r--gcc/testsuite/gcc.dg/plugin/start_unit_plugin.cc2
-rw-r--r--gcc/testsuite/gcc.dg/pr78408-2.c4
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr122079-1.c27
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr122079-2.c27
-rw-r--r--gcc/testsuite/gcc.dg/torture/pr122079-3.c27
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c20
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c20
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c22
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c22
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c18
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c23
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/pr79691.c2
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-10.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/asm-flag-1.c2
-rw-r--r--gcc/testsuite/gcc.target/aarch64/builtin_pld_pli.c72
-rw-r--r--gcc/testsuite/gcc.target/aarch64/csinc-1.c43
-rw-r--r--gcc/testsuite/gcc.target/aarch64/csneg-1.c44
-rw-r--r--gcc/testsuite/gcc.target/aarch64/declare-simd-2.c7
-rw-r--r--gcc/testsuite/gcc.target/aarch64/flt_mov_immediate_1.c50
-rw-r--r--gcc/testsuite/gcc.target/aarch64/ldp_stp_18.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-1.c43
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c8
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c37
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c28
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c40
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c37
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error1.c18
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error10.c13
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error11.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error12.c13
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error2.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error3.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error4.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error5.c8
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error6.c20
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error7.c11
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error8.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-error9.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols1.c38
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols10.c42
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols11.c16
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols12.c27
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols13.c28
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols14.c34
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols2.c28
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols3.c27
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols4.c31
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols5.c36
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols6.c20
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols7.c47
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols8.c47
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mv-symbols9.c44
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-error1.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-error2.c9
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c25
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c15
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c19
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/mvc-warning1.c13
-rw-r--r--gcc/testsuite/gcc.target/aarch64/ror_2.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/scalar_intrinsics.c2
-rw-r--r--gcc/testsuite/gcc.target/aarch64/scalar_shift_1.c114
-rw-r--r--gcc/testsuite/gcc.target/aarch64/simd/fold_to_highpart_5.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/singleton_intrinsics_1.c2
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/arith_1.c2
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_1.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_3.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_5.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_7.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_1.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_3.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_5.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_7.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fmul_3.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/cond_fsubr_3.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/mixed_size_6.c4
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/pcs/annotate_1.c1
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/pcs/return_6.c2
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/struct_move_3.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/struct_move_6.c12
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/uzp1_1.c3
-rw-r--r--gcc/testsuite/gcc.target/aarch64/sve/uzp2_1.c3
-rw-r--r--gcc/testsuite/gcc.target/aarch64/vector-compare-5.c10
-rw-r--r--gcc/testsuite/gcc.target/riscv/pr121937.c66
-rw-r--r--gcc/testsuite/gcc.target/riscv/pr122051.c24
-rw-r--r--gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c4
-rw-r--r--gcc/testsuite/gfortran.dg/pdt_52.f0336
-rw-r--r--gcc/testsuite/gfortran.dg/pdt_53.f0328
-rw-r--r--gcc/testsuite/gfortran.dg/pdt_54.f0328
-rw-r--r--gcc/tree-pass.h1
-rw-r--r--gcc/tree-ssa-ccp.cc1442
-rw-r--r--gcc/tree-ssa-forwprop.cc185
-rw-r--r--gcc/tree-ssa-pre.cc35
-rw-r--r--gcc/tree-vect-loop.cc12
-rw-r--r--gcc/tree.h9
153 files changed, 5033 insertions, 2616 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6a9d620..098bafb 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1852,6 +1852,8 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies.
OBJS-libcommon = \
+ custom-sarif-properties/digraphs.o \
+ custom-sarif-properties/state-graphs.o \
diagnostic-global-context.o \
diagnostics/buffering.o \
diagnostics/changes.o \
@@ -1871,7 +1873,6 @@ OBJS-libcommon = \
diagnostics/paths.o \
diagnostics/paths-output.o \
diagnostics/source-printing.o \
- diagnostics/state-graphs.o \
diagnostics/state-graphs-to-dot.o \
diagnostics/selftest-context.o \
diagnostics/selftest-logical-locations.o \
diff --git a/gcc/aclocal.m4 b/gcc/aclocal.m4
index 762e949..e44fc5f 100644
--- a/gcc/aclocal.m4
+++ b/gcc/aclocal.m4
@@ -69,6 +69,7 @@ m4_include([../ltversion.m4])
m4_include([../lt~obsolete.m4])
m4_include([../config/acx.m4])
m4_include([../config/cet.m4])
+m4_include([../config/clang-plugin.m4])
m4_include([../config/codeset.m4])
m4_include([../config/depstand.m4])
m4_include([../config/dfp.m4])
diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc
index 996538c..39acf26 100644
--- a/gcc/analyzer/ana-state-to-diagnostic-state.cc
+++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc
@@ -39,38 +39,55 @@ along with GCC; see the file COPYING3. If not see
namespace ana {
-using namespace ::diagnostics::state_graphs;
+namespace node_properties = custom_sarif_properties::state_graphs::node;
static void
-set_wi_attr (state_node_ref state_node,
- const char *attr_name,
+set_wi_attr (diagnostics::digraphs::node &state_node,
+ const json::string_property &property,
const wide_int_ref &w,
signop sgn)
{
pretty_printer pp;
pp_wide_int (&pp, w, sgn);
- state_node.set_attr (attr_name, pp_formatted_text (&pp));
+ state_node.set_property (property, pp_formatted_text (&pp));
}
static void
-set_type_attr (state_node_ref state_node, const_tree type)
+set_type_attr (diagnostics::digraphs::node &state_node,
+ const_tree type)
{
gcc_assert (type);
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%T", type);
- state_node.set_type (pp_formatted_text (&pp));
+ state_node.set_property (node_properties::type,
+ pp_formatted_text (&pp));
}
static void
-set_bits_attr (state_node_ref state_node,
+set_bits_attr (diagnostics::digraphs::node & state_node,
bit_range bits)
{
pretty_printer pp;
bits.dump_to_pp (&pp);
- state_node.set_attr ("bits", pp_formatted_text (&pp));
+ state_node.set_property (node_properties::bits,
+ pp_formatted_text (&pp));
}
+static void
+set_value_attrs (diagnostics::digraphs::node &state_node,
+ const svalue &sval)
+{
+ state_node.set_property (node_properties::value,
+ sval.to_json ());
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ sval.dump_to_pp (&pp, true);
+ state_node.set_property (node_properties::value_str,
+ pp_formatted_text (&pp));
+}
+
+
// class analyzer_state_graph : public diagnostics::digraphs::digraph
analyzer_state_graph::analyzer_state_graph (const program_state &state,
@@ -141,34 +158,34 @@ analyzer_state_graph::analyzer_state_graph (const program_state &state,
/* Ensure we have a node for the dst region. This
could lead to additional pending edges. */
- auto dst_node = get_or_create_state_node (item.m_dst_reg);
- add_edge (nullptr, item.m_src_node.m_node, dst_node.m_node);
+ auto &dst_node = get_or_create_state_node (item.m_dst_reg);
+ add_edge (nullptr, item.m_src_node, dst_node);
}
}
-state_node_ref
+diagnostics::digraphs::node &
analyzer_state_graph::get_or_create_state_node (const region &reg)
{
auto existing = m_region_to_state_node_map.find (&reg);
if (existing != m_region_to_state_node_map.end ())
return *existing->second;
- auto ref = create_and_add_state_node (reg);
- m_region_to_state_node_map[&reg] = &ref.m_node;
- return ref;
+ auto &state_node = create_and_add_state_node (reg);
+ m_region_to_state_node_map[&reg] = &state_node;
+ return state_node;
}
-state_node_ref
+diagnostics::digraphs::node &
analyzer_state_graph::create_and_add_state_node (const region &reg)
{
auto node = create_state_node (reg);
- state_node_ref result = *node;
+ diagnostics::digraphs::node &result = *node;
if (auto parent_reg = reg.get_parent_region ())
if (parent_reg->get_kind () != RK_ROOT)
{
- auto parent_state_node = get_or_create_state_node (*parent_reg);
- parent_state_node.m_node.add_child (std::move (node));
+ auto &parent_state_node = get_or_create_state_node (*parent_reg);
+ parent_state_node.add_child (std::move (node));
return result;
}
add_node (std::move (node));
@@ -264,19 +281,18 @@ analyzer_state_graph::make_node_id (const region &reg)
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::
-make_state_node (diagnostics::state_graphs::node_kind kind,
+make_state_node (enum node_properties::kind kind,
std::string id)
{
auto node = std::make_unique<diagnostics::digraphs::node> (*this, std::move (id));
- state_node_ref node_ref (*node);
- node_ref.set_node_kind (kind);
+ node->set_property (node_properties::kind, kind);
return node;
}
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::
make_memspace_state_node (const region &reg,
- diagnostics::state_graphs::node_kind kind)
+ enum node_properties::kind kind)
{
return make_state_node (kind, make_node_id (reg));
}
@@ -296,7 +312,7 @@ analyzer_state_graph::create_state_node (const region &reg)
const frame_region &frame_reg
= static_cast<const frame_region &> (reg);
- node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame,
+ node = make_state_node (node_properties::kind::stack_frame,
make_node_id (reg));
node->set_logical_loc
(m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ()));
@@ -304,58 +320,59 @@ analyzer_state_graph::create_state_node (const region &reg)
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%E", frame_reg.get_fndecl ());
- node->set_attr (STATE_NODE_PREFIX, "function",
- pp_formatted_text (&pp));
+ node->set_property (node_properties::function,
+ pp_formatted_text (&pp));
}
}
break;
case RK_GLOBALS:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::globals);
+ node_properties::kind::globals);
break;
case RK_CODE:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::code);
+ node_properties::kind::code);
break;
case RK_FUNCTION:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::function);
+ node_properties::kind::function);
// TODO
break;
case RK_STACK:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::stack);
+ node_properties::kind::stack);
break;
case RK_HEAP:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::heap_);
+ node_properties::kind::heap_);
break;
case RK_THREAD_LOCAL:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::thread_local_);
+ node_properties::kind::thread_local_);
break;
case RK_ROOT:
gcc_unreachable ();
break;
case RK_SYMBOLIC:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::other);
+ node_properties::kind::other);
break;
case RK_DECL:
{
- node = make_state_node (diagnostics::state_graphs::node_kind::variable,
+ node = make_state_node (node_properties::kind::variable,
make_node_id (reg));
const decl_region &decl_reg
= static_cast<const decl_region &> (reg);
- state_node_ref node_ref (*node);
+
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%E", decl_reg.get_decl ());
- node_ref.set_name (pp_formatted_text (&pp));
+ node->set_property (node_properties::name,
+ pp_formatted_text (&pp));
}
set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ()));
}
@@ -377,14 +394,14 @@ analyzer_state_graph::create_state_node (const region &reg)
case RK_ERRNO:
case RK_PRIVATE:
case RK_UNKNOWN:
- node = make_state_node (diagnostics::state_graphs::node_kind::other,
+ node = make_state_node (node_properties::kind::other,
make_node_id (reg));
break;
case RK_HEAP_ALLOCATED:
case RK_ALLOCA:
node = make_memspace_state_node (reg,
- diagnostics::state_graphs::node_kind::dynalloc_buffer);
+ node_properties::kind::dynalloc_buffer);
set_attr_for_dynamic_extents (reg, *node);
break;
}
@@ -425,9 +442,9 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster,
get_or_create_state_node (*reg);
}
- auto ref = get_or_create_state_node (*cluster.get_base_region ());
+ auto &ref = get_or_create_state_node (*cluster.get_base_region ());
- ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings));
+ ref.add_child (create_state_node_for_conc_bindings (conc_bindings));
const region *typed_reg = cluster.get_base_region ();
if (!typed_reg->get_type ())
@@ -455,23 +472,18 @@ create_state_nodes_for_binding_cluster (const binding_cluster &cluster,
std::unique_ptr<diagnostics::digraphs::node>
analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings)
{
- auto node = make_state_node (diagnostics::state_graphs::node_kind::other,
+ auto node = make_state_node (node_properties::kind::other,
make_node_id ("concrete-bindings"));
for (auto iter : conc_bindings)
{
const bit_range bits = iter.first;
const svalue *sval = iter.second;
auto binding_state_node
- = make_state_node (diagnostics::state_graphs::node_kind::other,
+ = make_state_node (node_properties::kind::other,
make_node_id ("binding"));
set_bits_attr (*binding_state_node, bits);
- {
- pretty_printer pp;
- pp_format_decoder (&pp) = default_tree_printer;
- sval->dump_to_pp (&pp, true);
- binding_state_node->set_attr (STATE_NODE_PREFIX, "value",
- pp_formatted_text (&pp));
- }
+ gcc_assert (sval);
+ set_value_attrs (*binding_state_node, *sval);
node->add_child (std::move (binding_state_node));
}
return node;
@@ -496,27 +508,28 @@ analyzer_state_graph::get_bit_range_within_base_region (const region &reg,
void
analyzer_state_graph::
-populate_state_node_for_typed_region (state_node_ref node,
+populate_state_node_for_typed_region (diagnostics::digraphs::node &state_node,
const region &reg,
const concrete_bindings_t &conc_bindings,
bool create_all)
{
const_tree reg_type = reg.get_type ();
gcc_assert (reg_type);
- set_type_attr (node, reg_type);
+ set_type_attr (state_node, reg_type);
bit_range bits (0, 0);
if (get_bit_range_within_base_region (reg, bits))
{
- set_bits_attr (node, bits);
+ set_bits_attr (state_node, bits);
auto search = conc_bindings.find (bits);
if (search != conc_bindings.end ())
{
const svalue *bound_sval = search->second;
- node.set_json_attr ("value", bound_sval->to_json ());
+ gcc_assert (bound_sval);
+ set_value_attrs (state_node, *bound_sval);
if (const region *dst_reg = bound_sval->maybe_get_region ())
- m_pending_edges.push_back ({node, *dst_reg});
+ m_pending_edges.push_back ({state_node, *dst_reg});
}
}
@@ -555,9 +568,10 @@ populate_state_node_for_typed_region (state_node_ref node,
{
auto child_state_node
= make_state_node
- (diagnostics::state_graphs::node_kind::element,
+ (node_properties::kind::element,
make_node_id (*child_reg));
- set_wi_attr (*child_state_node, "index", idx, UNSIGNED);
+ set_wi_attr (*child_state_node,
+ node_properties::index, idx, UNSIGNED);
// Recurse:
gcc_assert (element_type);
@@ -565,7 +579,7 @@ populate_state_node_for_typed_region (state_node_ref node,
*child_reg,
conc_bindings,
create_all);
- node.m_node.add_child (std::move (child_state_node));
+ state_node.add_child (std::move (child_state_node));
}
}
}
@@ -587,11 +601,12 @@ populate_state_node_for_typed_region (state_node_ref node,
{
auto child_state_node
= make_state_node
- (diagnostics::state_graphs::node_kind::padding,
+ (node_properties::kind::padding,
make_node_id (*child_reg));
- set_wi_attr (*child_state_node, "num_bits",
+ set_wi_attr (*child_state_node,
+ node_properties::num_bits,
item.m_bit_range.m_size_in_bits, SIGNED);
- node.m_node.add_child (std::move (child_state_node));
+ state_node.add_child (std::move (child_state_node));
}
}
else
@@ -600,27 +615,27 @@ populate_state_node_for_typed_region (state_node_ref node,
= m_mgr.get_field_region (&reg,
const_cast<tree> (item.m_field));
if (show_child_state_node_for_child_region_p (*child_reg,
- conc_bindings,
- create_all))
+ conc_bindings,
+ create_all))
{
auto child_state_node
= make_state_node
- (diagnostics::state_graphs::node_kind::field,
+ (node_properties::kind::field,
make_node_id (*child_reg));
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_printf (&pp, "%D", item.m_field);
- child_state_node->set_attr (STATE_NODE_PREFIX, "name",
- pp_formatted_text (&pp));
+ child_state_node->set_property (node_properties::name,
+ pp_formatted_text (&pp));
}
// Recurse:
populate_state_node_for_typed_region (*child_state_node,
- *child_reg,
- conc_bindings,
- create_all);
- node.m_node.add_child (std::move (child_state_node));
+ *child_reg,
+ conc_bindings,
+ create_all);
+ state_node.add_child (std::move (child_state_node));
}
}
}
@@ -630,8 +645,9 @@ populate_state_node_for_typed_region (state_node_ref node,
}
void
-analyzer_state_graph::set_attr_for_dynamic_extents (const region &reg,
- state_node_ref node_ref)
+analyzer_state_graph::
+set_attr_for_dynamic_extents (const region &reg,
+ diagnostics::digraphs::node &state_node)
{
const svalue *sval = m_state.m_region_model->get_dynamic_extents (&reg);
if (sval)
@@ -642,15 +658,16 @@ analyzer_state_graph::set_attr_for_dynamic_extents (const region &reg,
pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED);
else
sval->dump_to_pp (&pp, true);
- node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp));
+ state_node.set_property (state_node_properties::dynamic_extents,
+ pp_formatted_text (&pp));
}
}
bool
analyzer_state_graph::
show_child_state_node_for_child_region_p (const region &reg,
- const concrete_bindings_t &conc_bindings,
- bool create_all)
+ const concrete_bindings_t &conc_bindings,
+ bool create_all)
{
if (create_all)
return true;
diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h
index 3a5ccc1..eec3d56 100644
--- a/gcc/analyzer/ana-state-to-diagnostic-state.h
+++ b/gcc/analyzer/ana-state-to-diagnostic-state.h
@@ -23,34 +23,38 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostics/state-graphs.h"
#include "tree-logical-location.h"
+#include "custom-sarif-properties/state-graphs.h"
namespace ana {
+namespace state_node_properties = custom_sarif_properties::state_graphs::node;
+
class analyzer_state_graph : public diagnostics::digraphs::digraph
{
public:
analyzer_state_graph (const program_state &state,
const extrinsic_state &ext_state);
- diagnostics::state_graphs::state_node_ref
+ diagnostics::digraphs::node &
get_or_create_state_node (const region &reg);
private:
+
struct pending_edge
{
- diagnostics::state_graphs::state_node_ref m_src_node;
+ diagnostics::digraphs::node & m_src_node;
const region &m_dst_reg;
};
-
- diagnostics::state_graphs::state_node_ref
+
+ diagnostics::digraphs::node &
create_and_add_state_node (const region &reg);
std::unique_ptr<diagnostics::digraphs::node>
- make_state_node (diagnostics::state_graphs::node_kind kind,
+ make_state_node (enum state_node_properties::kind kind,
std::string id);
std::unique_ptr<diagnostics::digraphs::node>
make_memspace_state_node (const region &reg,
- enum diagnostics::state_graphs::node_kind kind);
+ enum state_node_properties::kind kind);
std::unique_ptr<diagnostics::digraphs::node>
create_state_node (const region &reg);
@@ -71,14 +75,14 @@ private:
bit_range &out);
void
- populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref,
+ populate_state_node_for_typed_region (diagnostics::digraphs::node &,
const region &reg,
const concrete_bindings_t &conc_bindings,
bool create_all);
void
set_attr_for_dynamic_extents (const region &reg,
- diagnostics::state_graphs::state_node_ref);
+ diagnostics::digraphs::node &);
bool
show_child_state_node_for_child_region_p (const region &reg,
@@ -95,7 +99,8 @@ private:
const program_state &m_state;
const extrinsic_state &m_ext_state;
region_model_manager &m_mgr;
- std::map<const region *, diagnostics::digraphs::node *> m_region_to_state_node_map;
+ std::map<const region *,
+ diagnostics::digraphs::node *> m_region_to_state_node_map;
std::map<const region *, tree> m_types_for_untyped_regions;
unsigned m_next_id;
std::vector<pending_edge> m_pending_edges;
diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc
index 4eac945..790ebc7 100644
--- a/gcc/analyzer/checker-event.cc
+++ b/gcc/analyzer/checker-event.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-logical-location.h"
#include "diagnostics/sarif-sink.h"
#include "diagnostics/state-graphs.h"
+#include "custom-sarif-properties/state-graphs.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
@@ -242,9 +243,11 @@ checker_event::maybe_make_diagnostic_state_graph (bool debug) const
pretty_printer pp;
text_art::theme *theme = global_dc->get_diagram_theme ();
text_art::dump_to_pp (*state, theme, &pp);
- result->set_attr (STATE_GRAPH_PREFIX,
- "analyzer/program_state/",
- pp_formatted_text (&pp));
+ const json::string_property program_state_property
+ (custom_sarif_properties::state_graphs::graph::prefix,
+ "analyzer/program_state/");
+ result->set_property (program_state_property,
+ pp_formatted_text (&pp));
}
return result;
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index a6b1421..b25e2ad 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -2735,7 +2735,7 @@ malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
smap->set_state (model, new_ptr_sval, m_free.m_nonnull, nullptr, ext_state);
}
-static enum diagnostics::state_graphs::node_dynalloc_state
+static enum custom_sarif_properties::state_graphs::node::dynalloc_state
get_dynalloc_state_for_state (enum resource_state rs)
{
switch (rs)
@@ -2746,17 +2746,17 @@ get_dynalloc_state_for_state (enum resource_state rs)
case RS_NULL:
case RS_NON_HEAP:
case RS_STOP:
- return diagnostics::state_graphs::node_dynalloc_state::unknown;
+ return state_node_properties::dynalloc_state::unknown;
case RS_ASSUMED_NON_NULL:
- return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+ return state_node_properties::dynalloc_state::nonnull;
case RS_UNCHECKED:
- return diagnostics::state_graphs::node_dynalloc_state::unchecked;
+ return state_node_properties::dynalloc_state::unchecked;
case RS_NONNULL:
- return diagnostics::state_graphs::node_dynalloc_state::nonnull;
+ return state_node_properties::dynalloc_state::nonnull;
case RS_FREED:
- return diagnostics::state_graphs::node_dynalloc_state::freed;
+ return state_node_properties::dynalloc_state::freed;
}
}
@@ -2768,24 +2768,23 @@ add_state_to_state_graph (analyzer_state_graph &out_state_graph,
{
if (const region *reg = sval.maybe_get_region ())
{
- auto reg_node = out_state_graph.get_or_create_state_node (*reg);
+ auto &reg_node = out_state_graph.get_or_create_state_node (*reg);
auto alloc_state = as_a_allocation_state (state);
gcc_assert (alloc_state);
- reg_node.set_dynalloc_state
- (get_dynalloc_state_for_state (alloc_state->m_rs));
+ reg_node.set_property (state_node_properties::dynalloc_state,
+ get_dynalloc_state_for_state (alloc_state->m_rs));
+
if (alloc_state->m_deallocators)
{
pretty_printer pp;
alloc_state->m_deallocators->dump_to_pp (&pp);
- reg_node.m_node.set_attr (STATE_NODE_PREFIX,
- "expected-deallocators",
- pp_formatted_text (&pp));
+ reg_node.set_property (state_node_properties::expected_deallocators,
+ pp_formatted_text (&pp));
}
if (alloc_state->m_deallocator)
- reg_node.m_node.set_attr (STATE_NODE_PREFIX,
- "deallocator",
- alloc_state->m_deallocator->m_name);
+ reg_node.set_property (state_node_properties::deallocator,
+ alloc_state->m_deallocator->m_name);
}
}
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index df9ff99..5bc5183 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -249,6 +249,29 @@ static const struct attribute_spec::exclusions attr_target_clones_exclusions[] =
ATTR_EXCL ("always_inline", true, true, true),
ATTR_EXCL ("target", TARGET_HAS_FMV_TARGET_ATTRIBUTE,
TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE),
+ ATTR_EXCL ("omp declare simd", true, true, true),
+ ATTR_EXCL ("simd", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_target_version_exclusions[] =
+{
+ ATTR_EXCL ("omp declare simd", true, true, true),
+ ATTR_EXCL ("simd", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_omp_declare_simd_exclusions[] =
+{
+ ATTR_EXCL ("target_version", true, true, true),
+ ATTR_EXCL ("target_clones", true, true, true),
+ ATTR_EXCL (NULL, false, false, false),
+};
+
+static const struct attribute_spec::exclusions attr_simd_exclusions[] =
+{
+ ATTR_EXCL ("target_version", true, true, true),
+ ATTR_EXCL ("target_clones", true, true, true),
ATTR_EXCL (NULL, false, false, false),
};
@@ -536,7 +559,7 @@ const struct attribute_spec c_common_gnu_attributes[] =
attr_target_exclusions },
{ "target_version", 1, 1, true, false, false, false,
handle_target_version_attribute,
- NULL },
+ attr_target_version_exclusions},
{ "target_clones", 1, -1, true, false, false, false,
handle_target_clones_attribute,
attr_target_clones_exclusions },
@@ -563,7 +586,8 @@ const struct attribute_spec c_common_gnu_attributes[] =
{ "returns_nonnull", 0, 0, false, true, true, false,
handle_returns_nonnull_attribute, NULL },
{ "omp declare simd", 0, -1, true, false, false, false,
- handle_omp_declare_simd_attribute, NULL },
+ handle_omp_declare_simd_attribute,
+ attr_omp_declare_simd_exclusions },
{ "omp declare variant base", 0, -1, true, false, false, false,
handle_omp_declare_variant_attribute, NULL },
{ "omp declare variant variant", 0, -1, true, false, false, false,
@@ -572,7 +596,7 @@ const struct attribute_spec c_common_gnu_attributes[] =
false, false,
handle_omp_declare_variant_attribute, NULL },
{ "simd", 0, 1, true, false, false, false,
- handle_simd_attribute, NULL },
+ handle_simd_attribute, attr_simd_exclusions },
{ "omp declare target", 0, -1, true, false, false, false,
handle_omp_declare_target_attribute, NULL },
{ "omp declare target link", 0, 0, true, false, false, false,
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 7e4c7c2..632bbf0 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2086,6 +2086,35 @@ previous_tag (tree type)
return NULL_TREE;
}
+/* Subroutine to mark functions as versioned when using the attribute
+ 'target_version'. */
+
+static void
+maybe_mark_function_versioned (tree decl)
+{
+ if (!DECL_FUNCTION_VERSIONED (decl))
+ {
+ /* Check if the name of the function has been overridden. */
+ if (DECL_ASSEMBLER_NAME_SET_P (decl)
+ && IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl))[0] == '*')
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "cannot use function multiversioning on a renamed function");
+
+ /* We need to insert function version now to make sure the correct
+ pre-mangled assembler name is recorded. */
+ cgraph_node *node = cgraph_node::get_create (decl);
+
+ if (!node->function_version ())
+ node->insert_new_function_version ();
+
+ DECL_FUNCTION_VERSIONED (decl) = 1;
+
+ tree mangled_name
+ = targetm.mangle_decl_assembler_name (decl, DECL_NAME (decl));
+ SET_DECL_ASSEMBLER_NAME (decl, mangled_name);
+ }
+}
+
/* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL.
Returns true if the caller should proceed to merge the two, false
if OLDDECL should simply be discarded. As a side effect, issues
@@ -2505,6 +2534,10 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
"but not here");
}
}
+ /* Check if these are unmergable overlapping FMV declarations. */
+ if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && diagnose_versioned_decls (olddecl, newdecl))
+ return false;
}
else if (VAR_P (newdecl))
{
@@ -2971,6 +3004,12 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
if (TREE_CODE (newdecl) == FUNCTION_DECL)
{
+ if (DECL_FUNCTION_VERSIONED (olddecl)
+ || DECL_FUNCTION_VERSIONED (newdecl))
+ {
+ maybe_mark_function_versioned (olddecl);
+ maybe_mark_function_versioned (newdecl);
+ }
/* If we're redefining a function previously defined as extern
inline, make sure we emit debug info for the inline before we
throw it away, in case it was inlined into a function that
@@ -3370,6 +3409,53 @@ pushdecl (tree x)
TREE_TYPE (b_use->decl) = b_use->u.type;
}
}
+
+ /* Check if x is part of a FMV set with b_use. */
+ if (b_use && TREE_CODE (b_use->decl) == FUNCTION_DECL
+ && TREE_CODE (x) == FUNCTION_DECL && DECL_FILE_SCOPE_P (b_use->decl)
+ && DECL_FILE_SCOPE_P (x)
+ && disjoint_version_decls (x, b_use->decl)
+ && comptypes (vistype, type) != 0)
+ {
+ maybe_mark_function_versioned (b_use->decl);
+ maybe_mark_function_versioned (b->decl);
+ maybe_mark_function_versioned (x);
+
+ cgraph_node *b_node = cgraph_node::get_create (b_use->decl);
+ cgraph_function_version_info *b_v = b_node->function_version ();
+ if (!b_v)
+ b_v = b_node->insert_new_function_version ();
+
+ /* Check if this new node conflicts with any previous functions
+ in the set. */
+ cgraph_function_version_info *version = b_v;
+ for (; version; version = version->next)
+ if (!disjoint_version_decls (version->this_node->decl, x))
+ {
+ /* The decls define overlapping version, so attempt to merge
+ or diagnose the conflict. */
+ if (duplicate_decls (x, version->this_node->decl))
+ return version->this_node->decl;
+ else
+ return error_mark_node;
+ }
+
+ /* This is a new version to be added to FMV structure. */
+ cgraph_node::add_function_version (b_v, x);
+
+ /* Get the first node from the structure. */
+ cgraph_function_version_info *default_v = b_v;
+ while (default_v->prev)
+ default_v = default_v->prev;
+ /* Always use the default node for the bindings. */
+ b_use->decl = default_v->this_node->decl;
+ b->decl = default_v->this_node->decl;
+
+ /* Node is not a duplicate, so no need to do the rest of the
+ checks. */
+ return x;
+ }
+
if (duplicate_decls (x, b_use->decl))
{
if (b_use != b)
@@ -4494,6 +4580,12 @@ tree
lookup_name (tree name)
{
struct c_binding *b = I_SYMBOL_BINDING (name);
+ /* Do not resolve non-default function versions. */
+ if (b
+ && TREE_CODE (b->decl) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (b->decl)
+ && !is_function_default_version (b->decl))
+ return NULL_TREE;
if (b && !b->invisible)
{
maybe_record_typedef_use (b->decl);
@@ -5776,6 +5868,17 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
&& VAR_OR_FUNCTION_DECL_P (decl))
objc_check_global_decl (decl);
+ /* To enable versions to be created across TU's we mark and mangle all
+ non-default versioned functions. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && !TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && get_target_version (decl).is_valid ())
+ {
+ maybe_mark_function_versioned (decl);
+ if (current_scope != file_scope)
+ error ("versioned declarations are only allowed at file scope");
+ }
+
/* Add this decl to the current scope.
TEM may equal DECL or it may be a previous decl of the same name. */
if (do_push)
@@ -10754,6 +10857,17 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
warn_parm_array_mismatch (origloc, old_decl, parms);
}
+ /* To enable versions to be created across TU's we mark and mangle all
+ non-default versioned functions. */
+ if (TREE_CODE (decl1) == FUNCTION_DECL
+ && !TARGET_HAS_FMV_TARGET_ATTRIBUTE
+ && get_target_version (decl1).is_valid ())
+ {
+ maybe_mark_function_versioned (decl1);
+ if (current_scope != file_scope)
+ error ("versioned definitions are only allowed at file scope");
+ }
+
/* Record the decl so that the function name is defined.
If we already have a decl for this name, and it is a FUNCTION_DECL,
use the old decl. */
@@ -13585,6 +13699,10 @@ c_parse_final_cleanups (void)
c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
c_write_global_declarations_1 (BLOCK_VARS (ext_block));
+ /* Call this to set cpp_implicit_aliases_done on all nodes. This is
+ important for function multiversioning aliases to get resolved. */
+ symtab->process_same_body_aliases ();
+
if (!in_lto_p)
free_attr_access_data ();
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index df44a91..7c24526 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -27776,6 +27776,13 @@ c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms,
clauses[0].type = CPP_EOF;
return;
}
+ if (DECL_FUNCTION_VERSIONED (fndecl))
+ {
+ error_at (DECL_SOURCE_LOCATION (fndecl),
+ "%<#pragma omp declare %s%> cannot be used with function "
+ "multi-versioning", kind);
+ return;
+ }
if (parms == NULL_TREE)
parms = DECL_ARGUMENTS (fndecl);
diff --git a/gcc/cfghooks.cc b/gcc/cfghooks.cc
index 8b33468..25bc5d4 100644
--- a/gcc/cfghooks.cc
+++ b/gcc/cfghooks.cc
@@ -819,7 +819,7 @@ merge_blocks (basic_block a, basic_block b)
/* Pick the more reliable count. If both qualities agrees, pick the larger
one since turning mistakely hot code to cold is more harmful. */
- if (a->count.initialized_p ())
+ if (!a->count.initialized_p ())
a->count = b->count;
else if (a->count.quality () < b->count.quality ())
a->count = b->count;
diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md
index 777e71b..056f9e2 100644
--- a/gcc/config/riscv/predicates.md
+++ b/gcc/config/riscv/predicates.md
@@ -607,13 +607,12 @@
(define_predicate "ge_operator"
(match_code "ge,geu"))
-;; pmode_reg_or_uimm5_operand can be used by vsll.vx/vsrl.vx/vsra.vx instructions.
-;; Since it has the same predicate with vector_length_operand which allows register
-;; or immediate (0 ~ 31), we define this predicate same as vector_length_operand here.
-;; We don't use vector_length_operand directly to predicate vsll.vx/vsrl.vx/vsra.vx
-;; since it may be confusing.
+;; pmode_reg_or_uimm5_operand can be used by vsll.vx/vsrl.vx/vsra.vx instructions
+;; It is *not* equivalent to vector_length_operand due to the vector_length_operand
+;; needing to conditionalize some behavior on XTHEADVECTOR.
(define_special_predicate "pmode_reg_or_uimm5_operand"
- (match_operand 0 "vector_length_operand"))
+ (ior (match_operand 0 "pmode_register_operand")
+ (match_operand 0 "const_csr_operand")))
(define_special_predicate "pmode_reg_or_0_operand"
(ior (match_operand 0 "const_0_operand")
diff --git a/gcc/config/riscv/riscv.md b/gcc/config/riscv/riscv.md
index 843a048..78a01ef 100644
--- a/gcc/config/riscv/riscv.md
+++ b/gcc/config/riscv/riscv.md
@@ -2322,27 +2322,38 @@
else
{
rtx reg;
- rtx label = gen_label_rtx ();
+ rtx label1 = gen_label_rtx ();
+ rtx label2 = gen_label_rtx ();
+ rtx label3 = gen_label_rtx ();
rtx end_label = gen_label_rtx ();
rtx abs_reg = gen_reg_rtx (<ANYF:MODE>mode);
rtx coeff_reg = gen_reg_rtx (<ANYF:MODE>mode);
rtx tmp_reg = gen_reg_rtx (<ANYF:MODE>mode);
- rtx fflags = gen_reg_rtx (SImode);
riscv_emit_move (tmp_reg, operands[1]);
+
+ if (flag_trapping_math)
+ {
+ /* Check if the input is a NaN. */
+ riscv_expand_conditional_branch (label1, EQ,
+ operands[1], operands[1]);
+
+ emit_jump_insn (gen_jump (label3));
+ emit_barrier ();
+
+ emit_label (label1);
+ }
+
riscv_emit_move (coeff_reg,
riscv_vector::get_fp_rounding_coefficient (<ANYF:MODE>mode));
emit_insn (gen_abs<ANYF:mode>2 (abs_reg, operands[1]));
- /* fp compare can set invalid flag for NaN, so backup fflags. */
- if (flag_trapping_math)
- emit_insn (gen_riscv_frflags (fflags));
- riscv_expand_conditional_branch (label, LT, abs_reg, coeff_reg);
+ riscv_expand_conditional_branch (label2, LT, abs_reg, coeff_reg);
emit_jump_insn (gen_jump (end_label));
emit_barrier ();
- emit_label (label);
+ emit_label (label2);
switch (<ANYF:MODE>mode)
{
case SFmode:
@@ -2361,15 +2372,17 @@
emit_insn (gen_copysign<ANYF:mode>3 (tmp_reg, abs_reg, operands[1]));
- emit_label (end_label);
+ emit_jump_insn (gen_jump (end_label));
+ emit_barrier ();
- /* Restore fflags, but after label. This is slightly different
- than glibc implementation which only needs to restore under
- the label, since it checks for NaN first, meaning following fp
- compare can't raise fp exceptons and thus not clobber fflags. */
if (flag_trapping_math)
- emit_insn (gen_riscv_fsflags (fflags));
+ {
+ emit_label (label3);
+ /* Generate a qNaN from an sNaN if needed. */
+ emit_insn (gen_add<ANYF:mode>3 (tmp_reg, operands[1], operands[1]));
+ }
+ emit_label (end_label);
riscv_emit_move (operands[0], tmp_reg);
}
diff --git a/gcc/configure b/gcc/configure
index d6cc7fc..a742ad7 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -758,6 +758,7 @@ LIPO
NMEDIT
DSYMUTIL
STRIP
+LLVM_CONFIG
OBJDUMP
ac_ct_DUMPBIN
DUMPBIN
@@ -16455,8 +16456,266 @@ test -z "$deplibs_check_method" && deplibs_check_method=unknown
-plugin_option=
+
+# Try CLANG_PLUGIN_FILE first since GCC_PLUGIN_OPTION may return the
+# wrong plugin_option with clang.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clang" >&5
+$as_echo_n "checking for clang... " >&6; }
+if ${clang_cv_is_clang+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef __clang__
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ clang_cv_is_clang=yes
+else
+ clang_cv_is_clang=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $clang_cv_is_clang" >&5
+$as_echo "$clang_cv_is_clang" >&6; }
+ plugin_file=
+ if test $clang_cv_is_clang = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clang plugin file" >&5
+$as_echo_n "checking for clang plugin file... " >&6; }
+ plugin_names="LLVMgold.so"
+ for plugin in $plugin_names; do
+ plugin_file=`${CC} ${CFLAGS} --print-file-name $plugin`
+ if test x$plugin_file = x$plugin; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}llvm-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}llvm-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LLVM_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LLVM_CONFIG"; then
+ ac_cv_prog_LLVM_CONFIG="$LLVM_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LLVM_CONFIG="${ac_tool_prefix}llvm-config"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LLVM_CONFIG=$ac_cv_prog_LLVM_CONFIG
+if test -n "$LLVM_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LLVM_CONFIG" >&5
+$as_echo "$LLVM_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LLVM_CONFIG"; then
+ ac_ct_LLVM_CONFIG=$LLVM_CONFIG
+ # Extract the first word of "llvm-config", so it can be a program name with args.
+set dummy llvm-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LLVM_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LLVM_CONFIG"; then
+ ac_cv_prog_ac_ct_LLVM_CONFIG="$ac_ct_LLVM_CONFIG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_LLVM_CONFIG="llvm-config"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LLVM_CONFIG=$ac_cv_prog_ac_ct_LLVM_CONFIG
+if test -n "$ac_ct_LLVM_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LLVM_CONFIG" >&5
+$as_echo "$ac_ct_LLVM_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LLVM_CONFIG" = x; then
+ LLVM_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LLVM_CONFIG=$ac_ct_LLVM_CONFIG
+ fi
+else
+ LLVM_CONFIG="$ac_cv_prog_LLVM_CONFIG"
+fi
+
+ if test "$?" != 0; then
+ as_fn_error $? "Required tool 'llvm-config' not found on PATH." "$LINENO" 5
+ fi
+ clang_lib_dir=`$LLVM_CONFIG --libdir`
+ if test -f $clang_lib_dir/$plugin; then
+ plugin_file=$clang_lib_dir/$plugin
+ fi
+ if test x$plugin_file != x$plugin; then
+ break;
+ fi
+ fi
+ done
+ if test -z $plugin_file; then
+ as_fn_error $? "Couldn't find clang plugin file for $CC." "$LINENO" 5
+ fi
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+ if test "${AR}" = "" ; then
+ as_fn_error $? "Required archive tool 'ar' not found on PATH." "$LINENO" 5
+ fi
+ plugin_option="--plugin $plugin_file"
+ touch conftest.c
+ ${AR} $plugin_option rc conftest.a conftest.c
+ if test "$?" != 0; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed: $AR $plugin_option rc" >&5
+$as_echo "$as_me: WARNING: Failed: $AR $plugin_option rc" >&2;}
+ plugin_file=
+ fi
+ rm -f conftest.*
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $plugin_file" >&5
+$as_echo "$plugin_file" >&6; }
+ fi
+ plugin_file="$plugin_file"
+
+if test -n "$plugin_file"; then
+ plugin_option="--plugin $plugin_file"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -plugin option" >&5
+$as_echo_n "checking for -plugin option... " >&6; }
+
plugin_names="liblto_plugin.so liblto_plugin-0.dll cyglto_plugin-0.dll"
+plugin_option=
for plugin in $plugin_names; do
plugin_so=`${CC} ${CFLAGS} --print-prog-name $plugin`
if test x$plugin_so = x$plugin; then
@@ -16467,7 +16726,119 @@ for plugin in $plugin_names; do
break
fi
done
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+if test "${AR}" = "" ; then
+ as_fn_error $? "Required archive tool 'ar' not found on PATH." "$LINENO" 5
+fi
+touch conftest.c
+${AR} $plugin_option rc conftest.a conftest.c
+if test "$?" != 0; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed: $AR $plugin_option rc" >&5
+$as_echo "$as_me: WARNING: Failed: $AR $plugin_option rc" >&2;}
+ plugin_option=
+fi
+rm -f conftest.*
+if test -n "$plugin_option"; then
+ plugin_option="$plugin_option"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $plugin_option" >&5
+$as_echo "$plugin_option" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
set dummy ${ac_tool_prefix}ar; ac_word=$2
@@ -16562,17 +16933,15 @@ fi
test -z "$AR" && AR=ar
if test -n "$plugin_option"; then
- if $AR --help 2>&1 | grep -q "\--plugin"; then
- touch conftest.c
- $AR $plugin_option rc conftest.a conftest.c
- if test "$?" != 0; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed: $AR $plugin_option rc" >&5
-$as_echo "$as_me: WARNING: Failed: $AR $plugin_option rc" >&2;}
- else
+ case "$AR" in
+ *"$plugin_option"*)
+ ;;
+ *)
+ if $AR --help 2>&1 | grep -q "\--plugin"; then
AR="$AR $plugin_option"
fi
- rm -f conftest.*
- fi
+ ;;
+ esac
fi
test -z "$AR_FLAGS" && AR_FLAGS=cru
@@ -16779,9 +17148,15 @@ fi
test -z "$RANLIB" && RANLIB=:
if test -n "$plugin_option" && test "$RANLIB" != ":"; then
- if $RANLIB --help 2>&1 | grep -q "\--plugin"; then
- RANLIB="$RANLIB $plugin_option"
- fi
+ case "$RANLIB" in
+ *"$plugin_option"*)
+ ;;
+ *)
+ if $RANLIB --help 2>&1 | grep -q "\--plugin"; then
+ RANLIB="$RANLIB $plugin_option"
+ fi
+ ;;
+ esac
fi
@@ -21484,7 +21859,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 21487 "configure"
+#line 21862 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -21590,7 +21965,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 21593 "configure"
+#line 21968 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -36493,7 +36868,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
"depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;;
"gccdepdir":C)
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
- for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec
+ for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec
do
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
done ;;
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 19975fa..253d3ff 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -1368,7 +1368,7 @@ AC_CHECK_HEADERS(ext/hash_map)
ZW_CREATE_DEPDIR
AC_CONFIG_COMMANDS([gccdepdir],[
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR
- for lang in $subdirs c-family common analyzer diagnostics text-art rtl-ssa sym-exec
+ for lang in $subdirs c-family common analyzer custom-sarif-properties diagnostics text-art rtl-ssa sym-exec
do
${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR
done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR])
diff --git a/gcc/custom-sarif-properties/digraphs.cc b/gcc/custom-sarif-properties/digraphs.cc
new file mode 100644
index 0000000..30ca2b6
--- /dev/null
+++ b/gcc/custom-sarif-properties/digraphs.cc
@@ -0,0 +1,28 @@
+/* Extra properties for digraphs in SARIF property bags.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@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 "json.h"
+#include "custom-sarif-properties/digraphs.h"
+
+const json::string_property custom_sarif_properties::digraphs::digraph::kind
+ ("gcc/digraphs/graph/kind");
diff --git a/gcc/custom-sarif-properties/digraphs.h b/gcc/custom-sarif-properties/digraphs.h
new file mode 100644
index 0000000..93817ed
--- /dev/null
+++ b/gcc/custom-sarif-properties/digraphs.h
@@ -0,0 +1,37 @@
+/* Extra properties for digraphs in SARIF property bags.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H
+#define GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H
+
+/* SARIF property names relating to digraphs. */
+
+namespace custom_sarif_properties {
+ namespace digraphs {
+ namespace digraph {
+ /* A hint about the kind of graph we have,
+ and thus what kinds of nodes and edges to expect. */
+ extern const json::string_property kind;
+ // string; values: "cfg"
+ }
+ }
+}
+
+#endif /* ! GCC_CUSTOM_SARIF_PROPERTIES_DIGRAPHS_H */
diff --git a/gcc/custom-sarif-properties/state-graphs.cc b/gcc/custom-sarif-properties/state-graphs.cc
new file mode 100644
index 0000000..3e0e58a
--- /dev/null
+++ b/gcc/custom-sarif-properties/state-graphs.cc
@@ -0,0 +1,157 @@
+/* Properties for capturing state graphs in SARIF property bags.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@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 "json.h"
+#include "custom-sarif-properties/state-graphs.h"
+
+/* graph. */
+namespace graph = custom_sarif_properties::state_graphs::graph;
+#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/"
+const char *const graph::prefix = STATE_GRAPH_PREFIX;
+#undef STATE_GRAPH_PREFIX
+
+/* node. */
+namespace node = custom_sarif_properties::state_graphs::node;
+#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/"
+
+const json::enum_property<enum node::kind> node::kind
+ (STATE_NODE_PREFIX "kind");
+
+const json::string_property node::function (STATE_NODE_PREFIX "function");
+
+const json::string_property node::dynamic_extents
+ (STATE_NODE_PREFIX "dynamic-extents");
+
+const json::string_property node::name (STATE_NODE_PREFIX "name");
+const json::string_property node::type (STATE_NODE_PREFIX "type");
+const json::json_property node::value (STATE_NODE_PREFIX "value");
+const json::string_property node::value_str (STATE_NODE_PREFIX "value_str");
+
+const json::string_property node::index (STATE_NODE_PREFIX "index");
+
+const json::string_property node::bits (STATE_NODE_PREFIX "bits");
+
+const json::string_property node::num_bits (STATE_NODE_PREFIX "num_bits");
+
+const json::string_property node::deallocator (STATE_NODE_PREFIX "deallocator");
+
+const json::string_property node::expected_deallocators
+ (STATE_NODE_PREFIX "expected-deallocators");
+
+const json::enum_property<enum node::dynalloc_state> node::dynalloc_state
+ (STATE_NODE_PREFIX "dynalloc-state");
+
+#undef STATE_NODE_PREFIX
+
+
+/* edge. */
+namespace edge_props = custom_sarif_properties::state_graphs::edge;
+#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/"
+extern const char *const edge_props::prefix = STATE_EDGE_PREFIX;
+#undef STATE_EDGE_PREFIX
+
+// Traits for enum node:kind
+
+template<>
+enum node::kind
+json::enum_traits<enum node::kind>::get_unknown_value ()
+{
+ return node::kind::other;
+}
+
+static const char * const node_kind_strs[] = {
+ "globals",
+ "code",
+ "function",
+ "stack",
+ "stack-frame",
+ "heap",
+ "thread-local",
+ "dynalloc-buffer",
+ "variable",
+ "field",
+ "padding",
+ "element",
+ "other",
+};
+
+template<>
+bool
+json::enum_traits<enum node::kind>::
+maybe_get_value_from_string (const char *str,
+ enum_t &out)
+{
+ for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i)
+ if (!strcmp (node_kind_strs[i], str))
+ {
+ out = static_cast<enum_t> (i);
+ return true;
+ }
+ return false;
+}
+
+template<>
+const char *
+json::enum_traits<enum node::kind>::get_string_for_value (enum_t value)
+{
+ return node_kind_strs[static_cast<int> (value)];
+}
+
+// Traits for enum node:dynalloc_state
+
+template<>
+enum node::dynalloc_state
+json::enum_traits<enum node::dynalloc_state>::get_unknown_value ()
+{
+ return node::dynalloc_state::unknown;
+}
+
+static const char * const dynalloc_state_strs[] = {
+ "unknown",
+ "nonnull",
+ "unchecked",
+ "freed"
+};
+
+template<>
+bool
+json::enum_traits<enum node::dynalloc_state>::
+maybe_get_value_from_string (const char *str,
+ enum_t &out)
+{
+ for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i)
+ if (!strcmp (dynalloc_state_strs[i], str))
+ {
+ out = static_cast<enum_t> (i);
+ return true;
+ }
+ return false;
+}
+
+template<>
+const char *
+json::enum_traits<enum node::dynalloc_state>::
+get_string_for_value (enum_t value)
+{
+ return dynalloc_state_strs[static_cast <size_t> (value)];
+}
diff --git a/gcc/custom-sarif-properties/state-graphs.h b/gcc/custom-sarif-properties/state-graphs.h
new file mode 100644
index 0000000..6ae9ad8
--- /dev/null
+++ b/gcc/custom-sarif-properties/state-graphs.h
@@ -0,0 +1,97 @@
+/* Properties for capturing state graphs in SARIF property bags.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@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 "json.h"
+
+#ifndef GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
+#define GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H
+
+/* SARIF property names relating to GCC's CFGs. */
+
+namespace custom_sarif_properties {
+ namespace state_graphs {
+ namespace graph {
+ extern const char *const prefix;
+ }
+ namespace node {
+
+ enum class kind
+ {
+ // Memory regions
+ globals,
+ code,
+ function, // code within a particular function
+ stack,
+ stack_frame,
+ heap_,
+ thread_local_,
+
+ /* Dynamically-allocated buffer,
+ on heap or stack (depending on parent). */
+ dynalloc_buffer,
+
+ variable,
+
+ field, // field within a struct or union
+ padding, // padding bits in a struct or union
+ element, // element within an array
+
+ other // anything else
+ };
+
+ enum class dynalloc_state
+ {
+ unknown,
+ nonnull,
+ unchecked,
+ freed
+ };
+
+ extern const json::enum_property<enum kind> kind;
+
+ extern const json::string_property function;
+ extern const json::string_property dynamic_extents;
+ extern const json::string_property name;
+ extern const json::string_property type;
+ /* The value of a memory region, expressed as a json::value. */
+ extern const json::json_property value;
+ /* The value of a memory region, expressed as a string. */
+ extern const json::string_property value_str;
+
+ /* For element nodes, the index within the array. */
+ extern const json::string_property index;
+
+ /* The range of bits or bytes within the base region. */
+ extern const json::string_property bits;
+
+ /* The size of a padding region. */
+ extern const json::string_property num_bits;
+
+ extern const json::string_property deallocator;
+ extern const json::string_property expected_deallocators;
+ extern const json::enum_property<enum dynalloc_state> dynalloc_state;
+ }
+ namespace edge {
+ extern const char *const prefix;
+ }
+ }
+}
+
+#endif /* ! GCC_DIAGNOSTICS_SARIF_PROPERTIES_STATE_GRAPHS_H */
diff --git a/gcc/diagnostics/diagnostics-selftests.cc b/gcc/diagnostics/diagnostics-selftests.cc
index 94a212a..757655b 100644
--- a/gcc/diagnostics/diagnostics-selftests.cc
+++ b/gcc/diagnostics/diagnostics-selftests.cc
@@ -46,7 +46,6 @@ run_diagnostics_selftests ()
sarif_sink_cc_tests ();
digraphs_cc_tests ();
output_spec_cc_tests ();
- state_graphs_cc_tests ();
lazy_paths_cc_tests ();
paths_output_cc_tests ();
changes_cc_tests ();
diff --git a/gcc/diagnostics/diagnostics-selftests.h b/gcc/diagnostics/diagnostics-selftests.h
index 994ebad..5a68a04 100644
--- a/gcc/diagnostics/diagnostics-selftests.h
+++ b/gcc/diagnostics/diagnostics-selftests.h
@@ -44,7 +44,6 @@ extern void paths_output_cc_tests ();
extern void sarif_sink_cc_tests ();
extern void selftest_logical_locations_cc_tests ();
extern void source_printing_cc_tests ();
-extern void state_graphs_cc_tests ();
} /* end of namespace diagnostics::selftest. */
diff --git a/gcc/diagnostics/digraphs.cc b/gcc/diagnostics/digraphs.cc
index 4a2ea4f..60d3e8c 100644
--- a/gcc/diagnostics/digraphs.cc
+++ b/gcc/diagnostics/digraphs.cc
@@ -30,13 +30,15 @@ along with GCC; see the file COPYING3. If not see
#include "graphviz.h"
#include "diagnostics/digraphs.h"
#include "diagnostics/sarif-sink.h"
+#include "custom-sarif-properties/digraphs.h"
-#include "selftest.h"
-
+using digraph_object = diagnostics::digraphs::object;
using digraph = diagnostics::digraphs::digraph;
using digraph_node = diagnostics::digraphs::node;
using digraph_edge = diagnostics::digraphs::edge;
+namespace properties = custom_sarif_properties::digraphs;
+
namespace {
class conversion_to_dot
@@ -171,66 +173,145 @@ conversion_to_dot::has_edges_p (const digraph_node &input_node)
// class object
+/* String properties. */
+
const char *
-diagnostics::digraphs::object::
-get_attr (const char *key_prefix, const char *key) const
+digraph_object::get_property (const json::string_property &property) const
{
if (!m_property_bag)
return nullptr;
- std::string prefixed_key = std::string (key_prefix) + key;
- if (json::value *jv = m_property_bag->get (prefixed_key.c_str ()))
+ if (json::value *jv = m_property_bag->get (property.m_key.get ()))
if (json::string *jstr = jv->dyn_cast_string ())
return jstr->get_string ();
return nullptr;
}
void
-diagnostics::digraphs::object::
-set_attr (const char *key_prefix, const char *key, const char *value)
+digraph_object::set_property (const json::string_property &property,
+ const char *utf8_value)
+{
+ auto &bag = ensure_property_bag ();
+ bag.set_string (property.m_key.get (), utf8_value);
+}
+
+/* Integer properties. */
+
+bool
+digraph_object::maybe_get_property (const json::integer_property &property,
+ long &out_value) const
+{
+ if (!m_property_bag)
+ return false;
+ if (json::value *jv = m_property_bag->get (property.m_key.get ()))
+ if (json::integer_number *jnum = jv->dyn_cast_integer_number ())
+ {
+ out_value = jnum->get ();
+ return true;
+ }
+ return false;
+}
+
+void
+digraph_object::set_property (const json::integer_property &property, long value)
+{
+ auto &bag = ensure_property_bag ();
+ bag.set_integer (property.m_key.get (), value);
+}
+
+/* Bool properties. */
+void
+digraph_object::set_property (const json::bool_property &property, bool value)
+{
+ auto &bag = ensure_property_bag ();
+ bag.set_bool (property.m_key.get (), value);
+}
+
+tristate
+digraph_object::
+get_property_as_tristate (const json::bool_property &property) const
+{
+ if (m_property_bag)
+ {
+ if (json::value *jv = m_property_bag->get (property.m_key.get ()))
+ switch (jv->get_kind ())
+ {
+ default:
+ break;
+ case json::JSON_TRUE:
+ return tristate (true);
+ case json::JSON_FALSE:
+ return tristate (false);
+ }
+ }
+ return tristate::unknown ();
+}
+
+/* Array-of-string properties. */
+json::array *
+digraph_object::get_property (const json::array_of_string_property &property) const
{
- set_json_attr (key_prefix, key, std::make_unique<json::string> (value));
+ if (m_property_bag)
+ if (json::value *jv = m_property_bag->get (property.m_key.get ()))
+ if (json::array *arr = jv->dyn_cast_array ())
+ return arr;
+ return nullptr;
+}
+
+/* json::value properties. */
+const json::value *
+digraph_object::get_property (const json::json_property &property) const
+{
+ if (m_property_bag)
+ return m_property_bag->get (property.m_key.get ());
+ return nullptr;
}
void
-diagnostics::digraphs::object::
-set_json_attr (const char *key_prefix, const char *key, std::unique_ptr<json::value> value)
+digraph_object::set_property (const json::json_property &property,
+ std::unique_ptr<json::value> value)
+{
+ auto &bag = ensure_property_bag ();
+ bag.set (property.m_key.get (), std::move (value));
+}
+
+json::object &
+digraph_object::ensure_property_bag ()
{
- std::string prefixed_key = std::string (key_prefix) + key;
if (!m_property_bag)
- m_property_bag = std::make_unique<json::object> ();
- m_property_bag->set (prefixed_key.c_str (), std::move (value));
+ m_property_bag = std::make_unique<sarif_property_bag> ( );
+ return *m_property_bag;
}
// class digraph
DEBUG_FUNCTION void
-diagnostics::digraphs::digraph::dump () const
+digraph::dump () const
{
make_json_sarif_graph ()->dump ();
}
std::unique_ptr<json::object>
-diagnostics::digraphs::digraph::make_json_sarif_graph () const
+digraph::make_json_sarif_graph () const
{
return make_sarif_graph (*this, nullptr, nullptr);
}
std::unique_ptr<dot::graph>
-diagnostics::digraphs::digraph::make_dot_graph () const
+digraph::make_dot_graph () const
{
- conversion_to_dot to_dot;
- return to_dot.make_dot_graph_from_diagnostic_graph (*this);
+ conversion_to_dot converter;
+ return converter.make_dot_graph_from_diagnostic_graph (*this);
}
-std::unique_ptr<diagnostics::digraphs::digraph>
-diagnostics::digraphs::digraph::clone () const
+std::unique_ptr<digraph>
+digraph::clone () const
{
auto result = std::make_unique<diagnostics::digraphs::digraph> ();
if (get_property_bag ())
result->set_property_bag (get_property_bag ()->clone_as_object ());
- std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> node_mapping;
+ std::map<digraph_node *, digraph_node *> node_mapping;
for (auto &iter : m_nodes)
result->add_node (iter->clone (*result, node_mapping));
@@ -241,10 +322,10 @@ diagnostics::digraphs::digraph::clone () const
}
void
-diagnostics::digraphs::digraph::add_edge (const char *id,
- node &src_node,
- node &dst_node,
- const char *label)
+digraph::add_edge (const char *id,
+ node &src_node,
+ node &dst_node,
+ const char *label)
{
auto e = std::make_unique<digraph_edge> (*this,
id,
@@ -263,7 +344,7 @@ diagnostics::digraphs::digraph::add_edge (const char *id,
to edges by id (SARIF 2.1.0's §3.43.2 edgeId property). */
std::string
-diagnostics::digraphs::digraph::make_edge_id (const char *edge_id)
+digraph::make_edge_id (const char *edge_id)
{
/* If we have an id, use it. */
if (edge_id)
@@ -284,27 +365,38 @@ diagnostics::digraphs::digraph::make_edge_id (const char *edge_id)
}
}
+const char *
+digraph::get_graph_kind () const
+{
+ return get_property (properties::digraph::kind);
+}
+
+void
+digraph::set_graph_kind (const char *kind)
+{
+ set_property (properties::digraph::kind, kind);
+}
+
// class node
DEBUG_FUNCTION void
-diagnostics::digraphs::node::dump () const
+digraph_node::dump () const
{
to_json_sarif_node ()->dump ();
}
std::unique_ptr<json::object>
-diagnostics::digraphs::node::to_json_sarif_node () const
+digraph_node::to_json_sarif_node () const
{
return make_sarif_node (*this, nullptr, nullptr);
}
-std::unique_ptr<diagnostics::digraphs::node>
-diagnostics::digraphs::node::clone (digraph &new_graph,
- std::map<node *, node *> &node_mapping) const
+std::unique_ptr<digraph_node>
+digraph_node::clone (digraph &new_graph,
+ std::map<node *, node *> &node_mapping) const
{
auto result
- = std::make_unique<diagnostics::digraphs::node> (new_graph,
- get_id ());
+ = std::make_unique<digraph_node> (new_graph, get_id ());
node_mapping.insert ({const_cast <node *> (this), result.get ()});
result->set_logical_loc (m_logical_loc);
@@ -353,6 +445,9 @@ diagnostics::digraphs::edge::to_json_sarif_edge () const
#if CHECKING_P
+#include "selftest.h"
+#include "custom-sarif-properties/state-graphs.h"
+
namespace diagnostics {
namespace selftest {
@@ -391,16 +486,17 @@ test_simple_graph ()
#define KEY_PREFIX "/placeholder/"
auto g = std::make_unique<digraph> ();
g->set_description ("test graph");
- g->set_attr (KEY_PREFIX, "date", "1066");
+ g->set_property (json::string_property (KEY_PREFIX, "date"), "1066");
auto a = std::make_unique<digraph_node> (*g, "a");
auto b = std::make_unique<digraph_node> (*g, "b");
- b->set_attr (KEY_PREFIX, "color", "red");
+ b->set_property (json::string_property (KEY_PREFIX, "color"), "red");
auto c = std::make_unique<digraph_node> (*g, "c");
c->set_label ("I am a node label");
auto e = std::make_unique<digraph_edge> (*g, nullptr, *a, *c);
- e->set_attr (KEY_PREFIX, "status", "copacetic");
+ e->set_property (json::string_property (KEY_PREFIX, "status"),
+ "copacetic");
e->set_label ("I am an edge label");
g->add_edge (std::move (e));
@@ -449,6 +545,34 @@ test_simple_graph ()
}
}
+static void
+test_property_objects ()
+{
+ namespace state_node_properties = custom_sarif_properties::state_graphs::node;
+
+ digraph g;
+ digraph_node node (g, "a");
+
+ ASSERT_EQ (node.get_property (state_node_properties::kind),
+ state_node_properties::kind::other);
+ node.set_property (state_node_properties::kind,
+ state_node_properties::kind::stack);
+ ASSERT_EQ (node.get_property (state_node_properties::kind),
+ state_node_properties::kind::stack);
+
+ ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
+ state_node_properties::dynalloc_state::unknown);
+ node.set_property (state_node_properties::dynalloc_state,
+ state_node_properties::dynalloc_state::freed);
+ ASSERT_EQ (node.get_property (state_node_properties::dynalloc_state),
+ state_node_properties::dynalloc_state::freed);
+
+ ASSERT_EQ (node.get_property (state_node_properties::type), nullptr);
+ node.set_property (state_node_properties::type, "const char *");
+ ASSERT_STREQ (node.get_property (state_node_properties::type),
+ "const char *");
+}
+
/* Run all of the selftests within this file. */
void
@@ -456,6 +580,7 @@ digraphs_cc_tests ()
{
test_empty_graph ();
test_simple_graph ();
+ test_property_objects ();
}
} // namespace diagnostics::selftest
diff --git a/gcc/diagnostics/digraphs.h b/gcc/diagnostics/digraphs.h
index 7193ee4..485a189 100644
--- a/gcc/diagnostics/digraphs.h
+++ b/gcc/diagnostics/digraphs.h
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#define GCC_DIAGNOSTICS_DIGRAPHS_H
#include "json.h"
+#include "tristate.h"
#include "diagnostics/logical-locations.h"
class graphviz_out;
@@ -55,23 +56,57 @@ class edge;
class object
{
public:
- const char *
- get_attr (const char *key_prefix,
- const char *key) const;
-
+ /* String properties. */
+ const char *get_property (const json::string_property &property) const;
+ void set_property (const json::string_property &property,
+ const char *utf8_value);
+
+ /* Integer properties. */
+ bool maybe_get_property (const json::integer_property &property, long &out) const;
+ void set_property (const json::integer_property &property, long value);
+
+ /* Bool properties. */
+ tristate
+ get_property_as_tristate (const json::bool_property &property) const;
+ void set_property (const json::bool_property &property, bool value);
+
+ /* Array-of-string properties. */
+ json::array *
+ get_property (const json::array_of_string_property &property) const;
+
+ /* enum properties. */
+ template <typename EnumType>
+ EnumType
+ get_property (const json::enum_property<EnumType> &property) const
+ {
+ if (m_property_bag)
+ {
+ EnumType result;
+ if (m_property_bag->maybe_get_enum<EnumType> (property, result))
+ return result;
+ }
+ return json::enum_traits<EnumType>::get_unknown_value ();
+ }
+ template <typename EnumType>
void
- set_attr (const char *key_prefix,
- const char *key,
- const char *value);
+ set_property (const json::enum_property<EnumType> &property,
+ EnumType value)
+ {
+ auto &bag = ensure_property_bag ();
+ bag.set_enum<EnumType> (property, value);
+ }
- void
- set_json_attr (const char *key_prefix,
- const char *key,
- std::unique_ptr<json::value> value);
+ /* json::value properties. */
+ const json::value *get_property (const json::json_property &property) const;
+ void set_property (const json::json_property &property,
+ std::unique_ptr<json::value> value);
json::object *
get_property_bag () const { return m_property_bag.get (); }
+ json::object &
+ ensure_property_bag ();
+
void
set_property_bag (std::unique_ptr<json::object> property_bag)
{
@@ -188,6 +223,9 @@ class digraph : public object
std::unique_ptr<digraph> clone () const;
+ const char *get_graph_kind () const;
+ void set_graph_kind (const char *);
+
private:
void
add_node_id (std::string node_id, node &new_node)
@@ -300,7 +338,7 @@ class node : public object
clone (digraph &new_graph,
std::map<node *, node *> &node_mapping) const;
- private:
+private:
std::string m_id;
std::unique_ptr<std::string> m_label;
std::vector<std::unique_ptr<node>> m_children;
diff --git a/gcc/diagnostics/html-sink.cc b/gcc/diagnostics/html-sink.cc
index d3fb107..99d3b9d 100644
--- a/gcc/diagnostics/html-sink.cc
+++ b/gcc/diagnostics/html-sink.cc
@@ -57,8 +57,8 @@ html_generation_options::html_generation_options ()
: m_css (true),
m_javascript (true),
m_show_state_diagrams (false),
- m_show_state_diagrams_sarif (false),
- m_show_state_diagrams_dot_src (false)
+ m_show_graph_sarif (false),
+ m_show_graph_dot_src (false)
{
}
@@ -68,8 +68,8 @@ html_generation_options::dump (FILE *outfile, int indent) const
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_css);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_javascript);
DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams);
- DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_sarif);
- DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_state_diagrams_dot_src);
+ DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_sarif);
+ DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_show_graph_dot_src);
}
class html_builder;
@@ -640,7 +640,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
the debug version. */
auto state_graph
= event.maybe_make_diagnostic_state_graph
- (m_html_gen_opts.m_show_state_diagrams_sarif);
+ (m_html_gen_opts.m_show_graph_sarif);
if (!state_graph)
return nullptr;
@@ -652,7 +652,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
auto wrapper = std::make_unique<xml::element> ("div", false);
xml::printer xp (*wrapper);
- if (m_html_gen_opts.m_show_state_diagrams_sarif)
+ if (m_html_gen_opts.m_show_graph_sarif)
{
// For debugging, show the SARIF src inline:
pretty_printer pp;
@@ -660,7 +660,7 @@ html_builder::maybe_make_state_diagram (const paths::event &event)
print_pre_source (xp, pp_formatted_text (&pp));
}
- if (m_html_gen_opts.m_show_state_diagrams_dot_src)
+ if (m_html_gen_opts.m_show_graph_dot_src)
{
// For debugging, show the dot src inline:
pretty_printer pp;
@@ -1278,21 +1278,41 @@ void
html_builder::add_graph (const digraphs::digraph &dg,
xml::element &parent_element)
{
+ auto div = std::make_unique<xml::element> ("div", false);
+ div->set_attr ("class", "gcc-directed-graph");
+ xml::printer xp (*div);
+
+ if (m_html_gen_opts.m_show_graph_sarif)
+ {
+ // For debugging, show the SARIF src inline:
+ pretty_printer pp;
+ dg.make_json_sarif_graph ()->print (&pp, true);
+ print_pre_source (xp, pp_formatted_text (&pp));
+ }
+
if (auto dot_graph = dg.make_dot_graph ())
- if (auto svg_element = dot::make_svg_from_graph (*dot_graph))
- {
- auto div = std::make_unique<xml::element> ("div", false);
- div->set_attr ("class", "gcc-directed-graph");
- xml::printer xp (*div);
- if (const char *description = dg.get_description ())
- {
- xp.push_tag ("h2", true);
- xp.add_text (description);
- xp.pop_tag ("h2");
- }
- xp.append (std::move (svg_element));
- parent_element.add_child (std::move (div));
- }
+ {
+ if (m_html_gen_opts.m_show_graph_dot_src)
+ {
+ // For debugging, show the dot src inline:
+ pretty_printer pp;
+ dot::writer w (pp);
+ dot_graph->print (w);
+ print_pre_source (xp, pp_formatted_text (&pp));
+ }
+
+ if (auto svg_element = dot::make_svg_from_graph (*dot_graph))
+ {
+ if (const char *description = dg.get_description ())
+ {
+ xp.push_tag ("h2", true);
+ xp.add_text (description);
+ xp.pop_tag ("h2");
+ }
+ xp.append (std::move (svg_element));
+ parent_element.add_child (std::move (div));
+ }
+ }
}
void
diff --git a/gcc/diagnostics/html-sink.h b/gcc/diagnostics/html-sink.h
index d25ceea..ad68e6f 100644
--- a/gcc/diagnostics/html-sink.h
+++ b/gcc/diagnostics/html-sink.h
@@ -40,11 +40,12 @@ struct html_generation_options
// If true, attempt to show state diagrams at events
bool m_show_state_diagrams;
- // If true, show the SARIF form of the state with such diagrams
- bool m_show_state_diagrams_sarif;
+ /* If true, show the SARIF form of the state with such diagrams,
+ and of other graphs. */
+ bool m_show_graph_sarif;
- // If true, show the .dot source used for the diagram
- bool m_show_state_diagrams_dot_src;
+ // If true, show the .dot source used for such graphs
+ bool m_show_graph_dot_src;
};
extern diagnostics::output_file
diff --git a/gcc/diagnostics/output-spec.cc b/gcc/diagnostics/output-spec.cc
index 28ea044f..f7cce0a 100644
--- a/gcc/diagnostics/output-spec.cc
+++ b/gcc/diagnostics/output-spec.cc
@@ -73,171 +73,160 @@ struct scheme_name_and_params
class output_factory
{
public:
- output_factory ();
+ output_factory (diagnostics::context &dc);
std::unique_ptr<sink>
make_sink (const context &ctxt,
diagnostics::context &dc,
const scheme_name_and_params &scheme_and_kvs);
- const scheme_handler *get_scheme_handler (const std::string &scheme_name);
+ scheme_handler *get_scheme_handler (const std::string &scheme_name);
private:
std::vector<std::unique_ptr<scheme_handler>> m_scheme_handlers;
};
-class scheme_handler
+enum key_handler::result
+key_handler::parse_bool_value (const context &ctxt,
+ const std::string &key,
+ const std::string &value,
+ bool &out) const
{
-public:
- scheme_handler (std::string scheme_name)
- : m_scheme_name (std::move (scheme_name))
- {}
- virtual ~scheme_handler () {}
-
- const std::string &get_scheme_name () const { return m_scheme_name; }
-
- virtual std::unique_ptr<sink>
- make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const = 0;
+ if (value == "yes")
+ {
+ out = true;
+ return result::ok;
+ }
+ else if (value == "no")
+ {
+ out = false;
+ return result::ok;
+ }
+ else
+ {
+ ctxt.report_error
+ ("%<%s%s%>:"
+ " unexpected value %qs for key %qs; expected %qs or %qs",
+ ctxt.get_option_name (), ctxt.get_unparsed_spec (),
+ value.c_str (),
+ key.c_str (),
+ "yes", "no");
+ return result::malformed_value;
+ }
+}
-protected:
- bool
- parse_bool_value (const context &ctxt,
- const std::string &key,
- const std::string &value,
- bool &out) const
- {
- if (value == "yes")
- {
- out = true;
- return true;
- }
- else if (value == "no")
- {
- out = false;
- return true;
- }
- else
+template <typename EnumType, size_t NumValues>
+key_handler::result
+key_handler::parse_enum_value (const context &ctxt,
+ const std::string &key,
+ const std::string &value,
+ const std::array<std::pair<const char *,
+ EnumType>,
+ NumValues> &value_names,
+ EnumType &out) const
+{
+ for (auto &iter : value_names)
+ if (value == iter.first)
{
- ctxt.report_error
- ("%<%s%s%>:"
- " unexpected value %qs for key %qs; expected %qs or %qs",
- ctxt.get_option_name (), ctxt.get_unparsed_spec (),
- value.c_str (),
- key.c_str (),
- "yes", "no");
-
- return false;
+ out = iter.second;
+ return result::ok;
}
- }
- template <typename EnumType, size_t NumValues>
- bool
- parse_enum_value (const context &ctxt,
- const std::string &key,
- const std::string &value,
- const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
- EnumType &out) const
- {
- for (auto &iter : value_names)
- if (value == iter.first)
- {
- out = iter.second;
- return true;
- }
-
- auto_vec<const char *> known_values;
- for (auto iter : value_names)
- known_values.safe_push (iter.first);
- pp_markup::comma_separated_quoted_strings e (known_values);
- ctxt.report_error
- ("%<%s%s%>:"
- " unexpected value %qs for key %qs; known values: %e",
- ctxt.get_option_name (), ctxt.get_unparsed_spec (),
- value.c_str (),
- key.c_str (),
- &e);
- return false;
- }
-private:
- const std::string m_scheme_name;
-};
+ auto_vec<const char *> known_values;
+ for (auto iter : value_names)
+ known_values.safe_push (iter.first);
+ pp_markup::comma_separated_quoted_strings e (known_values);
+ ctxt.report_error
+ ("%<%s%s%>:"
+ " unexpected value %qs for key %qs; known values: %e",
+ ctxt.get_option_name (), ctxt.get_unparsed_spec (),
+ value.c_str (),
+ key.c_str (),
+ &e);
+ return result::malformed_value;
+}
class text_scheme_handler : public scheme_handler
{
public:
- struct decoded_args
+ text_scheme_handler (diagnostics::context &dc)
+ : scheme_handler ("text"),
+ m_show_color (pp_show_color (dc.get_reference_printer ())),
+ m_show_nesting (true),
+ m_show_locations_in_nesting (true),
+ m_show_levels (false)
{
- bool m_show_color;
- bool m_show_nesting;
- bool m_show_locations_in_nesting;
- bool m_show_levels;
- };
-
- text_scheme_handler () : scheme_handler ("text") {}
+ }
std::unique_ptr<sink>
make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const final override;
+ diagnostics::context &dc) final override;
+
+ enum result
+ maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value) final override;
+
+ void
+ get_keys (auto_vec<const char *> &out) const final override;
- bool
- decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &out_opts) const;
+private:
+ bool m_show_color;
+ bool m_show_nesting;
+ bool m_show_locations_in_nesting;
+ bool m_show_levels;
};
class sarif_scheme_handler : public scheme_handler
{
public:
- struct decoded_args
+ sarif_scheme_handler ()
+ : scheme_handler ("sarif"),
+ m_serialization_kind (sarif_serialization_kind::json)
{
- label_text m_filename;
- enum sarif_serialization_kind m_serialization_kind;
- sarif_generation_options m_generation_opts;
- };
-
- sarif_scheme_handler () : scheme_handler ("sarif") {}
+ }
std::unique_ptr<sink>
make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const final override;
+ diagnostics::context &dc) final override;
- bool
- decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &out_opts) const;
+ enum result
+ maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value) final override;
+
+ void
+ get_keys (auto_vec<const char *> &out) const final override;
private:
static std::unique_ptr<sarif_serialization_format>
make_sarif_serialization_object (enum sarif_serialization_kind);
+
+ label_text m_filename;
+ enum sarif_serialization_kind m_serialization_kind;
+ sarif_generation_options m_generation_opts;
};
class html_scheme_handler : public scheme_handler
{
public:
- struct decoded_args
- {
- label_text m_filename;
- html_generation_options m_html_gen_opts;
- };
-
html_scheme_handler () : scheme_handler ("experimental-html") {}
std::unique_ptr<sink>
make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const final override;
+ diagnostics::context &dc) final override;
+
+ enum result
+ maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value) final override;
- bool
- decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &opts_out) const;
+ void
+ get_keys (auto_vec<const char *> &out) const final override;
+
+private:
+ label_text m_filename;
+ html_generation_options m_html_gen_opts;
};
/* struct context. */
@@ -253,15 +242,38 @@ context::report_error (const char *gmsgid, ...) const
void
context::report_unknown_key (const std::string &key,
- const std::string &scheme_name,
- auto_vec<const char *> &known_keys) const
+ const scheme_handler &scheme) const
{
- pp_markup::comma_separated_quoted_strings e (known_keys);
+ auto_vec<const char *> scheme_key_vec;
+ scheme.get_keys (scheme_key_vec);
+
+ pp_markup::comma_separated_quoted_strings e_scheme_keys (scheme_key_vec);
+
+ const char *scheme_name = scheme.get_scheme_name ().c_str ();
+
+ if (m_client_keys)
+ {
+ auto_vec<const char *> client_key_vec;
+ m_client_keys->get_keys (client_key_vec);
+ if (!client_key_vec.is_empty ())
+ {
+ pp_markup::comma_separated_quoted_strings e_client_keys
+ (client_key_vec);
+ report_error
+ ("%<%s%s%>:"
+ " unknown key %qs for output scheme %qs;"
+ " scheme keys: %e; client keys: %e",
+ get_option_name (), get_unparsed_spec (),
+ key.c_str (), scheme_name,
+ &e_scheme_keys, &e_client_keys);
+ }
+ }
+
report_error
("%<%s%s%>:"
- " unknown key %qs for format %qs; known keys: %e",
+ " unknown key %qs for output scheme %qs; scheme keys: %e",
get_option_name (), get_unparsed_spec (),
- key.c_str (), scheme_name.c_str (), &e);
+ key.c_str (), scheme_name, &e_scheme_keys);
}
void
@@ -348,7 +360,7 @@ context::parse_and_make_sink (diagnostics::context &dc)
if (!parsed_arg)
return nullptr;
- output_factory factory;
+ output_factory factory (dc);
return factory.make_sink (*this, dc, *parsed_arg);
}
@@ -356,14 +368,14 @@ context::parse_and_make_sink (diagnostics::context &dc)
/* class output_factory. */
-output_factory::output_factory ()
+output_factory::output_factory (diagnostics::context &dc)
{
- m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> ());
+ m_scheme_handlers.push_back (std::make_unique<text_scheme_handler> (dc));
m_scheme_handlers.push_back (std::make_unique<sarif_scheme_handler> ());
m_scheme_handlers.push_back (std::make_unique<html_scheme_handler> ());
}
-const scheme_handler *
+scheme_handler *
output_factory::get_scheme_handler (const std::string &scheme_name)
{
for (auto &iter : m_scheme_handlers)
@@ -391,61 +403,91 @@ output_factory::make_sink (const context &ctxt,
return nullptr;
}
- return scheme_handler->make_sink (ctxt, dc, scheme_and_kvs);
-}
-
-/* class text_scheme_handler : public scheme_handler. */
-
-std::unique_ptr<sink>
-text_scheme_handler::make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const
-{
- decoded_args opts;
- opts.m_show_color = pp_show_color (dc.get_reference_printer ());
- opts.m_show_nesting = true;
- opts.m_show_locations_in_nesting = true;
- opts.m_show_levels = false;
+ /* Parse key/value pairs. */
for (auto& iter : scheme_and_kvs.m_kvs)
{
const std::string &key = iter.first;
const std::string &value = iter.second;
- if (!decode_kv (ctxt, key, value, opts))
+ if (!ctxt.handle_kv (key, value, *scheme_handler))
return nullptr;
}
+ return scheme_handler->make_sink (ctxt, dc);
+}
+
+bool
+context::handle_kv (const std::string &key,
+ const std::string &value,
+ scheme_handler &scheme) const
+{
+ auto result = scheme.maybe_handle_kv (*this, key, value);
+ switch (result)
+ {
+ default: gcc_unreachable ();
+ case key_handler::result::ok:
+ return true;
+ case key_handler::result::malformed_value:
+ return false;
+ case key_handler::result::unrecognized:
+ /* Key recognized by the scheme; try the client keys. */
+ if (m_client_keys)
+ {
+ result = m_client_keys->maybe_handle_kv (*this, key, value);
+ switch (result)
+ {
+ default: gcc_unreachable ();
+ case key_handler::result::ok:
+ return true;
+ case key_handler::result::malformed_value:
+ return false;
+ case key_handler::result::unrecognized:
+ break;
+ }
+ }
+ report_unknown_key (key, scheme);
+ return false;
+ }
+}
+
+/* class text_scheme_handler : public scheme_handler. */
+
+std::unique_ptr<sink>
+text_scheme_handler::make_sink (const context &,
+ diagnostics::context &dc)
+{
auto sink = std::make_unique<diagnostics::text_sink> (dc);
- sink->set_show_nesting (opts.m_show_nesting);
- sink->set_show_locations_in_nesting (opts.m_show_locations_in_nesting);
- sink->set_show_nesting_levels (opts.m_show_levels);
- pp_show_color (sink->get_printer ()) = opts.m_show_color;
+ sink->set_show_nesting (m_show_nesting);
+ sink->set_show_locations_in_nesting (m_show_locations_in_nesting);
+ sink->set_show_nesting_levels (m_show_levels);
+ pp_show_color (sink->get_printer ()) = m_show_color;
return sink;
}
-bool
-text_scheme_handler::decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &opts_out) const
+enum key_handler::result
+text_scheme_handler::maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value)
{
if (key == "color")
- return parse_bool_value (ctxt, key, value, opts_out.m_show_color);
+ return parse_bool_value (ctxt, key, value, m_show_color);
if (key == "show-nesting")
- return parse_bool_value (ctxt, key, value, opts_out.m_show_nesting);
+ return parse_bool_value (ctxt, key, value, m_show_nesting);
if (key == "show-nesting-locations")
return parse_bool_value (ctxt, key, value,
- opts_out.m_show_locations_in_nesting);
+ m_show_locations_in_nesting);
if (key == "show-nesting-levels")
- return parse_bool_value (ctxt, key, value, opts_out.m_show_levels);
-
- /* Key not found. */
- auto_vec<const char *> known_keys;
- known_keys.safe_push ("color");
- known_keys.safe_push ("show-nesting");
- known_keys.safe_push ("show-nesting-locations");
- known_keys.safe_push ("show-nesting-levels");
- ctxt.report_unknown_key (key, get_scheme_name (), known_keys);
- return false;
+ return parse_bool_value (ctxt, key, value, m_show_levels);
+
+ return result::unrecognized;
+}
+
+void
+text_scheme_handler::get_keys (auto_vec<const char *> &out) const
+{
+ out.safe_push ("color");
+ out.safe_push ("show-nesting");
+ out.safe_push ("show-nesting-locations");
+ out.safe_push ("show-nesting-levels");
}
/* class sarif_scheme_handler : public scheme_handler. */
@@ -453,23 +495,11 @@ text_scheme_handler::decode_kv (const context &ctxt,
std::unique_ptr<sink>
sarif_scheme_handler::
make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const
+ diagnostics::context &dc)
{
- decoded_args opts;
- opts.m_serialization_kind = sarif_serialization_kind::json;
-
- for (auto& iter : scheme_and_kvs.m_kvs)
- {
- const std::string &key = iter.first;
- const std::string &value = iter.second;
- if (!decode_kv (ctxt, key, value, opts))
- return nullptr;
- }
-
output_file output_file_;
- if (opts.m_filename.get ())
- output_file_ = ctxt.open_output_file (std::move (opts.m_filename));
+ if (m_filename.get ())
+ output_file_ = ctxt.open_output_file (std::move (m_filename));
else
// Default filename
{
@@ -485,33 +515,32 @@ make_sink (const context &ctxt,
= open_sarif_output_file (dc,
ctxt.get_affected_location_mgr (),
basename,
- opts.m_serialization_kind);
+ m_serialization_kind);
}
if (!output_file_)
return nullptr;
auto serialization_obj
- = make_sarif_serialization_object (opts.m_serialization_kind);
+ = make_sarif_serialization_object (m_serialization_kind);
auto sink = make_sarif_sink (dc,
*ctxt.get_affected_location_mgr (),
std::move (serialization_obj),
- opts.m_generation_opts,
+ m_generation_opts,
std::move (output_file_));
return sink;
}
-bool
-sarif_scheme_handler::decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &opts_out) const
+enum key_handler::result
+sarif_scheme_handler::maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value)
{
if (key == "file")
{
- opts_out.m_filename = label_text::take (xstrdup (value.c_str ()));
- return true;
+ m_filename = label_text::take (xstrdup (value.c_str ()));
+ return result::ok;
}
if (key == "serialization")
{
@@ -522,7 +551,7 @@ sarif_scheme_handler::decode_kv (const context &ctxt,
(ctxt,
key, value,
value_names,
- opts_out.m_serialization_kind);
+ m_serialization_kind);
}
if (key == "version")
{
@@ -534,20 +563,22 @@ sarif_scheme_handler::decode_kv (const context &ctxt,
(ctxt,
key, value,
value_names,
- opts_out.m_generation_opts.m_version);
+ m_generation_opts.m_version);
}
if (key == "state-graphs")
return parse_bool_value (ctxt, key, value,
- opts_out.m_generation_opts.m_state_graph);
-
- /* Key not found. */
- auto_vec<const char *> known_keys;
- known_keys.safe_push ("file");
- known_keys.safe_push ("serialization");
- known_keys.safe_push ("state-graphs");
- known_keys.safe_push ("version");
- ctxt.report_unknown_key (key, get_scheme_name (), known_keys);
- return false;
+ m_generation_opts.m_state_graph);
+
+ return result::unrecognized;
+}
+
+void
+sarif_scheme_handler::get_keys (auto_vec<const char *> &out) const
+{
+ out.safe_push ("file");
+ out.safe_push ("serialization");
+ out.safe_push ("state-graphs");
+ out.safe_push ("version");
}
std::unique_ptr<sarif_serialization_format>
@@ -569,21 +600,11 @@ make_sarif_serialization_object (enum sarif_serialization_kind kind)
std::unique_ptr<sink>
html_scheme_handler::
make_sink (const context &ctxt,
- diagnostics::context &dc,
- const scheme_name_and_params &scheme_and_kvs) const
+ diagnostics::context &dc)
{
- decoded_args opts;
- for (auto& iter : scheme_and_kvs.m_kvs)
- {
- const std::string &key = iter.first;
- const std::string &value = iter.second;
- if (!decode_kv (ctxt, key, value, opts))
- return nullptr;
- }
-
output_file output_file_;
- if (opts.m_filename.get ())
- output_file_ = ctxt.open_output_file (std::move (opts.m_filename));
+ if (m_filename.get ())
+ output_file_ = ctxt.open_output_file (std::move (m_filename));
else
// Default filename
{
@@ -606,49 +627,47 @@ make_sink (const context &ctxt,
auto sink = make_html_sink (dc,
*ctxt.get_affected_location_mgr (),
- opts.m_html_gen_opts,
+ m_html_gen_opts,
std::move (output_file_));
return sink;
}
-bool
-html_scheme_handler::decode_kv (const context &ctxt,
- const std::string &key,
- const std::string &value,
- decoded_args &opts_out) const
+enum key_handler::result
+html_scheme_handler::maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value)
{
if (key == "css")
- return parse_bool_value (ctxt, key, value, opts_out.m_html_gen_opts.m_css);
+ return parse_bool_value (ctxt, key, value, m_html_gen_opts.m_css);
if (key == "file")
{
- opts_out.m_filename = label_text::take (xstrdup (value.c_str ()));
- return true;
+ m_filename = label_text::take (xstrdup (value.c_str ()));
+ return result::ok;
}
if (key == "javascript")
return parse_bool_value (ctxt, key, value,
- opts_out.m_html_gen_opts.m_javascript);
+ m_html_gen_opts.m_javascript);
if (key == "show-state-diagrams")
return parse_bool_value (ctxt, key, value,
- opts_out.m_html_gen_opts.m_show_state_diagrams);
- if (key == "show-state-diagrams-dot-src")
- return parse_bool_value
- (ctxt, key, value,
- opts_out.m_html_gen_opts.m_show_state_diagrams_dot_src);
- if (key == "show-state-diagrams-sarif")
- return parse_bool_value
- (ctxt, key, value,
- opts_out.m_html_gen_opts.m_show_state_diagrams_sarif);
-
- /* Key not found. */
- auto_vec<const char *> known_keys;
- known_keys.safe_push ("css");
- known_keys.safe_push ("file");
- known_keys.safe_push ("javascript");
- known_keys.safe_push ("show-state-diagrams");
- known_keys.safe_push ("show-state-diagram-dot-src");
- known_keys.safe_push ("show-state-diagram-sarif");
- ctxt.report_unknown_key (key, get_scheme_name (), known_keys);
- return false;
+ m_html_gen_opts.m_show_state_diagrams);
+ if (key == "show-graph-dot-src")
+ return parse_bool_value (ctxt, key, value,
+ m_html_gen_opts.m_show_graph_dot_src);
+ if (key == "show-graph-sarif")
+ return parse_bool_value (ctxt, key, value,
+ m_html_gen_opts.m_show_graph_sarif);
+ return result::unrecognized;
+}
+
+void
+html_scheme_handler::get_keys (auto_vec<const char *> &out) const
+{
+ out.safe_push ("css");
+ out.safe_push ("file");
+ out.safe_push ("javascript");
+ out.safe_push ("show-state-diagrams");
+ out.safe_push ("show-graph-dot-src");
+ out.safe_push ("show-graph-sarif");
}
} // namespace output_spec
@@ -685,17 +704,19 @@ struct parser_test
class test_spec_context : public diagnostics::output_spec::dc_spec_context
{
public:
- test_spec_context (diagnostics::context &dc,
+ test_spec_context (const char *option_name,
+ const char *unparsed_spec,
+ diagnostics::output_spec::key_handler *client_keys,
line_maps *location_mgr,
- location_t loc,
- const char *option_name,
- const char *unparsed_arg)
- : dc_spec_context (dc,
+ diagnostics::context &dc,
+ location_t loc)
+ : dc_spec_context (option_name,
+ unparsed_spec,
+ client_keys,
location_mgr,
+ dc,
location_mgr,
- loc,
- option_name,
- unparsed_arg)
+ loc)
{
}
@@ -706,9 +727,15 @@ struct parser_test
}
};
- parser_test (const char *unparsed_spec)
+ parser_test (const char *unparsed_spec,
+ diagnostics::output_spec::key_handler *client_keys = nullptr)
: m_dc (),
- m_ctxt (m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION=", unparsed_spec),
+ m_ctxt ("-fOPTION=",
+ unparsed_spec,
+ client_keys,
+ line_table,
+ m_dc,
+ UNKNOWN_LOCATION),
m_fmt (m_dc.get_sink (0))
{
pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
@@ -720,6 +747,12 @@ struct parser_test
return diagnostics::output_spec::parse (m_ctxt);
}
+ std::unique_ptr<diagnostics::sink>
+ parse_and_make_sink ()
+ {
+ return m_ctxt.parse_and_make_sink (m_dc);
+ }
+
bool execution_failed_p () const
{
return m_dc.execution_failed_p ();
@@ -742,9 +775,6 @@ private:
static void
test_output_arg_parsing ()
{
- auto_fix_quotes fix_quotes;
- auto_fix_progname fix_progname;
-
/* Minimal correct example. */
{
parser_test pt ("foo");
@@ -831,12 +861,59 @@ test_output_arg_parsing ()
}
}
+class test_key_handler : public diagnostics::output_spec::key_handler
+{
+public:
+ test_key_handler ()
+ : m_verbose (false),
+ m_strict (false)
+ {
+ }
+
+ enum result
+ maybe_handle_kv (const diagnostics::output_spec::context &ctxt,
+ const std::string &key,
+ const std::string &value) final override
+ {
+ if (key == "verbose")
+ return parse_bool_value (ctxt, key, value, m_verbose);
+ if (key == "strict")
+ return parse_bool_value (ctxt, key, value, m_strict);
+ return result::unrecognized;
+ }
+
+ void
+ get_keys (auto_vec<const char *> &out_known_keys) const final override
+ {
+ out_known_keys.safe_push ("verbose");
+ out_known_keys.safe_push ("strict");
+ }
+
+ bool m_verbose;
+ bool m_strict;
+};
+
+static void
+test_client_arg_parsing ()
+{
+ test_key_handler client_keys;
+ parser_test pt ("text:verbose=yes,strict=no", &client_keys);
+ auto result = pt.parse_and_make_sink ();
+ ASSERT_TRUE (result.get ());
+ ASSERT_TRUE (client_keys.m_verbose);
+ ASSERT_FALSE (client_keys.m_strict);
+}
+
/* Run all of the selftests within this file. */
void
output_spec_cc_tests ()
{
+ auto_fix_quotes fix_quotes;
+ auto_fix_progname fix_progname;
+
test_output_arg_parsing ();
+ test_client_arg_parsing ();
}
} // namespace diagnostics::selftest
diff --git a/gcc/diagnostics/output-spec.h b/gcc/diagnostics/output-spec.h
index e24002b..bfc42c0 100644
--- a/gcc/diagnostics/output-spec.h
+++ b/gcc/diagnostics/output-spec.h
@@ -27,12 +27,71 @@ along with GCC; see the file COPYING3. If not see
namespace diagnostics {
namespace output_spec {
+class context;
+
+/* An abstract base class for schemes, and for client-specific keys. */
+
+class key_handler
+{
+public:
+ enum class result
+ {
+ ok,
+ unrecognized,
+ malformed_value
+ };
+
+ /* Attempt to decode KEY and VALUE, storing the decoded value. */
+ virtual enum result
+ maybe_handle_kv (const context &ctxt,
+ const std::string &key,
+ const std::string &value) = 0;
+
+ virtual void
+ get_keys (auto_vec<const char *> &out) const = 0;
+
+ enum result
+ parse_bool_value (const context &ctxt,
+ const std::string &key,
+ const std::string &value,
+ bool &out) const;
+
+ template <typename EnumType, size_t NumValues>
+ enum result
+ parse_enum_value (const context &ctxt,
+ const std::string &key,
+ const std::string &value,
+ const std::array<std::pair<const char *, EnumType>,
+ NumValues> &value_names,
+ EnumType &out) const;
+};
+
+/* Abstract subclass for handling particular schemes and their keys. */
+
+class scheme_handler : public key_handler
+{
+public:
+ scheme_handler (std::string scheme_name)
+ : m_scheme_name (std::move (scheme_name))
+ {}
+ virtual ~scheme_handler () {}
+
+ const std::string &get_scheme_name () const { return m_scheme_name; }
+
+ virtual std::unique_ptr<sink>
+ make_sink (const context &ctxt,
+ diagnostics::context &dc) = 0;
+
+private:
+ const std::string m_scheme_name;
+};
+
/* An abstract base class for handling the DSL of -fdiagnostics-add-output=
and -fdiagnostics-set-output=. */
class context
{
- public:
+public:
std::unique_ptr<sink>
parse_and_make_sink (diagnostics::context &dc);
@@ -42,8 +101,7 @@ class context
void
report_unknown_key (const std::string &key,
- const std::string &scheme_name,
- auto_vec<const char *> &known_keys) const;
+ const scheme_handler &scheme) const;
void
report_missing_key (const std::string &key,
@@ -70,12 +128,19 @@ class context
virtual const char *
get_base_filename () const = 0;
+ bool
+ handle_kv (const std::string &key,
+ const std::string &value,
+ scheme_handler &scheme) const;
+
protected:
context (const char *option_name,
const char *unparsed_spec,
+ key_handler *client_keys,
line_maps *affected_location_mgr)
: m_option_name (option_name),
m_unparsed_spec (unparsed_spec),
+ m_client_keys (client_keys),
m_affected_location_mgr (affected_location_mgr)
{
}
@@ -86,6 +151,9 @@ protected:
// e.g. "scheme:foo=bar,key=value"
const char *m_unparsed_spec;
+ // Optional borrowed ptr to client-specific keys
+ key_handler *m_client_keys;
+
line_maps *m_affected_location_mgr;
};
@@ -94,13 +162,17 @@ protected:
struct dc_spec_context : public output_spec::context
{
public:
- dc_spec_context (diagnostics::context &dc,
+ dc_spec_context (const char *option_name,
+ const char *unparsed_spec,
+ key_handler *client_keys,
line_maps *affected_location_mgr,
+ diagnostics::context &dc,
line_maps *control_location_mgr,
- location_t loc,
- const char *option_name,
- const char *unparsed_spec)
- : context (option_name, unparsed_spec, affected_location_mgr),
+ location_t loc)
+ : context (option_name,
+ unparsed_spec,
+ client_keys,
+ affected_location_mgr),
m_dc (dc),
m_control_location_mgr (control_location_mgr),
m_loc (loc)
diff --git a/gcc/diagnostics/state-graphs-to-dot.cc b/gcc/diagnostics/state-graphs-to-dot.cc
index 2d80e6b..8a3ad24 100644
--- a/gcc/diagnostics/state-graphs-to-dot.cc
+++ b/gcc/diagnostics/state-graphs-to-dot.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
+#include "custom-sarif-properties/state-graphs.h"
#include "diagnostics/state-graphs.h"
#include "graphviz.h"
#include "xml.h"
@@ -36,6 +37,8 @@ along with GCC; see the file COPYING3. If not see
using namespace diagnostics;
using namespace diagnostics::state_graphs;
+namespace state_node_properties = custom_sarif_properties::state_graphs::node;
+
static int
get_depth (const digraphs::node &n)
{
@@ -47,28 +50,28 @@ get_depth (const digraphs::node &n)
}
static const char *
-get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st)
+get_color_for_dynalloc_state (enum state_node_properties::dynalloc_state dynalloc_st)
{
switch (dynalloc_st)
{
default:
gcc_unreachable ();
break;
- case node_dynalloc_state::unknown:
- case node_dynalloc_state::nonnull:
+ case state_node_properties::dynalloc_state::unknown:
+ case state_node_properties::dynalloc_state::nonnull:
return nullptr;
- case node_dynalloc_state::unchecked:
+ case state_node_properties::dynalloc_state::unchecked:
return "#ec7a08"; // pf-orange-400
- case node_dynalloc_state::freed:
+ case state_node_properties::dynalloc_state::freed:
return "#cc0000"; // pf-red-100
}
}
static void
set_color_for_dynalloc_state (dot::attr_list &attrs,
- enum node_dynalloc_state state)
+ enum state_node_properties::dynalloc_state state)
{
if (const char *color = get_color_for_dynalloc_state (state))
attrs.add (dot::id ("color"), dot::id (color));
@@ -106,7 +109,7 @@ public:
= std::make_unique<dot::subgraph> (dot::id ("cluster_memory_regions"));
for (size_t i = 0; i < input_state_graph.get_num_nodes (); ++i)
on_input_state_node (*root_cluster,
- state_node_ref (input_state_graph.get_node (i)));
+ input_state_graph.get_node (i));
add_stmt (std::move (root_cluster));
/* Now create dot edges for edges in input_stage_graph. */
@@ -126,7 +129,8 @@ public:
auto e = std::make_unique<dot::edge_stmt> (src_port_id->second,
dst_port_id->second);
set_color_for_dynalloc_state
- (e->m_attrs, state_node_ref (dst_node).get_dynalloc_state ());
+ (e->m_attrs,
+ dst_node.get_property (state_node_properties::dynalloc_state));
add_stmt (std::move (e));
}
@@ -147,9 +151,9 @@ private:
}
dot::id
- make_id (state_node_ref state_node, bool cluster)
+ make_id (const diagnostics::digraphs::node &state_node, bool cluster)
{
- std::string input_node_id = state_node.m_node.get_id ();
+ std::string input_node_id = state_node.get_id ();
if (cluster)
return std::string ("cluster_") + input_node_id;
else
@@ -157,44 +161,44 @@ private:
}
bool
- starts_node_p (state_node_ref state_node)
+ starts_node_p (const diagnostics::digraphs::node &state_node)
{
- switch (state_node.get_node_kind ())
+ switch (state_node.get_property (state_node_properties::kind))
{
default:
return false;
- case node_kind::stack:
+ case state_node_properties::kind::stack:
/* We want all frames in the stack in the same table,
so they are grouped. */
- case node_kind::dynalloc_buffer:
- case node_kind::variable:
+ case state_node_properties::kind::dynalloc_buffer:
+ case state_node_properties::kind::variable:
return true;
}
}
const char *
- get_label_for_node (state_node_ref state_node)
+ get_label_for_node (const diagnostics::digraphs::node &state_node)
{
- switch (state_node.get_node_kind ())
+ switch (state_node.get_property (state_node_properties::kind))
{
default:
return nullptr;
- case node_kind::globals:
+ case state_node_properties::kind::globals:
return _("Globals");
- case node_kind::code:
+ case state_node_properties::kind::code:
return _("Code");
- case node_kind::stack:
+ case state_node_properties::kind::stack:
return _("Stack");
- case node_kind::heap_:
+ case state_node_properties::kind::heap_:
return _("Heap");
}
}
void
on_input_state_node (dot::subgraph &parent_subgraph,
- state_node_ref state_node)
+ const diagnostics::digraphs::node &state_node)
{
dot::id sg_id = make_id (state_node, true);
@@ -207,7 +211,7 @@ private:
xp.set_attr ("cellborder", "1");
xp.set_attr ("cellspacing", "0");
- const int max_depth = get_depth (state_node.m_node);
+ const int max_depth = get_depth (state_node);
const int num_columns = max_depth + 2;
dot::id id_of_dot_node = make_id (state_node, false);
@@ -233,9 +237,9 @@ private:
child_subgraph->add_attr (dot::id ("label"), dot::id (label));
// recurse:
- for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
+ for (size_t i = 0; i < state_node.get_num_children (); ++i)
on_input_state_node (*child_subgraph,
- state_node.m_node.get_child (i));
+ state_node.get_child (i));
parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph));
}
}
@@ -246,10 +250,10 @@ private:
add_title_tr (const dot::id &id_of_dot_node,
xml::printer &xp,
int num_columns,
- state_node_ref state_node,
+ const diagnostics::digraphs::node &state_node,
std::string heading,
enum style styl,
- enum node_dynalloc_state dynalloc_state)
+ enum state_node_properties::dynalloc_state dynalloc_state)
{
xp.push_tag ("tr", true);
xp.push_tag ("td", false);
@@ -298,48 +302,49 @@ private:
void
on_node_in_table (const dot::id &id_of_dot_node,
xml::printer &xp,
- state_node_ref state_node,
+ const diagnostics::digraphs::node &state_node,
int max_depth,
int depth,
int num_columns)
{
bool recurse = true;
- auto input_node_kind = state_node.get_node_kind ();
+ auto input_node_kind = state_node.get_property (state_node_properties::kind);
switch (input_node_kind)
{
- case node_kind::padding:
- case node_kind::other:
+ case state_node_properties::kind::padding:
+ case state_node_properties::kind::other:
return;
- case node_kind::stack:
+ case state_node_properties::kind::stack:
add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack",
style::h1,
- node_dynalloc_state::unknown);
+ state_node_properties::dynalloc_state::unknown);
break;
- case node_kind::stack_frame:
+ case state_node_properties::kind::stack_frame:
if (auto logical_loc = state_node.get_logical_loc ())
if (const char *function
= m_logical_loc_mgr.get_short_name (logical_loc))
add_title_tr (id_of_dot_node, xp, num_columns, state_node,
std::string ("Frame: ") + function,
style::h2,
- node_dynalloc_state::unknown);
+ state_node_properties::dynalloc_state::unknown);
break;
- case node_kind::dynalloc_buffer:
+ case state_node_properties::kind::dynalloc_buffer:
{
- enum node_dynalloc_state dynalloc_st
- = state_node.get_dynalloc_state ();
- const char *extents = state_node.get_dynamic_extents ();
- const char *type = state_node.get_type ();
+ enum state_node_properties::dynalloc_state dynalloc_st
+ = state_node.get_property (state_node_properties::dynalloc_state);
+ const char *extents
+ = state_node.get_property (state_node_properties::dynamic_extents);
+ const char *type = state_node.get_property (state_node_properties::type);
pretty_printer pp;
switch (dynalloc_st)
{
default:
gcc_unreachable ();
- case node_dynalloc_state::unknown:
- case node_dynalloc_state::nonnull:
+ case state_node_properties::dynalloc_state::unknown:
+ case state_node_properties::dynalloc_state::nonnull:
if (type)
{
if (extents)
@@ -356,7 +361,7 @@ private:
}
break;
- case node_dynalloc_state::unchecked:
+ case state_node_properties::dynalloc_state::unchecked:
if (type)
{
if (extents)
@@ -371,7 +376,7 @@ private:
}
break;
- case node_dynalloc_state::freed:
+ case state_node_properties::dynalloc_state::freed:
// TODO: show deallocator
// TODO: show deallocation event
pp_printf (&pp, "Freed buffer");
@@ -404,9 +409,10 @@ private:
{
default:
break;
- case node_kind::variable:
+ case state_node_properties::kind::variable:
{
- const char *name = state_node.get_name ();
+ const char *name
+ = state_node.get_property (state_node_properties::name);
gcc_assert (name);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -416,9 +422,10 @@ private:
xp.pop_tag ("td");
}
break;
- case node_kind::element:
+ case state_node_properties::kind::element:
{
- const char *index = state_node.get_index ();
+ const char *index
+ = state_node.get_property (state_node_properties::index);
gcc_assert (index);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -430,9 +437,10 @@ private:
xp.pop_tag ("td");
}
break;
- case node_kind::field:
+ case state_node_properties::kind::field:
{
- const char *name = state_node.get_name ();
+ const char *name
+ = state_node.get_property (state_node_properties::name);
gcc_assert (name);
xp.push_tag ("td", false);
maybe_add_dst_port (id_of_dot_node, xp, state_node);
@@ -445,7 +453,8 @@ private:
break;
}
- if (const char *type = state_node.get_type ())
+ if (const char *type
+ = state_node.get_property (state_node_properties::type))
{
xp.push_tag ("td", false);
xp.set_attr ("align", "right");
@@ -455,7 +464,8 @@ private:
xp.pop_tag ("td");
}
- if (const char *value = state_node.get_value ())
+ if (const char *value
+ = state_node.get_property (state_node_properties::value_str))
{
xp.push_tag ("td", false);
xp.set_attr ("align", "left");
@@ -466,15 +476,16 @@ private:
xp.pop_tag ("td");
recurse = false;
}
+
xp.pop_tag ("tr");
}
break;
}
if (recurse)
- for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i)
+ for (size_t i = 0; i < state_node.get_num_children (); ++i)
on_node_in_table (id_of_dot_node, xp,
- state_node.m_node.get_child (i),
+ state_node.get_child (i),
max_depth, depth + 1, num_columns);
}
@@ -497,9 +508,9 @@ private:
void
maybe_add_src_port (const dot::id &id_of_dot_node,
xml::printer &xp,
- state_node_ref state_node)
+ const diagnostics::digraphs::node &state_node)
{
- auto iter = m_src_nodes.find (&state_node.m_node);
+ auto iter = m_src_nodes.find (&state_node);
if (iter == m_src_nodes.end ())
return;
@@ -507,7 +518,7 @@ private:
dot::node_id node_id (id_of_dot_node,
dot::port (src_id,
dot::compass_pt::e));
- m_src_node_to_port_id.insert ({&state_node.m_node, node_id});
+ m_src_node_to_port_id.insert ({&state_node, node_id});
xp.set_attr ("port", src_id.m_str);
}
@@ -517,9 +528,9 @@ private:
void
maybe_add_dst_port (const dot::id &id_of_dot_node,
xml::printer &xp,
- state_node_ref state_node)
+ const diagnostics::digraphs::node &state_node)
{
- auto iter = m_dst_nodes.find (&state_node.m_node);
+ auto iter = m_dst_nodes.find (&state_node);
if (iter == m_dst_nodes.end ())
return;
@@ -527,7 +538,7 @@ private:
dot::node_id node_id (id_of_dot_node,
dot::port (dst_id/*,
dot::compass_pt::w*/));
- m_dst_node_to_port_id.insert ({&state_node.m_node, node_id});
+ m_dst_node_to_port_id.insert ({&state_node, node_id});
xp.set_attr ("port", dst_id.m_str);
}
@@ -535,11 +546,11 @@ private:
const logical_locations::manager &m_logical_loc_mgr;
/* All nodes involved in edges (and thus will need a port). */
- std::set<digraphs::node *> m_src_nodes;
- std::set<digraphs::node *> m_dst_nodes;
+ std::set<const digraphs::node *> m_src_nodes;
+ std::set<const digraphs::node *> m_dst_nodes;
- std::map<digraphs::node *, dot::node_id> m_src_node_to_port_id;
- std::map<digraphs::node *, dot::node_id> m_dst_node_to_port_id;
+ std::map<const digraphs::node *, dot::node_id> m_src_node_to_port_id;
+ std::map<const digraphs::node *, dot::node_id> m_dst_node_to_port_id;
};
std::unique_ptr<dot::graph>
diff --git a/gcc/diagnostics/state-graphs.cc b/gcc/diagnostics/state-graphs.cc
deleted file mode 100644
index 5941c41..0000000
--- a/gcc/diagnostics/state-graphs.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-/* Extensions to diagnostics::digraphs to support state graphs.
- Copyright (C) 2025 Free Software Foundation, Inc.
- Contributed by David Malcolm <dmalcolm@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/>. */
-
-#define INCLUDE_ALGORITHM
-#define INCLUDE_MAP
-#define INCLUDE_SET
-#define INCLUDE_STRING
-#define INCLUDE_VECTOR
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-
-#include "diagnostics/state-graphs.h"
-#include "selftest.h"
-
-using namespace diagnostics::state_graphs;
-
-const char * const node_kind_strs[] = {
- "globals",
- "code",
- "function",
- "stack",
- "stack-frame",
- "heap",
- "thread-local",
- "dynalloc-buffer",
- "variable",
- "field",
- "padding",
- "element",
- "other",
-};
-
-const char *
-diagnostics::state_graphs::node_kind_to_str (enum node_kind k)
-{
- return node_kind_strs[static_cast<int> (k)];
-}
-
-// struct state_node_ref
-
-enum node_kind
-state_node_ref::get_node_kind () const
-{
- const char *value = get_attr ("kind");
- if (!value)
- return node_kind::other;
-
- for (size_t i = 0; i < ARRAY_SIZE (node_kind_strs); ++i)
- if (!strcmp (node_kind_strs[i], value))
- return static_cast<enum node_kind> (i);
-
- return node_kind::other;
-}
-
-void
-state_node_ref::set_node_kind (enum node_kind k)
-{
- set_attr ("kind", node_kind_to_str (k));
-}
-
-const char * const dynalloc_state_strs[] = {
- "unknown",
- "nonnull",
- "unchecked",
- "freed"
-};
-
-enum node_dynalloc_state
-state_node_ref::get_dynalloc_state () const
-{
- const char *value = get_attr ("dynalloc-state");
- if (!value)
- return node_dynalloc_state::unknown;
-
- for (size_t i = 0; i < ARRAY_SIZE (dynalloc_state_strs); ++i)
- if (!strcmp (dynalloc_state_strs[i], value))
- return static_cast<enum node_dynalloc_state> (i);
-
- return node_dynalloc_state::unknown;
-}
-
-void
-state_node_ref::set_dynalloc_state (enum node_dynalloc_state s) const
-{
- set_attr ("dynalloc-state",
- dynalloc_state_strs[static_cast <size_t> (s)]);
-}
-
-const char *
-state_node_ref::get_dynamic_extents () const
-{
- return m_node.get_attr (STATE_NODE_PREFIX, "dynamic-extents");
-}
-
-void
-state_node_ref::set_json_attr (const char *key,
- std::unique_ptr<json::value> value) const
-{
- m_node.set_json_attr (STATE_NODE_PREFIX, key, std::move (value));
-}
-
-#if CHECKING_P
-
-namespace diagnostics {
-namespace selftest {
-
-static void
-test_node_attrs ()
-{
- digraphs::digraph g;
- digraphs::node n (g, "a");
- state_node_ref node_ref (n);
-
- ASSERT_EQ (node_ref.get_node_kind (), node_kind::other);
- node_ref.set_node_kind (node_kind::stack);
- ASSERT_EQ (node_ref.get_node_kind (), node_kind::stack);
-
- ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::unknown);
- node_ref.set_dynalloc_state (node_dynalloc_state::freed);
- ASSERT_EQ (node_ref.get_dynalloc_state (), node_dynalloc_state::freed);
-
- ASSERT_EQ (node_ref.get_type (), nullptr);
- node_ref.set_type ("const char *");
- ASSERT_STREQ (node_ref.get_type (), "const char *");
-}
-
-/* Run all of the selftests within this file. */
-
-void
-state_graphs_cc_tests ()
-{
- test_node_attrs ();
-}
-
-} // namespace diagnostics::selftest
-} // namespace diagnostics
-
-#endif /* CHECKING_P */
diff --git a/gcc/diagnostics/state-graphs.h b/gcc/diagnostics/state-graphs.h
index ad18f82..21aded0 100644
--- a/gcc/diagnostics/state-graphs.h
+++ b/gcc/diagnostics/state-graphs.h
@@ -22,7 +22,6 @@ along with GCC; see the file COPYING3. If not see
#define GCC_DIAGNOSTICS_STATE_GRAPHS_H
#include "diagnostics/digraphs.h"
-#include "diagnostics/logical-locations.h"
/* diagnostics::digraphs provides support for directed graphs.
@@ -34,118 +33,11 @@ along with GCC; see the file COPYING3. If not see
in these nodes to stash extra properties (e.g. what kind of memory region
a node is e.g. stack vs heap). */
-class sarif_graph;
namespace dot { class graph; }
namespace diagnostics {
namespace state_graphs {
-enum class node_kind
-{
- // Memory regions
- globals,
- code,
- function, // code within a particular function
- stack,
- stack_frame,
- heap_,
- thread_local_,
-
- /* Dynamically-allocated buffer,
- on heap or stack (depending on parent). */
- dynalloc_buffer,
-
- variable,
-
- field, // field within a struct or union
- padding, // padding bits in a struct or union
- element, // element within an array
-
- other // anything else
-};
-
-extern const char *
-node_kind_to_str (enum node_kind);
-
-enum class node_dynalloc_state
-{
- unknown,
- nonnull,
- unchecked,
- freed
-};
-
-/* Prefixes to use in SARIF property bags. */
-#define STATE_GRAPH_PREFIX "gcc/diagnostic_state_graph/"
-#define STATE_NODE_PREFIX "gcc/diagnostic_state_node/"
-#define STATE_EDGE_PREFIX "gcc/diagnostic_state_edge/"
-
-/* A wrapper around a node that gets/sets attributes, using
- the node's property bag for storage, so that the data roundtrips
- through SARIF. */
-
-struct state_node_ref
-{
- state_node_ref (diagnostics::digraphs::node &node)
- : m_node (node)
- {}
-
- enum node_kind
- get_node_kind () const;
- void
- set_node_kind (enum node_kind);
-
- // For node_kind::stack_frame, this will be the function
- logical_locations::key
- get_logical_loc () const
- {
- return m_node.get_logical_loc ();
- }
-
- // For node_kind::dynalloc_buffer
- enum node_dynalloc_state
- get_dynalloc_state () const;
-
- void
- set_dynalloc_state (enum node_dynalloc_state) const;
-
- const char *
- get_dynamic_extents () const;
-
- const char *
- get_name () const { return get_attr ("name"); }
- void
- set_name (const char *name) const { set_attr ("name", name); }
-
- const char *
- get_type () const { return get_attr ("type"); }
- void
- set_type (const char *type) const { set_attr ("type", type); }
-
- const char *
- get_value () const { return get_attr ("value"); }
-
- const char *
- get_index () const { return get_attr ("index"); }
-
- const char *
- get_attr (const char *key) const
- {
- return m_node.get_attr (STATE_NODE_PREFIX, key);
- }
-
- void
- set_attr (const char *key, const char *value) const
- {
- return m_node.set_attr (STATE_NODE_PREFIX, key, value);
- }
-
- void
- set_json_attr (const char *key, std::unique_ptr<json::value> value) const;
-
- diagnostics::digraphs::node &m_node;
-};
-
extern std::unique_ptr<dot::graph>
make_dot_graph (const diagnostics::digraphs::digraph &state_graph,
const logical_locations::manager &logical_loc_mgr);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 492ca29..81a495b 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -6271,16 +6271,16 @@ These are visible by pressing ``j'' and ``k'' to single-step forward and
backward through events. Enabling this option will slow down
HTML generation.
-@item show-state-diagrams-dot-src=@r{[}yes@r{|}no@r{]}
+@item show-graph-dot-src=@r{[}yes@r{|}no@r{]}
This is a debugging feature and defaults to @code{no}.
-If @code{show-state-diagrams-dot-src=yes}
+If @code{show-graph-dot-src=yes}
then if @code{show-state-diagrams=yes},
the generated state diagrams will also show the .dot source input to
GraphViz used for the diagram.
-@item show-state-diagrams-sarif=@r{[}yes@r{|}no@r{]}
+@item show-graph-sarif=@r{[}yes@r{|}no@r{]}
This is a debugging feature and defaults to @code{no}.
-If @code{show-state-diagrams-sarif=yes}
+If @code{show-graph-sarif=yes}
then if @code{show-state-diagrams=yes}, the generated state diagrams will
also show a SARIF representation of the state.
diff --git a/gcc/doc/passes.texi b/gcc/doc/passes.texi
index 282fc1a..f6db15d 100644
--- a/gcc/doc/passes.texi
+++ b/gcc/doc/passes.texi
@@ -735,12 +735,6 @@ cannot be used for branch prediction (though adapting it would
not be difficult). The pass is located in @file{tree-vrp.cc} and is
described by @code{pass_vrp}.
-@item Folding built-in functions
-
-This pass simplifies built-in functions, as applicable, with constant
-arguments or with inferable string lengths. It is located in
-@file{tree-ssa-ccp.cc} and is described by @code{pass_fold_builtins}.
-
@item Split critical edges
This pass identifies critical edges and inserts empty basic blocks
diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index f00f0e1..3761b65 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -4038,7 +4038,15 @@ gfc_get_pdt_instance (gfc_actual_arglist *param_list, gfc_symbol **sym,
}
kind_value = 0;
- gfc_extract_int (kind_expr, &kind_value);
+ /* This can come about during the parsing of nested pdt_templates. An
+ error arises because the KIND parameter expression has not been
+ provided. Use the template instead of an incorrect instance. */
+ if (gfc_extract_int (kind_expr, &kind_value))
+ {
+ gfc_free_actual_arglist (type_param_spec_list);
+ return MATCH_YES;
+ }
+
sprintf (name + strlen (name), "_%d", kind_value);
if (!name_seen && actual_param)
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 37ca085..2f64de2 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -5252,6 +5252,127 @@ gimple_fold_builtin_assume_aligned (gimple_stmt_iterator *gsi)
return true;
}
+/* If va_list type is a simple pointer and nothing special is needed,
+ optimize __builtin_va_start (&ap, 0) into ap = __builtin_next_arg (0),
+ __builtin_va_end (&ap) out as NOP and __builtin_va_copy into a simple
+ pointer assignment. Returns true if a change happened. */
+
+static bool
+gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, gcall *call)
+{
+ /* These shouldn't be folded before pass_stdarg. */
+ if (!(cfun->curr_properties & PROP_last_full_fold))
+ return false;
+
+ tree callee, lhs, rhs, cfun_va_list;
+ bool va_list_simple_ptr;
+ location_t loc = gimple_location (call);
+ gimple *nstmt0, *nstmt;
+ tree tlhs, oldvdef, newvdef;
+
+ callee = gimple_call_fndecl (call);
+
+ cfun_va_list = targetm.fn_abi_va_list (callee);
+ va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list)
+ && (TREE_TYPE (cfun_va_list) == void_type_node
+ || TREE_TYPE (cfun_va_list) == char_type_node);
+
+ switch (DECL_FUNCTION_CODE (callee))
+ {
+ case BUILT_IN_VA_START:
+ if (!va_list_simple_ptr
+ || targetm.expand_builtin_va_start != NULL
+ || !builtin_decl_explicit_p (BUILT_IN_NEXT_ARG))
+ return false;
+
+ if (gimple_call_num_args (call) != 2)
+ return false;
+
+ lhs = gimple_call_arg (call, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+ /* Create `tlhs = __builtin_next_arg(0);`. */
+ tlhs = make_ssa_name (cfun_va_list);
+ nstmt0 = gimple_build_call (builtin_decl_explicit (BUILT_IN_NEXT_ARG), 1, integer_zero_node);
+ lhs = fold_build2 (MEM_REF, cfun_va_list, lhs, build_zero_cst (TREE_TYPE (lhs)));
+ gimple_call_set_lhs (nstmt0, tlhs);
+ gimple_set_location (nstmt0, loc);
+ gimple_move_vops (nstmt0, call);
+ gsi_replace (gsi, nstmt0, false);
+ oldvdef = gimple_vdef (nstmt0);
+ newvdef = make_ssa_name (gimple_vop (cfun), nstmt0);
+ gimple_set_vdef (nstmt0, newvdef);
+
+ /* Create `*lhs = tlhs;`. */
+ nstmt = gimple_build_assign (lhs, tlhs);
+ gimple_set_location (nstmt, loc);
+ gimple_set_vuse (nstmt, newvdef);
+ gimple_set_vdef (nstmt, oldvdef);
+ SSA_NAME_DEF_STMT (oldvdef) = nstmt;
+ gsi_insert_after (gsi, nstmt, GSI_NEW_STMT);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Simplified\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ fprintf (dump_file, "into\n ");
+ print_gimple_stmt (dump_file, nstmt0, 0, dump_flags);
+ fprintf (dump_file, " ");
+ print_gimple_stmt (dump_file, nstmt, 0, dump_flags);
+ }
+ return true;
+
+ case BUILT_IN_VA_COPY:
+ if (!va_list_simple_ptr)
+ return false;
+
+ if (gimple_call_num_args (call) != 2)
+ return false;
+
+ lhs = gimple_call_arg (call, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+ rhs = gimple_call_arg (call, 1);
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+
+ lhs = fold_build2 (MEM_REF, cfun_va_list, lhs, build_zero_cst (TREE_TYPE (lhs)));
+ nstmt = gimple_build_assign (lhs, rhs);
+ gimple_set_location (nstmt, loc);
+ gimple_move_vops (nstmt, call);
+ gsi_replace (gsi, nstmt, false);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Simplified\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ fprintf (dump_file, "into\n ");
+ print_gimple_stmt (dump_file, nstmt, 0, dump_flags);
+ }
+ return true;
+
+ case BUILT_IN_VA_END:
+ /* No effect, so the statement will be deleted. */
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Removed\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ }
+ unlink_stmt_vdef (call);
+ release_defs (call);
+ gsi_replace (gsi, gimple_build_nop (), true);
+ return true;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Fold the non-target builtin at *GSI and return whether any simplification
was made. */
@@ -5270,6 +5391,10 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
switch (fcode)
{
+ case BUILT_IN_VA_START:
+ case BUILT_IN_VA_END:
+ case BUILT_IN_VA_COPY:
+ return gimple_fold_builtin_stdarg (gsi, stmt);
case BUILT_IN_BCMP:
return gimple_fold_builtin_bcmp (gsi);
case BUILT_IN_BCOPY:
@@ -5886,6 +6011,12 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
tree overflow = NULL_TREE;
switch (gimple_call_internal_fn (stmt))
{
+ case IFN_ASSUME:
+ /* Remove .ASSUME calls during the last fold since it is no
+ longer needed. */
+ if (cfun->curr_properties & PROP_last_full_fold)
+ replace_call_with_value (gsi, NULL_TREE);
+ break;
case IFN_BUILTIN_EXPECT:
result = fold_builtin_expect (gimple_location (stmt),
gimple_call_arg (stmt, 0),
diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
index 0d2efcb..b5dc579 100644
--- a/gcc/gimple-isel.cc
+++ b/gcc/gimple-isel.cc
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "optabs.h"
#include "gimple-fold.h"
#include "internal-fn.h"
+#include "fold-const.h"
/* Expand all ARRAY_REF(VIEW_CONVERT_EXPR) gimple assignments into calls to
internal function based on vector type of selected expansion.
@@ -349,6 +350,15 @@ maybe_duplicate_comparison (gassign *stmt, basic_block bb)
}
}
+/* match.pd function to match atomic_bit_test_and pattern which
+ has nop_convert:
+ _1 = __atomic_fetch_or_4 (&v, 1, 0);
+ _2 = (int) _1;
+ _5 = _2 & 1;
+ */
+extern bool gimple_nop_atomic_bit_test_and_p (tree, tree *,
+ tree (*) (tree));
+extern bool gimple_nop_convert (tree, tree*, tree (*) (tree));
namespace {
@@ -382,6 +392,947 @@ public:
}; // class pass_gimple_isel
+
+/* Convert
+ _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
+ _7 = ~_1;
+ _5 = (_Bool) _7;
+ to
+ _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
+ _8 = _1 & 1;
+ _5 = _8 == 0;
+ and convert
+ _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
+ _7 = ~_1;
+ _4 = (_Bool) _7;
+ to
+ _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
+ _8 = _1 & 1;
+ _4 = (_Bool) _8;
+
+ USE_STMT is the gimplt statement which uses the return value of
+ __atomic_fetch_or_*. LHS is the return value of __atomic_fetch_or_*.
+ MASK is the mask passed to __atomic_fetch_or_*.
+ */
+
+static gimple *
+convert_atomic_bit_not (enum internal_fn fn, gimple *use_stmt,
+ tree lhs, tree mask)
+{
+ tree and_mask;
+ if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
+ {
+ /* MASK must be ~1. */
+ if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs),
+ ~HOST_WIDE_INT_1), mask, 0))
+ return nullptr;
+ and_mask = build_int_cst (TREE_TYPE (lhs), 1);
+ }
+ else
+ {
+ /* MASK must be 1. */
+ if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs), 1), mask, 0))
+ return nullptr;
+ and_mask = mask;
+ }
+
+ tree use_lhs = gimple_assign_lhs (use_stmt);
+
+ use_operand_p use_p;
+ gimple *use_not_stmt;
+
+ if (!single_imm_use (use_lhs, &use_p, &use_not_stmt)
+ || !is_gimple_assign (use_not_stmt))
+ return nullptr;
+
+ if (!CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_not_stmt)))
+ return nullptr;
+
+ tree use_not_lhs = gimple_assign_lhs (use_not_stmt);
+ if (TREE_CODE (TREE_TYPE (use_not_lhs)) != BOOLEAN_TYPE)
+ return nullptr;
+
+ gimple_stmt_iterator gsi;
+ tree var = make_ssa_name (TREE_TYPE (lhs));
+ /* use_stmt need to be removed after use_nop_stmt,
+ so use_lhs can be released. */
+ gimple *use_stmt_removal = use_stmt;
+ use_stmt = gimple_build_assign (var, BIT_AND_EXPR, lhs, and_mask);
+ gsi = gsi_for_stmt (use_not_stmt);
+ gsi_insert_before (&gsi, use_stmt, GSI_NEW_STMT);
+ lhs = gimple_assign_lhs (use_not_stmt);
+ gimple *g = gimple_build_assign (lhs, EQ_EXPR, var,
+ build_zero_cst (TREE_TYPE (mask)));
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+ gsi = gsi_for_stmt (use_not_stmt);
+ gsi_remove (&gsi, true);
+ gsi = gsi_for_stmt (use_stmt_removal);
+ gsi_remove (&gsi, true);
+ return use_stmt;
+}
+
+/* Optimize
+ mask_2 = 1 << cnt_1;
+ _4 = __atomic_fetch_or_* (ptr_6, mask_2, _3);
+ _5 = _4 & mask_2;
+ to
+ _4 = .ATOMIC_BIT_TEST_AND_SET (ptr_6, cnt_1, 0, _3);
+ _5 = _4;
+ If _5 is only used in _5 != 0 or _5 == 0 comparisons, 1
+ is passed instead of 0, and the builtin just returns a zero
+ or 1 value instead of the actual bit.
+ Similarly for __sync_fetch_and_or_* (without the ", _3" part
+ in there), and/or if mask_2 is a power of 2 constant.
+ Similarly for xor instead of or, use ATOMIC_BIT_TEST_AND_COMPLEMENT
+ in that case. And similarly for and instead of or, except that
+ the second argument to the builtin needs to be one's complement
+ of the mask instead of mask. */
+
+static bool
+optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip,
+ enum internal_fn fn, bool has_model_arg,
+ bool after)
+{
+ gimple *call = gsi_stmt (*gsip);
+ tree lhs = gimple_call_lhs (call);
+ use_operand_p use_p;
+ gimple *use_stmt;
+ tree mask;
+ optab optab;
+
+ if (!flag_inline_atomics
+ || optimize_debug
+ || !gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+ || !lhs
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)
+ || !single_imm_use (lhs, &use_p, &use_stmt)
+ || !is_gimple_assign (use_stmt)
+ || !gimple_vdef (call))
+ return false;
+
+ switch (fn)
+ {
+ case IFN_ATOMIC_BIT_TEST_AND_SET:
+ optab = atomic_bit_test_and_set_optab;
+ break;
+ case IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT:
+ optab = atomic_bit_test_and_complement_optab;
+ break;
+ case IFN_ATOMIC_BIT_TEST_AND_RESET:
+ optab = atomic_bit_test_and_reset_optab;
+ break;
+ default:
+ return false;
+ }
+
+ tree bit = nullptr;
+
+ mask = gimple_call_arg (call, 1);
+ tree_code rhs_code = gimple_assign_rhs_code (use_stmt);
+ if (rhs_code != BIT_AND_EXPR)
+ {
+ if (rhs_code != NOP_EXPR && rhs_code != BIT_NOT_EXPR)
+ return false;
+
+ tree use_lhs = gimple_assign_lhs (use_stmt);
+ if (TREE_CODE (use_lhs) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs))
+ return false;
+
+ tree use_rhs = gimple_assign_rhs1 (use_stmt);
+ if (lhs != use_rhs)
+ return false;
+
+ if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
+ == CODE_FOR_nothing)
+ return false;
+
+ gimple *g;
+ gimple_stmt_iterator gsi;
+ tree var;
+ int ibit = -1;
+
+ if (rhs_code == BIT_NOT_EXPR)
+ {
+ g = convert_atomic_bit_not (fn, use_stmt, lhs, mask);
+ if (!g)
+ return false;
+ use_stmt = g;
+ ibit = 0;
+ }
+ else if (TREE_CODE (TREE_TYPE (use_lhs)) == BOOLEAN_TYPE)
+ {
+ tree and_mask;
+ if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
+ {
+ /* MASK must be ~1. */
+ if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs),
+ ~HOST_WIDE_INT_1),
+ mask, 0))
+ return false;
+
+ /* Convert
+ _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
+ _4 = (_Bool) _1;
+ to
+ _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
+ _5 = _1 & 1;
+ _4 = (_Bool) _5;
+ */
+ and_mask = build_int_cst (TREE_TYPE (lhs), 1);
+ }
+ else
+ {
+ and_mask = build_int_cst (TREE_TYPE (lhs), 1);
+ if (!operand_equal_p (and_mask, mask, 0))
+ return false;
+
+ /* Convert
+ _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
+ _4 = (_Bool) _1;
+ to
+ _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
+ _5 = _1 & 1;
+ _4 = (_Bool) _5;
+ */
+ }
+ var = make_ssa_name (TREE_TYPE (use_rhs));
+ replace_uses_by (use_rhs, var);
+ g = gimple_build_assign (var, BIT_AND_EXPR, use_rhs,
+ and_mask);
+ gsi = gsi_for_stmt (use_stmt);
+ gsi_insert_before (&gsi, g, GSI_NEW_STMT);
+ use_stmt = g;
+ ibit = 0;
+ }
+ else if (TYPE_PRECISION (TREE_TYPE (use_lhs))
+ <= TYPE_PRECISION (TREE_TYPE (use_rhs)))
+ {
+ gimple *use_nop_stmt;
+ if (!single_imm_use (use_lhs, &use_p, &use_nop_stmt)
+ || (!is_gimple_assign (use_nop_stmt)
+ && gimple_code (use_nop_stmt) != GIMPLE_COND))
+ return false;
+ /* Handle both
+ _4 = _5 < 0;
+ and
+ if (_5 < 0)
+ */
+ tree use_nop_lhs = nullptr;
+ rhs_code = ERROR_MARK;
+ if (is_gimple_assign (use_nop_stmt))
+ {
+ use_nop_lhs = gimple_assign_lhs (use_nop_stmt);
+ rhs_code = gimple_assign_rhs_code (use_nop_stmt);
+ }
+ if (!use_nop_lhs || rhs_code != BIT_AND_EXPR)
+ {
+ /* Also handle
+ if (_5 < 0)
+ */
+ if (use_nop_lhs
+ && TREE_CODE (use_nop_lhs) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_nop_lhs))
+ return false;
+ if (use_nop_lhs && rhs_code == BIT_NOT_EXPR)
+ {
+ /* Handle
+ _7 = ~_2;
+ */
+ g = convert_atomic_bit_not (fn, use_nop_stmt, lhs,
+ mask);
+ if (!g)
+ return false;
+ /* Convert
+ _1 = __atomic_fetch_or_4 (ptr_6, 1, _3);
+ _2 = (int) _1;
+ _7 = ~_2;
+ _5 = (_Bool) _7;
+ to
+ _1 = __atomic_fetch_or_4 (ptr_6, ~1, _3);
+ _8 = _1 & 1;
+ _5 = _8 == 0;
+ and convert
+ _1 = __atomic_fetch_and_4 (ptr_6, ~1, _3);
+ _2 = (int) _1;
+ _7 = ~_2;
+ _5 = (_Bool) _7;
+ to
+ _1 = __atomic_fetch_and_4 (ptr_6, 1, _3);
+ _8 = _1 & 1;
+ _5 = _8 == 0;
+ */
+ gsi = gsi_for_stmt (use_stmt);
+ gsi_remove (&gsi, true);
+ use_stmt = g;
+ ibit = 0;
+ }
+ else
+ {
+ tree cmp_rhs1, cmp_rhs2;
+ if (use_nop_lhs)
+ {
+ /* Handle
+ _4 = _5 < 0;
+ */
+ if (TREE_CODE (TREE_TYPE (use_nop_lhs))
+ != BOOLEAN_TYPE)
+ return false;
+ cmp_rhs1 = gimple_assign_rhs1 (use_nop_stmt);
+ cmp_rhs2 = gimple_assign_rhs2 (use_nop_stmt);
+ }
+ else
+ {
+ /* Handle
+ if (_5 < 0)
+ */
+ rhs_code = gimple_cond_code (use_nop_stmt);
+ cmp_rhs1 = gimple_cond_lhs (use_nop_stmt);
+ cmp_rhs2 = gimple_cond_rhs (use_nop_stmt);
+ }
+ if (rhs_code != GE_EXPR && rhs_code != LT_EXPR)
+ return false;
+ if (use_lhs != cmp_rhs1)
+ return false;
+ if (!integer_zerop (cmp_rhs2))
+ return false;
+
+ tree and_mask;
+
+ unsigned HOST_WIDE_INT bytes
+ = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (use_rhs)));
+ ibit = bytes * BITS_PER_UNIT - 1;
+ unsigned HOST_WIDE_INT highest
+ = HOST_WIDE_INT_1U << ibit;
+
+ if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
+ {
+ /* Get the signed maximum of the USE_RHS type. */
+ and_mask = build_int_cst (TREE_TYPE (use_rhs),
+ highest - 1);
+ if (!operand_equal_p (and_mask, mask, 0))
+ return false;
+
+ /* Convert
+ _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
+ _5 = (signed int) _1;
+ _4 = _5 < 0 or _5 >= 0;
+ to
+ _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
+ _6 = _1 & 0x80000000;
+ _4 = _6 != 0 or _6 == 0;
+ and convert
+ _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
+ _5 = (signed int) _1;
+ if (_5 < 0 or _5 >= 0)
+ to
+ _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
+ _6 = _1 & 0x80000000;
+ if (_6 != 0 or _6 == 0)
+ */
+ and_mask = build_int_cst (TREE_TYPE (use_rhs),
+ highest);
+ }
+ else
+ {
+ /* Get the signed minimum of the USE_RHS type. */
+ and_mask = build_int_cst (TREE_TYPE (use_rhs),
+ highest);
+ if (!operand_equal_p (and_mask, mask, 0))
+ return false;
+
+ /* Convert
+ _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
+ _5 = (signed int) _1;
+ _4 = _5 < 0 or _5 >= 0;
+ to
+ _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
+ _6 = _1 & 0x80000000;
+ _4 = _6 != 0 or _6 == 0;
+ and convert
+ _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
+ _5 = (signed int) _1;
+ if (_5 < 0 or _5 >= 0)
+ to
+ _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
+ _6 = _1 & 0x80000000;
+ if (_6 != 0 or _6 == 0)
+ */
+ }
+ var = make_ssa_name (TREE_TYPE (use_rhs));
+ gimple* use_stmt_removal = use_stmt;
+ g = gimple_build_assign (var, BIT_AND_EXPR, use_rhs,
+ and_mask);
+ gsi = gsi_for_stmt (use_nop_stmt);
+ gsi_insert_before (&gsi, g, GSI_NEW_STMT);
+ use_stmt = g;
+ rhs_code = rhs_code == GE_EXPR ? EQ_EXPR : NE_EXPR;
+ tree const_zero = build_zero_cst (TREE_TYPE (use_rhs));
+ if (use_nop_lhs)
+ g = gimple_build_assign (use_nop_lhs, rhs_code,
+ var, const_zero);
+ else
+ g = gimple_build_cond (rhs_code, var, const_zero,
+ nullptr, nullptr);
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+ gsi = gsi_for_stmt (use_nop_stmt);
+ gsi_remove (&gsi, true);
+ gsi = gsi_for_stmt (use_stmt_removal);
+ gsi_remove (&gsi, true);
+ }
+ }
+ else
+ {
+ tree match_op[3];
+ gimple *g;
+ if (!gimple_nop_atomic_bit_test_and_p (use_nop_lhs,
+ &match_op[0], NULL)
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (match_op[2])
+ || !single_imm_use (match_op[2], &use_p, &g)
+ || !is_gimple_assign (g))
+ return false;
+ mask = match_op[0];
+ if (TREE_CODE (match_op[1]) == INTEGER_CST)
+ {
+ ibit = tree_log2 (match_op[1]);
+ gcc_assert (ibit >= 0);
+ }
+ else
+ {
+ g = SSA_NAME_DEF_STMT (match_op[1]);
+ gcc_assert (is_gimple_assign (g));
+ bit = gimple_assign_rhs2 (g);
+ }
+ /* Convert
+ _1 = __atomic_fetch_or_4 (ptr_6, mask, _3);
+ _2 = (int) _1;
+ _5 = _2 & mask;
+ to
+ _1 = __atomic_fetch_or_4 (ptr_6, mask, _3);
+ _6 = _1 & mask;
+ _5 = (int) _6;
+ and convert
+ _1 = ~mask_7;
+ _2 = (unsigned int) _1;
+ _3 = __atomic_fetch_and_4 (ptr_6, _2, 0);
+ _4 = (int) _3;
+ _5 = _4 & mask_7;
+ to
+ _1 = __atomic_fetch_and_* (ptr_6, ~mask_7, _3);
+ _12 = _3 & mask_7;
+ _5 = (int) _12;
+
+ and Convert
+ _1 = __atomic_fetch_and_4 (ptr_6, ~mask, _3);
+ _2 = (short int) _1;
+ _5 = _2 & mask;
+ to
+ _1 = __atomic_fetch_and_4 (ptr_6, ~mask, _3);
+ _8 = _1 & mask;
+ _5 = (short int) _8;
+ */
+ gimple_seq stmts = NULL;
+ match_op[1] = gimple_convert (&stmts,
+ TREE_TYPE (use_rhs),
+ match_op[1]);
+ var = gimple_build (&stmts, BIT_AND_EXPR,
+ TREE_TYPE (use_rhs), use_rhs, match_op[1]);
+ gsi = gsi_for_stmt (use_stmt);
+ gsi_remove (&gsi, true);
+ release_defs (use_stmt);
+ use_stmt = gimple_seq_last_stmt (stmts);
+ gsi = gsi_for_stmt (use_nop_stmt);
+ gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
+ gimple_assign_set_rhs_with_ops (&gsi, CONVERT_EXPR, var);
+ update_stmt (use_nop_stmt);
+ }
+ }
+ else
+ return false;
+
+ if (!bit)
+ {
+ if (ibit < 0)
+ gcc_unreachable ();
+ bit = build_int_cst (TREE_TYPE (lhs), ibit);
+ }
+ }
+ else if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
+ == CODE_FOR_nothing)
+ return false;
+
+ tree use_lhs = gimple_assign_lhs (use_stmt);
+ if (!use_lhs)
+ return false;
+
+ if (!bit)
+ {
+ if (TREE_CODE (mask) == INTEGER_CST)
+ {
+ if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
+ mask = const_unop (BIT_NOT_EXPR, TREE_TYPE (mask), mask);
+ mask = fold_convert (TREE_TYPE (lhs), mask);
+ int ibit = tree_log2 (mask);
+ if (ibit < 0)
+ return false;
+ bit = build_int_cst (TREE_TYPE (lhs), ibit);
+ }
+ else if (TREE_CODE (mask) == SSA_NAME)
+ {
+ gimple *g = SSA_NAME_DEF_STMT (mask);
+ tree match_op;
+ if (gimple_nop_convert (mask, &match_op, NULL))
+ {
+ mask = match_op;
+ if (TREE_CODE (mask) != SSA_NAME)
+ return false;
+ g = SSA_NAME_DEF_STMT (mask);
+ }
+ if (!is_gimple_assign (g))
+ return false;
+
+ if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
+ {
+ if (gimple_assign_rhs_code (g) != BIT_NOT_EXPR)
+ return false;
+ mask = gimple_assign_rhs1 (g);
+ if (TREE_CODE (mask) != SSA_NAME)
+ return false;
+ g = SSA_NAME_DEF_STMT (mask);
+ }
+
+ if (!is_gimple_assign (g)
+ || gimple_assign_rhs_code (g) != LSHIFT_EXPR
+ || !integer_onep (gimple_assign_rhs1 (g)))
+ return false;
+ bit = gimple_assign_rhs2 (g);
+ }
+ else
+ return false;
+
+ tree cmp_mask;
+ if (gimple_assign_rhs1 (use_stmt) == lhs)
+ cmp_mask = gimple_assign_rhs2 (use_stmt);
+ else
+ cmp_mask = gimple_assign_rhs1 (use_stmt);
+
+ tree match_op;
+ if (gimple_nop_convert (cmp_mask, &match_op, NULL))
+ cmp_mask = match_op;
+
+ if (!operand_equal_p (cmp_mask, mask, 0))
+ return false;
+ }
+
+ bool use_bool = true;
+ bool has_debug_uses = false;
+ imm_use_iterator iter;
+ gimple *g;
+
+ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs))
+ use_bool = false;
+ FOR_EACH_IMM_USE_STMT (g, iter, use_lhs)
+ {
+ enum tree_code code = ERROR_MARK;
+ tree op0 = NULL_TREE, op1 = NULL_TREE;
+ if (is_gimple_debug (g))
+ {
+ has_debug_uses = true;
+ continue;
+ }
+ else if (is_gimple_assign (g))
+ switch (gimple_assign_rhs_code (g))
+ {
+ case COND_EXPR:
+ op1 = gimple_assign_rhs1 (g);
+ code = TREE_CODE (op1);
+ if (TREE_CODE_CLASS (code) != tcc_comparison)
+ break;
+ op0 = TREE_OPERAND (op1, 0);
+ op1 = TREE_OPERAND (op1, 1);
+ break;
+ case EQ_EXPR:
+ case NE_EXPR:
+ code = gimple_assign_rhs_code (g);
+ op0 = gimple_assign_rhs1 (g);
+ op1 = gimple_assign_rhs2 (g);
+ break;
+ default:
+ break;
+ }
+ else if (gimple_code (g) == GIMPLE_COND)
+ {
+ code = gimple_cond_code (g);
+ op0 = gimple_cond_lhs (g);
+ op1 = gimple_cond_rhs (g);
+ }
+
+ if ((code == EQ_EXPR || code == NE_EXPR)
+ && op0 == use_lhs
+ && integer_zerop (op1))
+ {
+ use_operand_p use_p;
+ int n = 0;
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ n++;
+ if (n == 1)
+ continue;
+ }
+
+ use_bool = false;
+ break;
+ }
+
+ tree new_lhs = make_ssa_name (TREE_TYPE (lhs));
+ tree flag = build_int_cst (TREE_TYPE (lhs), use_bool);
+ if (has_model_arg)
+ g = gimple_build_call_internal (fn, 5, gimple_call_arg (call, 0),
+ bit, flag, gimple_call_arg (call, 2),
+ gimple_call_fn (call));
+ else
+ g = gimple_build_call_internal (fn, 4, gimple_call_arg (call, 0),
+ bit, flag, gimple_call_fn (call));
+ gimple_call_set_lhs (g, new_lhs);
+ gimple_set_location (g, gimple_location (call));
+ gimple_move_vops (g, call);
+ bool throws = stmt_can_throw_internal (cfun, call);
+ gimple_call_set_nothrow (as_a <gcall *> (g),
+ gimple_call_nothrow_p (as_a <gcall *> (call)));
+ gimple_stmt_iterator gsi = *gsip;
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+ edge e = NULL;
+ if (throws)
+ {
+ maybe_clean_or_replace_eh_stmt (call, g);
+ if (after || (use_bool && has_debug_uses))
+ e = find_fallthru_edge (gsi_bb (gsi)->succs);
+ }
+ if (after)
+ {
+ /* The internal function returns the value of the specified bit
+ before the atomic operation. If we are interested in the value
+ of the specified bit after the atomic operation (makes only sense
+ for xor, otherwise the bit content is compile time known),
+ we need to invert the bit. */
+ tree mask_convert = mask;
+ gimple_seq stmts = NULL;
+ if (!use_bool)
+ mask_convert = gimple_convert (&stmts, TREE_TYPE (lhs), mask);
+ new_lhs = gimple_build (&stmts, BIT_XOR_EXPR, TREE_TYPE (lhs), new_lhs,
+ use_bool ? build_int_cst (TREE_TYPE (lhs), 1)
+ : mask_convert);
+ if (throws)
+ {
+ gsi_insert_seq_on_edge_immediate (e, stmts);
+ gsi = gsi_for_stmt (gimple_seq_last (stmts));
+ }
+ else
+ gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
+ }
+ if (use_bool && has_debug_uses)
+ {
+ tree temp = NULL_TREE;
+ if (!throws || after || single_pred_p (e->dest))
+ {
+ temp = build_debug_expr_decl (TREE_TYPE (lhs));
+ tree t = build2 (LSHIFT_EXPR, TREE_TYPE (lhs), new_lhs, bit);
+ g = gimple_build_debug_bind (temp, t, g);
+ if (throws && !after)
+ {
+ gsi = gsi_after_labels (e->dest);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ }
+ else
+ gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+ }
+ FOR_EACH_IMM_USE_STMT (g, iter, use_lhs)
+ if (is_gimple_debug (g))
+ {
+ use_operand_p use_p;
+ if (temp == NULL_TREE)
+ gimple_debug_bind_reset_value (g);
+ else
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ SET_USE (use_p, temp);
+ update_stmt (g);
+ }
+ }
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_lhs)
+ = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs);
+ replace_uses_by (use_lhs, new_lhs);
+ gsi = gsi_for_stmt (use_stmt);
+ gsi_remove (&gsi, true);
+ release_defs (use_stmt);
+ gsi_remove (gsip, true);
+ release_ssa_name (lhs);
+ return true;
+}
+
+/* Optimize
+ _4 = __atomic_add_fetch_* (ptr_6, arg_2, _3);
+ _5 = _4 == 0;
+ to
+ _4 = .ATOMIC_ADD_FETCH_CMP_0 (EQ_EXPR, ptr_6, arg_2, _3);
+ _5 = _4;
+ Similarly for __sync_add_and_fetch_* (without the ", _3" part
+ in there). */
+
+static bool
+optimize_atomic_op_fetch_cmp_0 (gimple_stmt_iterator *gsip,
+ enum internal_fn fn, bool has_model_arg)
+{
+ gimple *call = gsi_stmt (*gsip);
+ tree lhs = gimple_call_lhs (call);
+ use_operand_p use_p;
+ gimple *use_stmt;
+
+ if (!flag_inline_atomics
+ || !gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+ || !lhs
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)
+ || !single_imm_use (lhs, &use_p, &use_stmt)
+ || !gimple_vdef (call))
+ return false;
+
+ optab optab;
+ switch (fn)
+ {
+ case IFN_ATOMIC_ADD_FETCH_CMP_0:
+ optab = atomic_add_fetch_cmp_0_optab;
+ break;
+ case IFN_ATOMIC_SUB_FETCH_CMP_0:
+ optab = atomic_sub_fetch_cmp_0_optab;
+ break;
+ case IFN_ATOMIC_AND_FETCH_CMP_0:
+ optab = atomic_and_fetch_cmp_0_optab;
+ break;
+ case IFN_ATOMIC_OR_FETCH_CMP_0:
+ optab = atomic_or_fetch_cmp_0_optab;
+ break;
+ case IFN_ATOMIC_XOR_FETCH_CMP_0:
+ optab = atomic_xor_fetch_cmp_0_optab;
+ break;
+ default:
+ return false;
+ }
+
+ if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
+ == CODE_FOR_nothing)
+ return false;
+
+ tree use_lhs = lhs;
+ if (gimple_assign_cast_p (use_stmt))
+ {
+ use_lhs = gimple_assign_lhs (use_stmt);
+ if (!tree_nop_conversion_p (TREE_TYPE (use_lhs), TREE_TYPE (lhs))
+ || (!INTEGRAL_TYPE_P (TREE_TYPE (use_lhs))
+ && !POINTER_TYPE_P (TREE_TYPE (use_lhs)))
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs)
+ || !single_imm_use (use_lhs, &use_p, &use_stmt))
+ return false;
+ }
+ enum tree_code code = ERROR_MARK;
+ tree op0 = NULL_TREE, op1 = NULL_TREE;
+ if (is_gimple_assign (use_stmt))
+ switch (gimple_assign_rhs_code (use_stmt))
+ {
+ case COND_EXPR:
+ op1 = gimple_assign_rhs1 (use_stmt);
+ code = TREE_CODE (op1);
+ if (TREE_CODE_CLASS (code) == tcc_comparison)
+ {
+ op0 = TREE_OPERAND (op1, 0);
+ op1 = TREE_OPERAND (op1, 1);
+ }
+ break;
+ default:
+ code = gimple_assign_rhs_code (use_stmt);
+ if (TREE_CODE_CLASS (code) == tcc_comparison)
+ {
+ op0 = gimple_assign_rhs1 (use_stmt);
+ op1 = gimple_assign_rhs2 (use_stmt);
+ }
+ break;
+ }
+ else if (gimple_code (use_stmt) == GIMPLE_COND)
+ {
+ code = gimple_cond_code (use_stmt);
+ op0 = gimple_cond_lhs (use_stmt);
+ op1 = gimple_cond_rhs (use_stmt);
+ }
+
+ switch (code)
+ {
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (use_lhs))
+ || TREE_CODE (TREE_TYPE (use_lhs)) == BOOLEAN_TYPE
+ || TYPE_UNSIGNED (TREE_TYPE (use_lhs)))
+ return false;
+ /* FALLTHRU */
+ case EQ_EXPR:
+ case NE_EXPR:
+ if (op0 == use_lhs && integer_zerop (op1))
+ break;
+ return false;
+ default:
+ return false;
+ }
+
+ int encoded;
+ switch (code)
+ {
+ /* Use special encoding of the operation. We want to also
+ encode the mode in the first argument and for neither EQ_EXPR
+ etc. nor EQ etc. we can rely it will fit into QImode. */
+ case EQ_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_EQ; break;
+ case NE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_NE; break;
+ case LT_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_LT; break;
+ case LE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_LE; break;
+ case GT_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_GT; break;
+ case GE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_GE; break;
+ default: gcc_unreachable ();
+ }
+
+ tree new_lhs = make_ssa_name (boolean_type_node);
+ gimple *g;
+ tree flag = build_int_cst (TREE_TYPE (lhs), encoded);
+ if (has_model_arg)
+ g = gimple_build_call_internal (fn, 5, flag,
+ gimple_call_arg (call, 0),
+ gimple_call_arg (call, 1),
+ gimple_call_arg (call, 2),
+ gimple_call_fn (call));
+ else
+ g = gimple_build_call_internal (fn, 4, flag,
+ gimple_call_arg (call, 0),
+ gimple_call_arg (call, 1),
+ gimple_call_fn (call));
+ gimple_call_set_lhs (g, new_lhs);
+ gimple_set_location (g, gimple_location (call));
+ gimple_move_vops (g, call);
+ bool throws = stmt_can_throw_internal (cfun, call);
+ gimple_call_set_nothrow (as_a <gcall *> (g),
+ gimple_call_nothrow_p (as_a <gcall *> (call)));
+ gimple_stmt_iterator gsi = *gsip;
+ gsi_insert_after (&gsi, g, GSI_SAME_STMT);
+ if (throws)
+ maybe_clean_or_replace_eh_stmt (call, g);
+ if (is_gimple_assign (use_stmt))
+ switch (gimple_assign_rhs_code (use_stmt))
+ {
+ case COND_EXPR:
+ gimple_assign_set_rhs1 (use_stmt, new_lhs);
+ break;
+ default:
+ gsi = gsi_for_stmt (use_stmt);
+ if (tree ulhs = gimple_assign_lhs (use_stmt))
+ if (useless_type_conversion_p (TREE_TYPE (ulhs),
+ boolean_type_node))
+ {
+ gimple_assign_set_rhs_with_ops (&gsi, SSA_NAME, new_lhs);
+ break;
+ }
+ gimple_assign_set_rhs_with_ops (&gsi, NOP_EXPR, new_lhs);
+ break;
+ }
+ else if (gimple_code (use_stmt) == GIMPLE_COND)
+ {
+ gcond *use_cond = as_a <gcond *> (use_stmt);
+ gimple_cond_set_code (use_cond, NE_EXPR);
+ gimple_cond_set_lhs (use_cond, new_lhs);
+ gimple_cond_set_rhs (use_cond, boolean_false_node);
+ }
+
+ update_stmt (use_stmt);
+ if (use_lhs != lhs)
+ {
+ gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (use_lhs));
+ gsi_remove (&gsi, true);
+ release_ssa_name (use_lhs);
+ }
+ gsi_remove (gsip, true);
+ release_ssa_name (lhs);
+ return true;
+}
+
+/* Process builtin CALL located at GSI.
+ Currently it is only fgr atomic functions optimizations from above. */
+static void
+gimple_isel_builtin_call (gcall *call, gimple_stmt_iterator *gsi)
+{
+ /* Don't handle these in non optimization mode or optimize debug mode. */
+ if (!optimize || optimize_debug)
+ return;
+
+ if (!gimple_call_builtin_p (call, BUILT_IN_NORMAL))
+ return;
+
+ tree callee = gimple_call_fndecl (call);
+
+ switch (DECL_FUNCTION_CODE (callee))
+ {
+#define CASE_ATOMIC(NAME) \
+ case BUILT_IN_##NAME##_1: \
+ case BUILT_IN_##NAME##_2: \
+ case BUILT_IN_##NAME##_4: \
+ case BUILT_IN_##NAME##_8: \
+ case BUILT_IN_##NAME##_16
+#define CASE_ATOMIC_CMP0(ATOMIC, SYNC) \
+ CASE_ATOMIC(ATOMIC_##ATOMIC): \
+ optimize_atomic_op_fetch_cmp_0 (gsi, \
+ IFN_ATOMIC_##ATOMIC##_CMP_0, \
+ true); \
+ break; \
+ CASE_ATOMIC(SYNC_##SYNC): \
+ optimize_atomic_op_fetch_cmp_0 (gsi, \
+ IFN_ATOMIC_##ATOMIC##_CMP_0, \
+ false); \
+ break;
+
+
+ CASE_ATOMIC_CMP0(ADD_FETCH, ADD_AND_FETCH)
+ CASE_ATOMIC_CMP0(SUB_FETCH, SUB_AND_FETCH)
+ CASE_ATOMIC_CMP0(AND_FETCH, AND_AND_FETCH)
+ CASE_ATOMIC_CMP0(OR_FETCH, OR_AND_FETCH)
+#define CASE_ATOMIC_BIT_TEST_AND(ATOMIC, SYNC, FN, AFTER) \
+ CASE_ATOMIC(ATOMIC_##ATOMIC): \
+ optimize_atomic_bit_test_and (gsi, \
+ IFN_ATOMIC_BIT_TEST_AND_##FN, \
+ true, AFTER); \
+ break; \
+ CASE_ATOMIC(SYNC_##SYNC): \
+ optimize_atomic_bit_test_and (gsi, \
+ IFN_ATOMIC_BIT_TEST_AND_##FN, \
+ false, AFTER); \
+ break;
+ CASE_ATOMIC_BIT_TEST_AND(FETCH_OR, FETCH_AND_OR, SET, false)
+ CASE_ATOMIC_BIT_TEST_AND(FETCH_XOR, FETCH_AND_XOR, COMPLEMENT, false)
+ CASE_ATOMIC_BIT_TEST_AND(FETCH_AND, FETCH_AND_AND, RESET, false)
+
+ CASE_ATOMIC(ATOMIC_XOR_FETCH):
+ if (optimize_atomic_bit_test_and
+ (gsi, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, true, true))
+ break;
+ optimize_atomic_op_fetch_cmp_0 (gsi,
+ IFN_ATOMIC_XOR_FETCH_CMP_0,
+ true);
+ break;
+ CASE_ATOMIC(SYNC_XOR_AND_FETCH):
+ if (optimize_atomic_bit_test_and
+ (gsi, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, false, true))
+ break;
+ optimize_atomic_op_fetch_cmp_0 (gsi,
+ IFN_ATOMIC_XOR_FETCH_CMP_0,
+ false);
+ break;
+
+ default:;
+ }
+}
+
/* Iterate all gimple statements and perform pre RTL expansion
GIMPLE massaging to improve instruction selection. */
@@ -411,6 +1362,11 @@ pass_gimple_isel::execute (struct function *fun)
if (gsi_end_p (gsi))
break;
+ if (gcall *call = dyn_cast <gcall*>(*gsi))
+ {
+ gimple_isel_builtin_call (call, &gsi);
+ continue;
+ }
gassign *stmt = dyn_cast <gassign *> (*gsi);
if (!stmt)
continue;
diff --git a/gcc/json.cc b/gcc/json.cc
index 7153f08..14ff76b 100644
--- a/gcc/json.cc
+++ b/gcc/json.cc
@@ -394,6 +394,31 @@ object::set_bool (const char *key, bool v)
set (key, new json::literal (v));
}
+void
+object::set_string (const string_property &property, const char *utf8_value)
+{
+ set_string (property.m_key.get (), utf8_value);
+}
+
+void
+object::set_integer (const integer_property &property, long value)
+{
+ set_integer (property.m_key.get (), value);
+}
+
+void
+object::set_bool (const bool_property &property, bool value)
+{
+ set_bool (property.m_key.get (), value);
+}
+
+void
+object::set_array_of_string (const array_of_string_property &property,
+ std::unique_ptr<json::array> value)
+{
+ set<array> (property.m_key.get (), std::move (value));
+}
+
/* Subroutine of json::compare for comparing a pairs of objects. */
int
diff --git a/gcc/json.h b/gcc/json.h
index c706f2a..c53715e 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_JSON_H
#define GCC_JSON_H
+#include "label-text.h"
+
/* Implementation of JSON, a lightweight data-interchange format.
See http://www.json.org/
@@ -116,6 +118,41 @@ struct token
} // namespace json::pointer
+/* Typesafe way to work with properties in JSON objects. */
+
+template <typename Traits>
+struct property
+{
+ explicit property (const char *key)
+ : m_key (label_text::borrow (key))
+ {}
+
+ explicit property (const char *key_prefix, const char *key)
+ : m_key (label_text::take (concat (key_prefix, key, nullptr)))
+ {}
+
+ label_text m_key;
+};
+
+using string_property = property<string>;
+using integer_property = property<integer_number>;
+using bool_property = property<literal>;
+using json_property = property<value>;
+using array_of_string_property = property<array>;
+
+template <typename EnumType>
+struct enum_traits
+{
+ typedef EnumType enum_t;
+
+ static enum_t get_unknown_value ();
+ static bool maybe_get_value_from_string (const char *, enum_t &out);
+ static const char *get_string_for_value (enum_t value);
+};
+
+template <typename EnumType>
+using enum_property = property<enum_traits<EnumType>>;
+
/* Base class of JSON value. */
class value
@@ -130,6 +167,8 @@ class value
void DEBUG_FUNCTION dump () const;
virtual object *dyn_cast_object () { return nullptr; }
+ virtual array *dyn_cast_array () { return nullptr; }
+ virtual integer_number *dyn_cast_integer_number () { return nullptr; }
virtual string *dyn_cast_string () { return nullptr; }
static int compare (const json::value &val_a, const json::value &val_b);
@@ -183,6 +222,19 @@ class object : public value
/* Set to literal true/false. */
void set_bool (const char *key, bool v);
+ /* Typesafe access to properties by name (such as from a schema). */
+ void set_string (const string_property &property, const char *utf8_value);
+ void set_integer (const integer_property &property, long value);
+ void set_bool (const bool_property &property, bool value);
+ void set_array_of_string (const array_of_string_property &property,
+ std::unique_ptr<json::array> value);
+ template <typename EnumType>
+ bool maybe_get_enum (const enum_property<EnumType> &property,
+ EnumType &out) const;
+ template <typename EnumType>
+ void set_enum (const enum_property<EnumType> &property,
+ EnumType value);
+
static int compare (const json::object &obj_a, const json::object &obj_b);
size_t get_num_keys () const { return m_keys.length (); }
@@ -210,6 +262,8 @@ class array : public value
void print (pretty_printer *pp, bool formatted) const final override;
std::unique_ptr<value> clone () const final override;
+ array *dyn_cast_array () final override { return this; }
+
void append (value *v);
void append_string (const char *utf8_value);
@@ -269,6 +323,8 @@ class integer_number : public value
void print (pretty_printer *pp, bool formatted) const final override;
std::unique_ptr<value> clone () const final override;
+ integer_number *dyn_cast_integer_number () final override { return this; }
+
long get () const { return m_value; }
private:
@@ -317,6 +373,32 @@ class literal : public value
enum kind m_kind;
};
+
+template <typename EnumType>
+inline bool
+object::maybe_get_enum (const enum_property<EnumType> &property,
+ EnumType &out) const
+{
+ if (value *jv = get (property.m_key.get ()))
+ if (string *jstr = jv->dyn_cast_string ())
+ {
+ if (enum_traits<EnumType>::maybe_get_value_from_string
+ (jstr->get_string (), out))
+ return true;
+ }
+ return false;
+}
+
+template <typename EnumType>
+inline void
+object::set_enum (const enum_property<EnumType> &property,
+ EnumType value)
+{
+ const char *str
+ = json::enum_traits<EnumType>::get_string_for_value (value);
+ set_string (property.m_key.get (), str);
+}
+
} // namespace json
template <>
diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc
index 6b269a1..46714ff 100644
--- a/gcc/libgdiagnostics.cc
+++ b/gcc/libgdiagnostics.cc
@@ -2484,7 +2484,8 @@ public:
const char *unparsed_spec,
diagnostic_manager &affected_mgr,
diagnostic_manager &control_mgr)
- : context (option_name, unparsed_spec, affected_mgr.get_line_table ()),
+ : context (option_name, unparsed_spec, nullptr,
+ affected_mgr.get_line_table ()),
m_control_mgr (control_mgr)
{}
diff --git a/gcc/m2/gm2-compiler/M2GCCDeclare.mod b/gcc/m2/gm2-compiler/M2GCCDeclare.mod
index 710976e..24634fd 100644
--- a/gcc/m2/gm2-compiler/M2GCCDeclare.mod
+++ b/gcc/m2/gm2-compiler/M2GCCDeclare.mod
@@ -4447,19 +4447,6 @@ END PrintString ;
(*
- PrintKnown -
-*)
-
-PROCEDURE PrintKnown (sym: CARDINAL) ;
-BEGIN
- IF GccKnowsAbout (sym)
- THEN
- printf0 ("[gcc]")
- END
-END PrintKnown ;
-
-
-(*
PrintVerboseFromList - prints the, i, th element in the list, l.
*)
diff --git a/gcc/m2/gm2-gcc/m2type.cc b/gcc/m2/gm2-gcc/m2type.cc
index 535ab14..184b506 100644
--- a/gcc/m2/gm2-gcc/m2type.cc
+++ b/gcc/m2/gm2-gcc/m2type.cc
@@ -2213,7 +2213,7 @@ gm2_build_enumerator (location_t location, tree name, tree value)
enumvalues, list. It returns a copy of the value. */
tree
-m2type_BuildEnumerator (location_t location, char *name, tree value,
+m2type_BuildEnumerator (location_t location, const char *name, tree value,
tree *enumvalues)
{
tree id = get_identifier (name);
diff --git a/gcc/m2/gm2-gcc/m2type.def b/gcc/m2/gm2-gcc/m2type.def
index 8a72652..6f64c98 100644
--- a/gcc/m2/gm2-gcc/m2type.def
+++ b/gcc/m2/gm2-gcc/m2type.def
@@ -173,7 +173,7 @@ PROCEDURE BuildPointerType (totype: tree) : tree ;
It returns a copy of the value. --fixme-- why do this?
*)
-PROCEDURE BuildEnumerator (location: location_t; name: CharStar; value: tree;
+PROCEDURE BuildEnumerator (location: location_t; name: ConstCharStar; value: tree;
VAR enumvalues: tree) : tree ;
diff --git a/gcc/m2/gm2-gcc/m2type.h b/gcc/m2/gm2-gcc/m2type.h
index afd97f7..68015a0 100644
--- a/gcc/m2/gm2-gcc/m2type.h
+++ b/gcc/m2/gm2-gcc/m2type.h
@@ -183,7 +183,7 @@ EXTERN tree m2type_BuildStartEnumeration (location_t location, char *name,
bool ispacked);
EXTERN tree m2type_BuildEndEnumeration (location_t location, tree enumtype,
tree enumvalues);
-EXTERN tree m2type_BuildEnumerator (location_t location, char *name,
+EXTERN tree m2type_BuildEnumerator (location_t location, const char *name,
tree value, tree *enumvalues);
EXTERN tree m2type_BuildPointerType (tree totype);
EXTERN tree m2type_BuildConstPointerType (tree totype);
diff --git a/gcc/m2/gm2-libs/M2WIDESET.mod b/gcc/m2/gm2-libs/M2WIDESET.mod
index f1b1bed..93df428 100644
--- a/gcc/m2/gm2-libs/M2WIDESET.mod
+++ b/gcc/m2/gm2-libs/M2WIDESET.mod
@@ -490,21 +490,21 @@ PROCEDURE ShiftLeftByteBit (VAR dest: ARRAY OF BYTE; src: ARRAY OF BYTE;
highbit: CARDINAL;
byteshift, bitshift: CARDINAL) ;
VAR
- top, bot, mid : BYTESET ;
- i, h, from, to: CARDINAL ;
+ top, bot, mid : BYTESET ;
+ i, h, fromIdx, toIdx: CARDINAL ;
BEGIN
(* Copy the bytes into dest at the mostly correct position
(modulo byte position). *)
- to := 0 ;
- from := 0 ;
- WHILE to < byteshift DO
- dest[to] := BYTE (0) ;
- INC (to)
+ toIdx := 0 ;
+ fromIdx := 0 ;
+ WHILE toIdx < byteshift DO
+ dest[toIdx] := BYTE (0) ;
+ INC (toIdx)
END ;
- WHILE to <= HIGH (dest) DO
- dest[to] := src[from] ;
- INC (to) ;
- INC (from)
+ WHILE toIdx <= HIGH (dest) DO
+ dest[toIdx] := src[fromIdx] ;
+ INC (toIdx) ;
+ INC (fromIdx)
END ;
(* And adjust by bit shifting. *)
IF bitshift > 0
@@ -567,12 +567,12 @@ PROCEDURE ShiftRightByteBit (VAR dest: ARRAY OF BYTE; src: ARRAY OF BYTE;
highbit: CARDINAL;
byteshift, bitshift: CARDINAL) ;
VAR
- top, bot, mid : BYTESET ;
- i, h, to, from: CARDINAL ;
+ top, bot, mid : BYTESET ;
+ i, h, toIdx, fromIdx: CARDINAL ;
BEGIN
(* Copy the bytes. *)
- to := 0 ;
- from := byteshift ;
+ toIdx := 0 ;
+ fromIdx := byteshift ;
IF EnableDebugging
THEN
printf ("HIGH (dest) = %d\n", HIGH (dest))
@@ -580,15 +580,15 @@ BEGIN
IF byteshift <= HIGH (dest)
THEN
h := HIGH (dest) - byteshift ;
- WHILE to <= h DO
- dest[to] := src[from] ;
- INC (to) ;
- INC (from)
+ WHILE toIdx <= h DO
+ dest[toIdx] := src[fromIdx] ;
+ INC (toIdx) ;
+ INC (fromIdx)
END
END ;
- WHILE to <= HIGH (dest) DO
- dest[to] := BYTE (0) ;
- INC (to)
+ WHILE toIdx <= HIGH (dest) DO
+ dest[toIdx] := BYTE (0) ;
+ INC (toIdx)
END ;
(* And bit shift the remainder. *)
IF EnableDebugging
@@ -691,7 +691,7 @@ VAR
next : BOOLEAN ;
mask,
unused,
- set : BYTESET ;
+ setb : BYTESET ;
BEGIN
IF EnableDebugging
THEN
@@ -704,14 +704,14 @@ BEGIN
bytes. *)
i := 0 ;
WHILE i < high DO
- set := dest[i] ;
- next := MSB IN set ;
- set := SHIFT (set, 1) ; (* Shift left. *)
+ setb := dest[i] ;
+ next := MSB IN setb ;
+ setb := SHIFT (setb, 1) ; (* Shift left. *)
IF carry
THEN
- INCL (set, 0) (* Set bit 0. *)
+ INCL (setb, 0) (* Set bit 0. *)
END ;
- dest[i] := set ;
+ dest[i] := setb ;
carry := next ;
IF EnableDebugging
THEN
@@ -722,27 +722,27 @@ BEGIN
END ;
(* Last byte special case as there may be some unused bits which must be
preserved. *)
- set := dest[high] ;
+ setb := dest[high] ;
unused := BYTESET {} ; (* Will contain all top unused bits of dest[high]. *)
mask := - BYTESET {} ;
topbit := (highbit+1) MOD TBITSIZE (BYTE) ;
WHILE topbit # 0 DO
EXCL (mask, topbit) ;
- IF topbit IN set
+ IF topbit IN setb
THEN
- EXCL (set, topbit) ;
+ EXCL (setb, topbit) ;
INCL (unused, topbit)
END ;
topbit := (topbit+1) MOD TBITSIZE (BYTE)
END ;
- set := SHIFT (set, 1) ; (* Left shift. *)
+ setb := SHIFT (setb, 1) ; (* Left shift. *)
IF carry
THEN
- INCL (set, 0) (* Set bit 0. *)
+ INCL (setb, 0) (* Set bit 0. *)
END ;
- set := set * mask ; (* Remove all unused bits. *)
- set := set + unused ; (* Restore original unused bits. *)
- dest[high] := set ;
+ setb := setb * mask ; (* Remove all unused bits. *)
+ setb := setb + unused ; (* Restore original unused bits. *)
+ dest[high] := setb ;
IF EnableDebugging
THEN
printf ("ArithShiftLeft shifted byte dest[%d]\n", high);
@@ -785,32 +785,32 @@ VAR
next : BOOLEAN ;
mask,
unused,
- set : BYTESET ;
+ setb : BYTESET ;
BEGIN
high := HIGH (dest) ;
(* Clear any unused bits in the highest byte, but save them into unused. *)
- set := dest[high] ;
+ setb := dest[high] ;
unused := BYTESET {} ;
topbit := (highbit+1) MOD TBITSIZE (BYTE) ;
mask := - BYTESET {} ;
WHILE topbit # 0 DO
EXCL (mask, topbit) ;
- IF topbit IN set
+ IF topbit IN setb
THEN
- EXCL (set, topbit) ;
+ EXCL (setb, topbit) ;
INCL (unused, topbit)
END ;
topbit := (topbit+1) MOD TBITSIZE (BYTE)
END ;
(* Start at the top and work down to byte 0. *)
- set := set * mask ; (* Ignore unused bits. *)
- next := 0 IN set ; (* Next carry. *)
- set := SHIFT (set, -1) ; (* Shift right by 1 bit. *)
+ setb := setb * mask ; (* Ignore unused bits. *)
+ next := 0 IN setb ; (* Next carry. *)
+ setb := SHIFT (setb, -1) ; (* Shift right by 1 bit. *)
IF carry
THEN
- INCL (set, highbit MOD TBITSIZE (BYTE))
+ INCL (setb, highbit MOD TBITSIZE (BYTE))
END ;
- dest[high] := set + unused ; (* First byte is a special case as we
+ dest[high] := setb + unused ; (* First byte is a special case as we
have to preserve the unused bits. *)
(* Now we ripple through the remaining bytes, propagating local
carry between bytes. *)
@@ -818,14 +818,14 @@ BEGIN
WHILE i > 0 DO
prev := next ;
DEC (i) ;
- set := dest[i] ;
- next := 0 IN set ;
- set := SHIFT (set, -1) ;
+ setb := dest[i] ;
+ next := 0 IN setb ;
+ setb := SHIFT (setb, -1) ;
IF prev
THEN
- INCL (set, MSB)
+ INCL (setb, MSB)
END ;
- dest[i] := set
+ dest[i] := setb
END
END ArithShiftRightBit ;
@@ -914,7 +914,7 @@ VAR
high,
highplus1,
highbitplus1,
- from, to : CARDINAL ;
+ fromIdx, toIdx: CARDINAL ;
BEGIN
IF EnableDebugging
THEN
@@ -925,21 +925,21 @@ BEGIN
(* Copy the contents rotating on byte granularity, then
arithmetically shift the remaining number of bits. *)
high := HIGH (dest) ;
- from := 0 ;
+ fromIdx := 0 ;
highplus1 := high + 1 ;
highbitplus1 := highbit + 1 ;
- to := RotateCount DIV TBITSIZE (BYTE) ; (* Byte level granularity. *)
+ toIdx := RotateCount DIV TBITSIZE (BYTE) ; (* Byte level granularity. *)
REPEAT
- dest[to] := src[from] ;
+ dest[toIdx] := src[fromIdx] ;
IF EnableDebugging
THEN
printf ("RotateLeft after partial byte movement: dest[%d] := src[%d]\n",
- to, from);
+ toIdx, fromIdx);
DumpSet (dest, highbit)
END ;
- from := (from + 1) MOD highplus1 ;
- to := (to + 1) MOD highplus1 ;
- UNTIL from = 0 ;
+ fromIdx := (fromIdx + 1) MOD highplus1 ;
+ toIdx := (toIdx + 1) MOD highplus1 ;
+ UNTIL fromIdx = 0 ;
IF EnableDebugging
THEN
diff --git a/gcc/opts-diagnostic.cc b/gcc/opts-diagnostic.cc
index 6f459ec..a230c21 100644
--- a/gcc/opts-diagnostic.cc
+++ b/gcc/opts-diagnostic.cc
@@ -49,12 +49,13 @@ public:
location_t loc,
const char *option_name,
const char *option_value)
- : dc_spec_context (dc,
+ : dc_spec_context (option_name,
+ option_value,
+ nullptr,
location_mgr,
+ dc,
location_mgr,
- loc,
- option_name,
- option_value),
+ loc),
m_opts (opts)
{}
diff --git a/gcc/passes.def b/gcc/passes.def
index 3f82847..fac04cd 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -369,7 +369,6 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_forwprop, /*full_walk=*/false, /*last=*/true);
NEXT_PASS (pass_sink_code, true /* unsplit edges */);
NEXT_PASS (pass_phiopt, false /* early_p */);
- NEXT_PASS (pass_fold_builtins);
NEXT_PASS (pass_optimize_widening_mul);
NEXT_PASS (pass_store_merging);
/* If DCE is not run before checking for uninitialized uses,
@@ -405,12 +404,9 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_ccp, true /* nonzero_p */);
NEXT_PASS (pass_post_ipa_warn);
NEXT_PASS (pass_object_sizes);
- /* Fold remaining builtins. */
- NEXT_PASS (pass_fold_builtins);
NEXT_PASS (pass_strlen);
- /* Copy propagation also copy-propagates constants, this is necessary
- to forward object-size and builtin folding results properly. */
- NEXT_PASS (pass_copy_prop);
+ /* Fold remaining builtins. */
+ NEXT_PASS (pass_forwprop, /*full_walk=*/false, /*last=*/true);
NEXT_PASS (pass_dce);
/* Profile count may overflow as a result of inlinining very large
loop nests. This pass should run before any late pass that makes
diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 46f1df6..c4de035 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -3465,7 +3465,9 @@ simplify_context::simplify_binary_operation_1 (rtx_code code,
return plus_constant (mode, op0, trunc_int_for_mode (-offset, mode));
/* Don't let a relocatable value get a negative coeff. */
- if (poly_int_rtx_p (op1) && GET_MODE (op0) != VOIDmode)
+ if (is_a <scalar_int_mode> (mode)
+ && poly_int_rtx_p (op1)
+ && GET_MODE (op0) != VOIDmode)
return simplify_gen_binary (PLUS, mode,
op0,
neg_poly_int_rtx (mode, op1));
diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-5.c b/gcc/testsuite/gcc.dg/builtin-unreachable-5.c
index ba87bdd..91e6dcc 100644
--- a/gcc/testsuite/gcc.dg/builtin-unreachable-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-unreachable-5.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
int
foo (int a)
@@ -16,7 +16,7 @@ foo (int a)
return a > 0;
}
-/* { dg-final { scan-tree-dump-times "if \\(" 0 "fab1" } } */
-/* { dg-final { scan-tree-dump-times "goto" 0 "fab1" } } */
-/* { dg-final { scan-tree-dump-times "L1:" 0 "fab1" } } */
-/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 0 "fab1" } } */
+/* { dg-final { scan-tree-dump-times "if \\(" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "goto" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "L1:" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-6.c b/gcc/testsuite/gcc.dg/builtin-unreachable-6.c
index 4c3b9bb..c896ad4 100644
--- a/gcc/testsuite/gcc.dg/builtin-unreachable-6.c
+++ b/gcc/testsuite/gcc.dg/builtin-unreachable-6.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1 -fno-tree-dominator-opts -fno-tree-vrp" } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-tree-dominator-opts -fno-tree-vrp" } */
/* { dg-require-effective-target label_values } */
void
@@ -17,5 +17,5 @@ lab2:
goto *x;
}
-/* { dg-final { scan-tree-dump-times "lab:" 1 "fab1" } } */
-/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 1 "fab1" } } */
+/* { dg-final { scan-tree-dump-times "lab:" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-6a.c b/gcc/testsuite/gcc.dg/builtin-unreachable-6a.c
index f527f2e..5306235 100644
--- a/gcc/testsuite/gcc.dg/builtin-unreachable-6a.c
+++ b/gcc/testsuite/gcc.dg/builtin-unreachable-6a.c
@@ -1,7 +1,7 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
#include "builtin-unreachable-6.c"
-/* { dg-final { scan-tree-dump-times "lab:" 1 "fab1" } } */
-/* { dg-final { scan-tree-dump-not "__builtin_unreachable" "fab1" } } */
+/* { dg-final { scan-tree-dump-times "lab:" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_unreachable" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-unreachable-7.c b/gcc/testsuite/gcc.dg/builtin-unreachable-7.c
index a6c078f..0ff60b6 100644
--- a/gcc/testsuite/gcc.dg/builtin-unreachable-7.c
+++ b/gcc/testsuite/gcc.dg/builtin-unreachable-7.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1 -fno-tree-dominator-opts -fno-tree-vrp" } */
+/* { dg-options "-O2 -fdump-tree-optimized -fno-tree-dominator-opts -fno-tree-vrp" } */
/* { dg-require-effective-target label_values } */
void foo (int b, int c)
@@ -18,7 +18,7 @@ lab2:
/* Fab should still able to remove the conditional but leave the bb there. */
-/* { dg-final { scan-tree-dump-times "lab:" 1 "fab1" } } */
-/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 1 "fab1" } } */
-/* { dg-final { scan-tree-dump-not "if " "fab1" } } */
+/* { dg-final { scan-tree-dump-times "lab:" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_unreachable" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "if " "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/c2y-init-2.c b/gcc/testsuite/gcc.dg/c2y-init-2.c
new file mode 100644
index 0000000..cf62eaa
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2y-init-2.c
@@ -0,0 +1,33 @@
+/* Test invalid initializers that are consistent with the syntax: undefined
+ behavior ("shall" in Semantics not Constraints) before C2y, constraint
+ violation in C2y. Structure and union cases. */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+struct s1 { int a, b; };
+struct s2 { struct s1 x; };
+struct s3 { struct s2 x; };
+union u1 { int a; };
+union u2 { union u1 x; };
+union u3 { union u2 x; };
+
+struct s1 s1v;
+volatile struct s2 s2v;
+union u1 u1v;
+const union u2 u2v;
+
+void
+f ()
+{
+ struct s1 ts1a = {}, ts1b = s1v, ts1c = { 1, 2 };
+ const struct s2 ts2a = {}, ts2b = s2v, ts2c = { s1v }, ts2d = { 1 };
+ volatile struct s3 ts3a = { s2v }, ts3b = { s1v };
+ union u1 tu1a = {}, tu1b = u1v, tu1c = { 1 };
+ const union u2 tu2a = {}, tu2b = u2v, tu2c = { u1v }, tu2d = { 1 };
+ volatile union u3 tu3a = { u2v }, tu3b = { u1v };
+ struct s2 es2a = 1; /* { dg-error "invalid initializer" } */
+ struct s2 es2b = s1v; /* { dg-error "invalid initializer" } */
+ struct s1 es1a = s2v; /* { dg-error "invalid initializer" } */
+ union u2 eu2a = u1v; /* { dg-error "invalid initializer" } */
+ union u1 eu1a = 1; /* { dg-error "invalid initializer" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c2y-init-3.c b/gcc/testsuite/gcc.dg/c2y-init-3.c
new file mode 100644
index 0000000..1dd0607
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c2y-init-3.c
@@ -0,0 +1,106 @@
+/* Test invalid initializers that are consistent with the syntax: undefined
+ behavior ("shall" in Semantics not Constraints) before C2y, constraint
+ violation in C2y. Array cases. */
+/* { dg-do compile } */
+/* { dg-options "-std=c2y -pedantic-errors" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+typedef __CHAR8_TYPE__ char8_t;
+typedef __CHAR16_TYPE__ char16_t;
+typedef __CHAR32_TYPE__ char32_t;
+
+const char c1[] = "", c2[] = { "" }, c3[] = { "", };
+char c4[] = u8"", c5[] = { u8"" }, c6[] = { u8"", };
+
+signed char sc1[] = "", sc2[] = { "" }, sc3[] = { "", };
+volatile signed char sc4[] = u8"", sc5[] = { u8"" }, sc6[] = { u8"", };
+
+unsigned char uc1[] = "", uc2[] = { "" }, uc3[] = { "", };
+unsigned char uc4[] = u8"", uc5[] = { u8"" }, uc6[] = { u8"", };
+
+char8_t c8_1[] = "", c8_2[] = { "" }, c8_3[] = { "", };
+char8_t c8_4[] = u8"", c8_5[] = { u8"" }, c8_6[] = { u8"", };
+
+wchar_t w1[] = L"", w2[] = { L"" }, w3[] = { L"", };
+char16_t c16_1[] = u"", c16_2[] = { u"" }, c16_3[] = { u"", };
+char32_t c32_1[] = U"", c32_2[] = { U"" }, c32_3[] = { U"", };
+
+int ia[] = { 1, 2, 3 };
+
+_Atomic char ac[] = ""; /* { dg-error "inappropriate type" } */
+_Atomic wchar_t aw[] = L""; /* { dg-error "inappropriate type" } */
+_Atomic char8_t ac8[] = u8""; /* { dg-error "inappropriate type" } */
+_Atomic char16_t ac16[] = u""; /* { dg-error "inappropriate type" } */
+_Atomic char32_t ac32[] = U""; /* { dg-error "inappropriate type" } */
+
+#if __WCHAR_WIDTH__ > __SCHAR_WIDTH__
+typedef char char_not_wchar;
+typedef wchar_t wchar_not_char;
+#else
+typedef long long int char_not_wchar;
+typedef long long int wchar_not_char;
+#endif
+char_not_wchar cnw[] = L""; /* { dg-error "cannot initialize|inappropriate type" } */
+char_not_wchar cnwb[] = { L"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char wnc[] = ""; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char wncb[] = { "" }; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char wnc8[] = u8""; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char wnc8b[] = { u8"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+
+#if __INT_LEAST16_WIDTH__ > __SCHAR_WIDTH__
+typedef char char_not_char16;
+typedef char16_t char16_not_char;
+#else
+typedef long long int char_not_char16;
+typedef long long int char16_not_char;
+#endif
+char_not_char16 cn16[] = u""; /* { dg-error "cannot initialize|inappropriate type" } */
+char_not_char16 cn16b[] = { u"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_char c16nc[] = ""; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_char c16ncb[] = { "" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_char c16nc8[] = u8""; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_char c16nc8b[] = { u8"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+
+#if __INT_LEAST32_WIDTH__ > __SCHAR_WIDTH__
+typedef char char_not_char32;
+typedef char32_t char32_not_char;
+#else
+typedef long long int char_not_char32;
+typedef long long int char32_not_char;
+#endif
+char_not_char32 cn32[] = U""; /* { dg-error "cannot initialize|inappropriate type" } */
+char_not_char32 cn32b[] = { U"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_char c32nc[] = ""; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_char c32ncb[] = { "" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_char c32nc8[] = u8""; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_char c32nc8b[] = { u8"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+
+#if __WCHAR_WIDTH__ == __INT_LEAST16_WIDTH__
+typedef long long int wchar_not_char16;
+typedef long long int char16_not_wchar;
+#else
+typedef wchar_t wchar_not_char16;
+typedef char16_t char16_not_wchar;
+#endif
+wchar_not_char16 wcn16[] = u""; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char16 wcn16b[] = { u"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_wchar c16nwc[] = L""; /* { dg-error "cannot initialize|inappropriate type" } */
+char16_not_wchar c16nwcb[] = { L"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+
+#if __WCHAR_WIDTH__ == __INT_LEAST32_WIDTH__
+typedef long long int wchar_not_char32;
+typedef long long int char32_not_wchar;
+#else
+typedef wchar_t wchar_not_char32;
+typedef char32_t char32_not_wchar;
+#endif
+wchar_not_char32 wcn32[] = U""; /* { dg-error "cannot initialize|inappropriate type" } */
+wchar_not_char32 wcn32b[] = { U"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_wchar c32nwc[] = L""; /* { dg-error "cannot initialize|inappropriate type" } */
+char32_not_wchar c32nwcb[] = { L"" }; /* { dg-error "cannot initialize|inappropriate type" } */
+
+void
+f ()
+{
+ int ic[] = ia; /* { dg-error "invalid initializer" } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc
index 7398a29..8ba576e 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc
@@ -210,9 +210,8 @@ report_diag_with_graphs (location_t loc)
g->set_description (desc);
auto a = std::make_unique<diagnostic_node> (*g, "a");
auto b = std::make_unique<diagnostic_node> (*g, "b");
-#define KEY_PREFIX "/placeholder-prefix/"
- b->set_attr (KEY_PREFIX, "color", "red");
-#undef KEY_PREFIX
+ const json::string_property color ("/placeholder-prefix/color");
+ b->set_property (color, "red");
auto c = std::make_unique<diagnostic_node> (*g, "c");
c->set_label ("I am a node label");
diff --git a/gcc/testsuite/gcc.dg/plugin/start_unit_plugin.cc b/gcc/testsuite/gcc.dg/plugin/start_unit_plugin.cc
index 7b4f40e..3b3406e 100644
--- a/gcc/testsuite/gcc.dg/plugin/start_unit_plugin.cc
+++ b/gcc/testsuite/gcc.dg/plugin/start_unit_plugin.cc
@@ -2,7 +2,7 @@
* By the time a PLUGIN_START_UNIT callback is invoked, the frontend
* initialization should have completed. At least the different *_type_nodes
* should have been created. This plugin creates an artificial global
- * interger variable.
+ * integer variable.
*
*/
#include "gcc-plugin.h"
diff --git a/gcc/testsuite/gcc.dg/pr78408-2.c b/gcc/testsuite/gcc.dg/pr78408-2.c
index 89c9b7e..cad1285 100644
--- a/gcc/testsuite/gcc.dg/pr78408-2.c
+++ b/gcc/testsuite/gcc.dg/pr78408-2.c
@@ -1,7 +1,7 @@
/* PR c/78408 */
/* { dg-do compile { target size32plus } } */
-/* { dg-options "-O2 -fdump-tree-fab1-details" } */
-/* { dg-final { scan-tree-dump-not "after previous" "fab1" } } */
+/* { dg-options "-O2 -fdump-tree-forwprop1-details" } */
+/* { dg-final { scan-tree-dump-not "after previous" "forwprop1" } } */
struct S { char a[32]; };
struct T { char a[65536]; };
diff --git a/gcc/testsuite/gcc.dg/torture/pr122079-1.c b/gcc/testsuite/gcc.dg/torture/pr122079-1.c
new file mode 100644
index 0000000..0af01a5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr122079-1.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fcode-hoisting" } */
+
+int a, b, c;
+void e(int *f) {
+ int d = 0;
+ if (f)
+ goto g;
+ goto h;
+i:
+ d = 1 + f[0];
+j:
+ if (c)
+ goto h;
+k:
+ if (b)
+ goto i;
+ if (a)
+ goto j;
+g:
+ if (d + f[0])
+ goto k;
+h:
+ int l[] = {f[0]};
+ if (a)
+ e(l);
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr122079-2.c b/gcc/testsuite/gcc.dg/torture/pr122079-2.c
new file mode 100644
index 0000000..40c36b0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr122079-2.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+
+int a, b, *c = &a, d, e, f;
+void g(int *p) { a = p[0]; }
+int main() {
+ int h = 0;
+i:
+ d = c[0];
+ c[0] = h;
+ if (a)
+ goto j;
+k:
+ h = c[0] - 1;
+ while (1) {
+ if (b)
+ goto i;
+ if (f)
+ goto k;
+ j:
+ if (!e) {
+ int m[] = {c[0]};
+ g(m);
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/torture/pr122079-3.c b/gcc/testsuite/gcc.dg/torture/pr122079-3.c
new file mode 100644
index 0000000..df95c71
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr122079-3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-tree-loop-im" } */
+
+int a, b, c;
+void d(int[]);
+void e(int f[][2]) {
+g:
+ b = f[0][1];
+ if (c)
+ goto h;
+i:
+ if (a)
+ goto g;
+ if (f[1][1])
+ goto j;
+h:
+ if (f[1][1])
+ goto i;
+ goto k;
+j:
+ b--;
+ if (b + f[0][1])
+ goto i;
+k:
+ int l[] = {f[0][1]};
+ d(l);
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c
index 9e45014..31d7f70 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
typedef struct { int i; } FILE;
FILE *fp;
@@ -29,12 +29,12 @@ void test (void)
vi9 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi8.*fprintf.*fp.*\"%d%d\".*vi9" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi8.*fprintf.*fp.*\"%d%d\".*vi9" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c
index f3de73a..f4f18e8 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-chk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
typedef struct { int i; } FILE;
FILE *fp;
@@ -29,12 +29,12 @@ void test (void)
vi9 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi8.*__fprintf_chk.*fp.*1.*\"%d%d\".*vi9" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*fwrite.*\"hello\".*1, 5, fp.*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*fputc.*fp.*vi7" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi7.*fputc.*fp.*vi8" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi8.*__fprintf_chk.*fp.*1.*\"%d%d\".*vi9" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c
index bd119e0..056edea 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
extern int printf (const char *, ...);
volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
@@ -29,13 +29,13 @@ void test (void)
via = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*printf.*\"hello\".*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*printf.*\"hello\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi7 ={v} 0\[^\(\)\]*vi8 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*printf.*\"hello\".*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*printf.*\"hello\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi7 ={v} 0\[^\(\)\]*vi8 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c
index a0c0ef9..1a9690f 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-chk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
extern int __printf_chk (int, const char *, ...);
volatile int vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7, vi8, vi9, via;
@@ -29,13 +29,13 @@ void test (void)
via = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*__printf_chk.*1.*\"hello\".*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*__printf_chk.*1.*\"hello\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi7 ={v} 0\[^\(\)\]*vi8 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*__printf_chk.*1.*\"hello\".*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*__printf_chk.*1.*\"hello\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*puts.*\"hello\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*putchar.*vi7" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi7 ={v} 0\[^\(\)\]*vi8 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi8.*putchar.*vi9" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi9.*puts.*\"hello\\\\n\".*via" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c
index 29b4a4b..3124309 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
#include <stdarg.h>
@@ -29,10 +29,10 @@ test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
vi7 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*vfprintf.*\"%s\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*vfprintf.*\"%c\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*vfprintf.*\"%s\\\\n\".*vi7" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*vfprintf.*\"%s\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*vfprintf.*\"%c\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*vfprintf.*\"%s\\\\n\".*vi7" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c
index c91c709..15ee7f9 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vfprintf-chk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
#include <stdarg.h>
@@ -29,10 +29,10 @@ test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
vi7 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*__vfprintf_chk.*fp.*1.*\"%s\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*__vfprintf_chk.*fp.*1.*\"%c\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*__vfprintf_chk.*fp.*1.*\"%s\\\\n\".*vi7" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*fwrite.*\"hello\".*1, 5, fp.*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*fwrite.*\"hello\\\\n\".*1, 6, fp.*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*fputc.*fp.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*__vfprintf_chk.*fp.*1.*\"%s\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*__vfprintf_chk.*fp.*1.*\"%c\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*__vfprintf_chk.*fp.*1.*\"%s\\\\n\".*vi7" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c
index 023384a..ed7a4ae 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
#include <stdarg.h>
@@ -27,10 +27,10 @@ test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
vi7 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*vprintf.*\"hello\".*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*vprintf.*\"%s\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*vprintf.*\"%c\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*vprintf.*\"%s\\\\n\".*vi7" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*vprintf.*\"hello\".*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*vprintf.*\"%s\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*vprintf.*\"%c\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*vprintf.*\"%s\\\\n\".*vi7" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c
index 2b21f7b..b86fe33 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-vprintf-chk-1.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-fab1" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
#include <stdarg.h>
@@ -27,10 +27,10 @@ test (va_list ap1, va_list ap2, va_list ap3, va_list ap4, va_list ap5,
vi7 = 0;
}
-/* { dg-final { scan-tree-dump "vi0.*__vprintf_chk.*1.*\"hello\".*vi1" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi4.*__vprintf_chk.*1.*\"%s\".*vi5" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi5.*__vprintf_chk.*1.*\"%c\".*vi6" "fab1"} } */
-/* { dg-final { scan-tree-dump "vi6.*__vprintf_chk.*1.*\"%s\\\\n\".*vi7" "fab1"} } */
+/* { dg-final { scan-tree-dump "vi0.*__vprintf_chk.*1.*\"hello\".*vi1" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi1.*puts.*\"hello\".*vi2" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi2.*putchar.*vi3" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi3 ={v} 0\[^\(\)\]*vi4 ={v} 0" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi4.*__vprintf_chk.*1.*\"%s\".*vi5" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi5.*__vprintf_chk.*1.*\"%c\".*vi6" "optimized"} } */
+/* { dg-final { scan-tree-dump "vi6.*__vprintf_chk.*1.*\"%s\\\\n\".*vi7" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c
new file mode 100644
index 0000000..4ef8c6c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-1.c
@@ -0,0 +1,18 @@
+/* PR middle-end/122033 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void bar1 (char *, int);
+void bar3(void) __attribute__((noreturn));
+void foo1 (int size)
+{
+ {
+ char temp[size];
+ temp[size-1] = '\0';
+ bar1 (temp, size);
+ }
+ bar3 ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_stack_save" "optimized"} } */
+/* { dg-final { scan-tree-dump-not "__builtin_stack_restore" "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c
new file mode 100644
index 0000000..f429324
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr122033-2.c
@@ -0,0 +1,23 @@
+/* PR middle-end/122033 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void g(int*);
+void h();
+double t;
+void f(int a, int b)
+{
+ {
+ int array0[a];
+ {
+ int array1[b];
+ g(array0);
+ g(array1);
+ }
+ t = __builtin_sin(t);
+ }
+ h ();
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_stack_save" 2 "optimized"} } */
+/* { dg-final { scan-tree-dump-times "__builtin_stack_restore" 2 "optimized"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79691.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79691.c
index bf88931..43770c9 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr79691.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79691.c
@@ -34,4 +34,4 @@ int f4 (int i)
/* { dg-final { scan-tree-dump-times "sprintf" 1 "optimized" } }
{ dg-final { scan-tree-dump-times "snprintf" 1 "optimized" } }
- { dg-final { scan-tree-dump " = 9;" "optimized" } } */
+ { dg-final { scan-tree-dump "return 9;" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-10.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-10.c
index d6126a3..dc87a56 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-10.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-10.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-O1 -fdump-tree-fab1" } */
+/* { dg-options "-O1 -fdump-tree-optimized" } */
/* Check that we fold strlen of equally long strings, and that we do not
fail to terminate when there is a nontrivial cycle in the corresponding
@@ -32,4 +32,4 @@ middle:
}
/* There should be no calls to strlen. */
-/* { dg-final { scan-tree-dump-times "strlen" 0 "fab1"} } */
+/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized"} } */
diff --git a/gcc/testsuite/gcc.target/aarch64/asm-flag-1.c b/gcc/testsuite/gcc.target/aarch64/asm-flag-1.c
index 49901e5..7b07cdd 100644
--- a/gcc/testsuite/gcc.target/aarch64/asm-flag-1.c
+++ b/gcc/testsuite/gcc.target/aarch64/asm-flag-1.c
@@ -30,6 +30,6 @@ void f(char *out)
/* { dg-final { scan-assembler "cset.*, hi" } } */
/* { dg-final { scan-assembler "cset.*, ls" } } */
/* { dg-final { scan-assembler "cset.*, ge" } } */
-/* { dg-final { scan-assembler "cset.*, ls" } } */
+/* { dg-final { scan-assembler "cset.*, lt" } } */
/* { dg-final { scan-assembler "cset.*, gt" } } */
/* { dg-final { scan-assembler "cset.*, le" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/builtin_pld_pli.c b/gcc/testsuite/gcc.target/aarch64/builtin_pld_pli.c
index 8cbaa97..0e60baf 100644
--- a/gcc/testsuite/gcc.target/aarch64/builtin_pld_pli.c
+++ b/gcc/testsuite/gcc.target/aarch64/builtin_pld_pli.c
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-march=armv8-a -O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
#include <arm_acle.h>
@@ -38,23 +39,27 @@ prefetch_for_read_write (void *a)
__pldx (PST, SLC, KEEP, a);
__pldx (PST, SLC, STRM, a);
}
-
-/* { dg-final { scan-assembler "prfm\tPLDL1KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDL1STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDL2KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDL2STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDL3KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDL3STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDSLCKEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLDSLCSTRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL1KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL1STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL2KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL2STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL3KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTL3STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTSLCKEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPSTSLCSTRM, \\\[x\[0-9\]+\\\]" } } */
+/*
+** prefetch_for_read_write:
+** ...
+** prfm\tPLDL1KEEP, \[x[0-9]+\]
+** prfm\tPLDL1STRM, \[x[0-9]+\]
+** prfm\tPLDL2KEEP, \[x[0-9]+\]
+** prfm\tPLDL2STRM, \[x[0-9]+\]
+** prfm\tPLDL3KEEP, \[x[0-9]+\]
+** prfm\tPLDL3STRM, \[x[0-9]+\]
+** prfm\tPLDSLCKEEP, \[x[0-9]+\]
+** prfm\tPLDSLCSTRM, \[x[0-9]+\]
+** prfm\tPSTL1KEEP, \[x[0-9]+\]
+** prfm\tPSTL1STRM, \[x[0-9]+\]
+** prfm\tPSTL2KEEP, \[x[0-9]+\]
+** prfm\tPSTL2STRM, \[x[0-9]+\]
+** prfm\tPSTL3KEEP, \[x[0-9]+\]
+** prfm\tPSTL3STRM, \[x[0-9]+\]
+** prfm\tPSTSLCKEEP, \[x[0-9]+\]
+** prfm\tPSTSLCSTRM, \[x[0-9]+\]
+** ...
+*/
void
prefetch_simple (void *a)
@@ -62,9 +67,13 @@ prefetch_simple (void *a)
__pld (a);
__pli (a);
}
-
-/* { dg-final { scan-assembler "prfm\tPLDL1KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL1KEEP, \\\[x\[0-9\]+\\\]" } } */
+/*
+** prefetch_simple:
+** ...
+** prfm\tPLDL1KEEP, \[x[0-9]+\]
+** prfm\tPLIL1KEEP, \[x[0-9]+\]
+** ...
+*/
void
prefetch_instructions (void *a)
@@ -78,13 +87,16 @@ prefetch_instructions (void *a)
__plix (SLC, KEEP, a);
__plix (SLC, STRM, a);
}
-
-/* { dg-final { scan-assembler "prfm\tPLIL1KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL1STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL2KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL2STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL3KEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLIL3STRM, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLISLCKEEP, \\\[x\[0-9\]+\\\]" } } */
-/* { dg-final { scan-assembler "prfm\tPLISLCSTRM, \\\[x\[0-9\]+\\\]" } } */
-
+/*
+** prefetch_instructions:
+** ...
+** prfm\tPLIL1KEEP, \[x[0-9]+\]
+** prfm\tPLIL1STRM, \[x[0-9]+\]
+** prfm\tPLIL2KEEP, \[x[0-9]+\]
+** prfm\tPLIL2STRM, \[x[0-9]+\]
+** prfm\tPLIL3KEEP, \[x[0-9]+\]
+** prfm\tPLIL3STRM, \[x[0-9]+\]
+** prfm\tPLISLCKEEP, \[x[0-9]+\]
+** prfm\tPLISLCSTRM, \[x[0-9]+\]
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/csinc-1.c b/gcc/testsuite/gcc.target/aarch64/csinc-1.c
index 132a0f6..53e1ae2 100644
--- a/gcc/testsuite/gcc.target/aarch64/csinc-1.c
+++ b/gcc/testsuite/gcc.target/aarch64/csinc-1.c
@@ -1,16 +1,22 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
unsigned int
test_csinc32_ifcvt(unsigned int w0,
unsigned int w1,
unsigned int w2) {
- /* { dg-final { scan-assembler "csinc\tw\[0-9\]*.*ne" } } */
if (w0 == w1)
++ w2;
return w2;
}
+/*
+** test_csinc32_ifcvt:
+** cmp\tw0, w1
+** cinc\tw0, w2, eq
+** ret
+*/
unsigned int
test_csinc32_condasn1(unsigned int w0,
@@ -19,10 +25,15 @@ test_csinc32_condasn1(unsigned int w0,
unsigned int w3) {
unsigned int w4;
- /* { dg-final { scan-assembler "csinc\tw\[0-9\]*.*ne" } } */
w4 = (w0 == w1) ? (w3 + 1) : w2;
return w4;
}
+/*
+** test_csinc32_condasn1:
+** cmp\tw0, w1
+** csinc\tw0, w2, w3, ne
+** ret
+*/
unsigned int
test_csinc32_condasn2(unsigned int w0,
@@ -31,21 +42,31 @@ test_csinc32_condasn2(unsigned int w0,
unsigned int w3) {
unsigned int w4;
- /* { dg-final { scan-assembler "csinc\tw\[0-9\]*.*eq" } } */
w4 = (w0 == w1) ? w2 : (w3 + 1);
return w4;
}
+/*
+** test_csinc32_condasn2:
+** cmp\tw0, w1
+** csinc\tw0, w2, w3, eq
+** ret
+*/
unsigned long long
test_csinc64_ifcvt(unsigned long long x0,
unsigned long long x1,
unsigned long long x2) {
- /* { dg-final { scan-assembler "csinc\tx\[0-9\]*.*ne" } } */
if (x0 == x1)
++ x2;
return x2;
}
+/*
+** test_csinc64_ifcvt:
+** cmp\tx0, x1
+** cinc\tx0, x2, eq
+** ret
+*/
unsigned long long
test_csinc64_condasn1(unsigned long long x0,
@@ -54,10 +75,15 @@ test_csinc64_condasn1(unsigned long long x0,
unsigned long long x3) {
unsigned long long x4;
- /* { dg-final { scan-assembler "csinc\tx\[0-9\]*.*ne" } } */
x4 = (x0 == x1) ? (x3 + 1) : x2;
return x4;
}
+/*
+** test_csinc64_condasn1:
+** cmp\tx0, x1
+** csinc\tx0, x2, x3, ne
+** ret
+*/
unsigned long long
test_csinc64_condasn2(unsigned long long x0,
@@ -66,7 +92,12 @@ test_csinc64_condasn2(unsigned long long x0,
unsigned long long x3) {
unsigned long long x4;
- /* { dg-final { scan-assembler "csinc\tx\[0-9\]*.*eq" } } */
x4 = (x0 == x1) ? x2 : (x3 + 1);
return x4;
}
+/*
+** test_csinc64_condasn2:
+** cmp\tx0, x1
+** csinc\tx0, x2, x3, eq
+** ret
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/csneg-1.c b/gcc/testsuite/gcc.target/aarch64/csneg-1.c
index 4860d64..2533e7b 100644
--- a/gcc/testsuite/gcc.target/aarch64/csneg-1.c
+++ b/gcc/testsuite/gcc.target/aarch64/csneg-1.c
@@ -1,5 +1,6 @@
/* { dg-do compile } */
/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
int
test_csneg32_condasn1(int w0,
@@ -8,10 +9,15 @@ test_csneg32_condasn1(int w0,
int w3) {
int w4;
- /* { dg-final { scan-assembler "csneg\tw\[0-9\]*.*ne" } } */
w4 = (w0 == w1) ? -w3 : w2;
return w4;
}
+/*
+** test_csneg32_condasn1:
+** cmp\tw0, w1
+** csneg\tw0, w2, w3, ne
+** ret
+*/
int
test_csneg32_condasn2(int w0,
@@ -20,10 +26,15 @@ test_csneg32_condasn2(int w0,
int w3) {
int w4;
- /* { dg-final { scan-assembler "csneg\tw\[0-9\]*.*eq" } } */
w4 = (w0 == w1) ? w3 : -w2;
return w4;
}
+/*
+** test_csneg32_condasn2:
+** cmp\tw0, w1
+** csneg\tw0, w3, w2, eq
+** ret
+*/
long long
test_csneg64_condasn1(long long x0,
@@ -32,10 +43,15 @@ test_csneg64_condasn1(long long x0,
long long x3) {
long long x4;
- /* { dg-final { scan-assembler "csneg\tx\[0-9\]*.*ne" } } */
x4 = (x0 == x1) ? -x3 : x2;
return x4;
}
+/*
+** test_csneg64_condasn1:
+** cmp\tx0, x1
+** csneg\tx0, x2, x3, ne
+** ret
+*/
long long
test_csneg64_condasn2(long long x0,
@@ -44,27 +60,41 @@ test_csneg64_condasn2(long long x0,
long long x3) {
long long x4;
- /* { dg-final { scan-assembler "csneg\tx\[0-9\]*.*eq" } } */
x4 = (x0 == x1) ? x3 : -x2;
return x4;
}
+/*
+** test_csneg64_condasn2:
+** cmp\tx0, x1
+** csneg\tx0, x3, x2, eq
+** ret
+*/
int test_csneg_cmp(int x)
{
- /* { dg-final { scan-assembler "csneg\tw\[0-9\]" } } */
if (x > 3)
x = -x;
return x;
}
+/*
+** test_csneg_cmp:
+** cmp\tw0, 3
+** csneg\tw0, w0, w0, le
+** ret
+*/
unsigned long long
test_csneg_uxtw (unsigned int a,
unsigned int b,
unsigned int c)
{
- /* { dg-final { scan-assembler "csneg\tw\[0-9\]*.*ne" } } */
- /* { dg-final { scan-assembler-not "uxtw\tw\[0-9\]*.*" } } */
unsigned int val;
val = a ? b: -c;
return val;
}
+/*
+** test_csneg_uxtw:
+** cmp\tw0, 0
+** csneg\tw0, w1, w2, ne
+** ret
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/declare-simd-2.c b/gcc/testsuite/gcc.target/aarch64/declare-simd-2.c
index 2f4d3a8..595a172 100644
--- a/gcc/testsuite/gcc.target/aarch64/declare-simd-2.c
+++ b/gcc/testsuite/gcc.target/aarch64/declare-simd-2.c
@@ -51,11 +51,10 @@ void f05 (short a, short *b, short c)
*b += a + c;
}
-/* { dg-final { scan-assembler {_ZGVnN4ul2v_f05:} } } */
-/* { dg-final { scan-assembler {_ZGVnN4ul2v_f05:} } } */
-/* { dg-final { scan-assembler {_ZGVnM8ul2v_f05:} } } */
+/* { dg-final { scan-assembler {_ZGVnM4ul2v_f05:} } } */
/* { dg-final { scan-assembler {_ZGVnM8ul2v_f05:} } } */
+/* { dg-final { scan-assembler {_ZGVnN4ul2v_f05:} } } */
+/* { dg-final { scan-assembler {_ZGVnN8ul2v_f05:} } } */
#ifdef __cplusplus
}
#endif
-
diff --git a/gcc/testsuite/gcc.target/aarch64/flt_mov_immediate_1.c b/gcc/testsuite/gcc.target/aarch64/flt_mov_immediate_1.c
index 7b92a5a..36a1e34 100644
--- a/gcc/testsuite/gcc.target/aarch64/flt_mov_immediate_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/flt_mov_immediate_1.c
@@ -1,52 +1,74 @@
/* { dg-do compile } */
/* { dg-options "-O3" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
float f0(void)
{
float x = 0.0f;
return x;
}
+/*
+** f0:
+** movi\tv0.2s, #?0
+** ret
+*/
float fn1(void)
{
float x = -0.0f;
return x;
}
+/*
+** fn1:
+** movi\tv0.2s, 0x80, lsl 24
+** ret
+*/
float f1(void)
{
float x = 256.0f;
return x;
}
+/*
+** f1:
+** mov\t(w[0-9]+), 1132462080
+** fmov\ts0, \1
+** ret
+*/
float f2(void)
{
float x = 123256.0f;
return x;
}
+/*
+** f2:
+** mov\t(w[0-9]+), 48128
+** movk\t\1, 0x47f0, lsl 16
+** fmov\ts0, \1
+** ret
+*/
float f3(void)
{
float x = 2.0f;
return x;
}
+/*
+** f3:
+** fmov\ts0, 2\.0e\+0
+** ret
+*/
float f4(void)
{
float x = -20000.1;
return x;
}
-
-
-/* { dg-final { scan-assembler-times "movi\tv\[0-9\]+\\\.2s, ?#0" 1 } } */
-/* { dg-final { scan-assembler-times "movi\tv\[0-9\]+\\\.2s, 0x80, lsl 24" 1 } } */
-/* { dg-final { scan-assembler-times "movi\tv\[0-9\]+\\\.2s, 0x80, lsl 24" 1 } } */
-
-/* { dg-final { scan-assembler-times "mov\tw\[0-9\]+, 48128" 1 } } */
-/* { dg-final { scan-assembler-times "movk\tw\[0-9\]+, 0x47f0, lsl 16" 1 } } */
-
-/* { dg-final { scan-assembler-times "fmov\ts\[0-9\]+, 2\\\.0e\\\+0" 1 } } */
-
-/* { dg-final { scan-assembler-times "mov\tw\[0-9\]+, 16435" 1 } } */
-/* { dg-final { scan-assembler-times "movk\tw\[0-9\]+, 0xc69c, lsl 16" 1 } } */
-
+/*
+** f4:
+** mov\t(w[0-9]+), 16435
+** movk\t\1, 0xc69c, lsl 16
+** fmov\ts0, \1
+** ret
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/ldp_stp_18.c b/gcc/testsuite/gcc.target/aarch64/ldp_stp_18.c
index ea9fffc..49aa0b2 100644
--- a/gcc/testsuite/gcc.target/aarch64/ldp_stp_18.c
+++ b/gcc/testsuite/gcc.target/aarch64/ldp_stp_18.c
@@ -107,7 +107,7 @@ CONS4_FN (1, double);
CONS4_FN (2, double);
/*
-** cons2_8_double:
+** cons4_4_double:
** ...
** stp q[0-9]+, .*
** ret
@@ -115,7 +115,7 @@ CONS4_FN (2, double);
CONS4_FN (4, double);
/*
-** cons2_8_double:
+** cons4_8_double:
** ...
** stp q[0-9]+, .*
** ret
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-1.c b/gcc/testsuite/gcc.target/aarch64/mv-1.c
new file mode 100644
index 0000000..6f095ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("rng"))) int
+foo ()
+{
+ return 2;
+}
+
+__attribute__ ((target_version ("flagm"))) int
+foo ()
+{
+ return 3;
+}
+
+__attribute__ ((target_version ("rng+flagm"))) int
+foo ()
+{
+ return 4;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* Check usage of the first two FMV features, in case of off-by-one errors. */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mrng:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MrngMflagm:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mflagm:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c
new file mode 100644
index 0000000..b08de29
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; } /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) int
+foo () { return 1; } /* { dg-error "redefinition of .foo \\\[\\\[target_clones\\(.dotprod., .sve.\\)\\\]\\\]." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c
new file mode 100644
index 0000000..d34b246
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; } /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.default.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "conflicting types for .foo \\\[\\\[target_clones\\(.dotprod., .sve.\\)\\\]\\\].; have .float\\(void\\)." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c
new file mode 100644
index 0000000..a6a45bd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+float foo () { return 1; } /* { dg-message "previous definition of .foo." } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "redefinition of .foo \\\[\\\[target_clones\\(.default., .dotprod., .sve.\\)\\\]\\\]." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c
new file mode 100644
index 0000000..39ed306
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 0;
+}
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+ return 2;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c
new file mode 100644
index 0000000..17c7cbd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ();
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+ return 2;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c
new file mode 100644
index 0000000..8325c8e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ();
+
+__attribute__((target_version("default")))
+int foo ()
+{
+ return 0;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+ return 2;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
+// { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\.default\n" 1 } }
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Msve\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Msve2\n" 1 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c
new file mode 100644
index 0000000..951c950
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("dotprod")))
+int foo ()
+{
+ return 0;
+}
+
+__attribute__((target_clones("default", "sve+sve2")))
+int foo ()
+{
+ return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+ return 2;
+}
+
+int bar()
+{
+ return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error1.c b/gcc/testsuite/gcc.target/aarch64/mv-error1.c
new file mode 100644
index 0000000..61c9af2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; } /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.default.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "conflicting types for .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\].; have .float\\(void\\)." } */
+
+__attribute__ ((target_version ("sve"))) int
+foo2 () { return 1; } /* { dg-message "previous definition of .foo2 \\\[\\\[target_version\\(.sve.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo2 () { return 3; } /* { dg-error "conflicting types for .foo2 \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\].; have .float\\(void\\)." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error10.c b/gcc/testsuite/gcc.target/aarch64/mv-error10.c
new file mode 100644
index 0000000..218f103
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error10.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+void
+bar ()
+{
+ __attribute__ ((target_version ("dotprod"))) int
+ foo1 (); /* { dg-message "versioned declarations are only allowed at file scope" } */
+
+ __attribute__ ((target_version ("simd"))) int
+ foo2 () { return 1; } /* { dg-message "versioned definitions are only allowed at file scope" } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error11.c b/gcc/testsuite/gcc.target/aarch64/mv-error11.c
new file mode 100644
index 0000000..0fdd660
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error11.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+int fn () asm("name");
+int fn () { return 1; } /* { dg-error "cannot use function multiversioning on a renamed function" } */
+int fn [[gnu::target_version("sve")]] () { return 1; }
+
+int fn2 [[gnu::target_version("sve")]] () asm("name"); /* { dg-warning ".asm. declaration ignored due to conflict with previous rename" } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error12.c b/gcc/testsuite/gcc.target/aarch64/mv-error12.c
new file mode 100644
index 0000000..45da85a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error12.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-fopenmp" } */
+/* { dg-require-ifunc "" } */
+
+#pragma omp declare simd
+int fn [[gnu::target_version("sve")]] () { return 1; } /* { dg-error ".#pragma omp declare simd. cannot be used with function multi-versioning" } */
+
+#pragma omp declare simd
+int fn2 () { return 1; }
+
+int fn2 [[gnu::target_version("sve")]] (); /* { dg-warning "ignoring attribute .target_version. because it conflicts with attribute .omp declare simd." } */
+
+int fn3 [[gnu::target_version("sve")]] [[gnu::simd]] () { return 1; } /* { dg-warning "ignoring attribute .simd. because it conflicts with attribute .target_version." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error2.c b/gcc/testsuite/gcc.target/aarch64/mv-error2.c
new file mode 100644
index 0000000..19d961d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\]. with type .float\\(void\\)." } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "redefinition of .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\]." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error3.c b/gcc/testsuite/gcc.target/aarch64/mv-error3.c
new file mode 100644
index 0000000..451ce02
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; }
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.default.\\)\\\]\\\]. with type .float\\(void\\)." } */
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-error "redefinition of .foo \\\[\\\[target_version\\(.default.\\)\\\]\\\]." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error4.c b/gcc/testsuite/gcc.target/aarch64/mv-error4.c
new file mode 100644
index 0000000..44d3195
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error4.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("test"))) float
+foo () { return 3; } /* { dg-error "invalid feature modifier .test. of value .test. in .target_version. attribute" } */
+
+__attribute__ ((target_version ("sve+test"))) float
+foo2 () { return 3; } /* { dg-error "invalid feature modifier .test. of value .sve.test. in .target_version. attribute" } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error5.c b/gcc/testsuite/gcc.target/aarch64/mv-error5.c
new file mode 100644
index 0000000..776b80a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error5.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo(); /* { dg-message "previous declaration of .foo \\\[\\\[target_version\\(.sve\\\+sve2.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error6.c b/gcc/testsuite/gcc.target/aarch64/mv-error6.c
new file mode 100644
index 0000000..afc71a4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error6.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo () {
+ return 1;
+}
+
+__attribute__ ((target_version ("sve"))) int
+foo () { /* { dg-message "previous definition of .foo \\\[\\\[target_version\\(.sve.\\)\\\]\\\]. with type .int\\(void\\)." } */
+ return 1;
+}
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo2(); /* { dg-message "previous declaration of .foo2 \\\[\\\[target_version\\(.sve\\\+sve2.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+int bar2 () { return foo2 (); } /* { dg-error "implicit declaration of function .foo2." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error7.c b/gcc/testsuite/gcc.target/aarch64/mv-error7.c
new file mode 100644
index 0000000..68db978
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error7.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (); /* { dg-message "previous declaration of .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function .foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error8.c b/gcc/testsuite/gcc.target/aarch64/mv-error8.c
new file mode 100644
index 0000000..7599df1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error8.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo (int a, int (*b)[4]) { return 1; }
+
+int bar(void) {
+ __attribute__ ((target_version ("dotprod"))) int
+ foo (int a, int (*b)[5]) { return 3; } /* { dg-error "versioned definitions are only allowed at file scope" } */
+
+ return 1;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error9.c b/gcc/testsuite/gcc.target/aarch64/mv-error9.c
new file mode 100644
index 0000000..dc982e9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error9.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (); /* { dg-message "previous declaration of .foo \\\[\\\[target_version\\(.dotprod.\\)\\\]\\\]. with type .int\\(void\\)." } */
+
+int
+bar ()
+{
+ return foo (); /* { dg-error "implicit declaration of function .foo." } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c
new file mode 100644
index 0000000..7982278
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// Basic case of fmv correctness with all functions and use in one TU.
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mv-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c
new file mode 100644
index 0000000..d525638
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int
+foo ();
+
+int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c
new file mode 100644
index 0000000..fd3dc34
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// Check that types can be combined
+
+__attribute__ ((target_version ("default"))) int
+foo (int a, int (*b)[4]) { return 1; }
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (int a, int (*b)[]) { return 3; }
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c
new file mode 100644
index 0000000..1a0b667
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; }
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; }
+
+int bar ()
+{
+ int (*test)() = foo;
+
+ test();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c
new file mode 100644
index 0000000..308dace
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+int bar ()
+{
+ int (*test)() = foo;
+
+ test();
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; }
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\._Mdotprod\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\.default\n" 0 } } */
+
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols14.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols14.c
new file mode 100644
index 0000000..d1af69f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols14.c
@@ -0,0 +1,34 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+int foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c
new file mode 100644
index 0000000..a8732ca
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with definitions but no call
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c
new file mode 100644
index 0000000..962bae9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with declarations but no implementation
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c
new file mode 100644
index 0000000..a476800
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with a default implementation and declarations of other
+// versions
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c
new file mode 100644
index 0000000..4df2000
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with default declaration, and implementations of other
+// versions.
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c
new file mode 100644
index 0000000..cbf8bca
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+int bar()
+{
+ return foo();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "bl\tfoo.default\n" 1 } } */
+/* { dg-final { scan-assembler-times ".global\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times ".set\tfoo,foo.default\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c
new file mode 100644
index 0000000..2ea4d2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+int
+bar ()
+{
+ return foo ();
+}
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c
new file mode 100644
index 0000000..3e3eaf2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+ return 1;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c b/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c
new file mode 100644
index 0000000..8e0864f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+foo ()
+{
+ return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+ return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+ return 5;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error1.c b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c
new file mode 100644
index 0000000..482d0a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default, dotprod"))) float
+foo (); /* { dg-message "previous declaration of .foo \\\[\\\[target_clones\\(.default., .dotprod.\\)\\\]\\\]." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error ".foo \\\[\\\[target_clones\\(.dotprod., .sve.\\)\\\]\\\]. conflicts with overlapping .target_clone. declaration" } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error2.c b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c
new file mode 100644
index 0000000..482d0a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default, dotprod"))) float
+foo (); /* { dg-message "previous declaration of .foo \\\[\\\[target_clones\\(.default., .dotprod.\\)\\\]\\\]." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error ".foo \\\[\\\[target_clones\\(.dotprod., .sve.\\)\\\]\\\]. conflicts with overlapping .target_clone. declaration" } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c b/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c
new file mode 100644
index 0000000..3ad15e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ()
+{
+ return 1;
+}
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+ update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c b/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c
new file mode 100644
index 0000000..78385ed
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ()
+{
+ return 1;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c b/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c
new file mode 100644
index 0000000..1cbe3fd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+ return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c b/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c
new file mode 100644
index 0000000..abaf60f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ();
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c
new file mode 100644
index 0000000..1bae38c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo () {
+ return 1;
+}
+
+__attribute__((target_clones("invalid1")))
+int foo () { /* { dg-warning "invalid feature modifier .invalid1. in version .invalid1. for .target_clones. attribute" } */
+ return 2;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/ror_2.c b/gcc/testsuite/gcc.target/aarch64/ror_2.c
index 796c122..fbea839 100644
--- a/gcc/testsuite/gcc.target/aarch64/ror_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/ror_2.c
@@ -175,8 +175,8 @@ tst2 (unsigned x, unsigned y)
int
tst3 (unsigned x, unsigned y)
{
- /* { dg-final { scan-assembler "tst\tw\[0-9\]+, w\[0-9\]+, ror 20\n" } } */
- return ((unsigned long)x & ROR (y, 20)) == 0;
+ /* { dg-final { scan-assembler "tst\tw\[0-9\]+, w\[0-9\]+, ror 21\n" } } */
+ return ((unsigned long)x & ROR (y, 21)) == 0;
}
int
@@ -189,15 +189,15 @@ bics1 (unsigned x, unsigned y)
int
bics2 (unsigned x, unsigned y)
{
- /* { dg-final { scan-assembler "bics\twzr, w\[0-9\]+, w\[0-9\]+, ror 21\n" } } */
- return (x & ~ROR (y, 21)) == 0;
+ /* { dg-final { scan-assembler "bics\twzr, w\[0-9\]+, w\[0-9\]+, ror 22\n" } } */
+ return (x & ~ROR (y, 22)) == 0;
}
int
bics3 (unsigned x, unsigned y)
{
- /* { dg-final { scan-assembler "bics\twzr, w\[0-9\]+, w\[0-9\]+, ror 21\n" } } */
- return (x & (unsigned long)~ROR (y, 21)) == 0;
+ /* { dg-final { scan-assembler "bics\twzr, w\[0-9\]+, w\[0-9\]+, ror 23\n" } } */
+ return (x & (unsigned long)~ROR (y, 23)) == 0;
}
/* { dg-final { scan-assembler-not "cmp" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/scalar_intrinsics.c b/gcc/testsuite/gcc.target/aarch64/scalar_intrinsics.c
index dcf9dc7..094aaff 100644
--- a/gcc/testsuite/gcc.target/aarch64/scalar_intrinsics.c
+++ b/gcc/testsuite/gcc.target/aarch64/scalar_intrinsics.c
@@ -913,7 +913,7 @@ test_vrsrad_n_s64 (int64_t a, int64_t b)
return vrsrad_n_s64 (a, b, 3);
}
-/* { dg-final { scan-assembler-times "\\tsrsra\\td\[0-9\]+" 1 } } */
+/* { dg-final { scan-assembler-times "\\tursra\\td\[0-9\]+" 1 } } */
uint64_t
test_vrsrad_n_u64 (uint64_t a, uint64_t b)
diff --git a/gcc/testsuite/gcc.target/aarch64/scalar_shift_1.c b/gcc/testsuite/gcc.target/aarch64/scalar_shift_1.c
index 7be1b12..e715f19 100644
--- a/gcc/testsuite/gcc.target/aarch64/scalar_shift_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/scalar_shift_1.c
@@ -1,6 +1,6 @@
/* { dg-do run } */
/* { dg-options "-O2 -fno-inline -save-temps" } */
-
+/* { dg-final { check-function-bodies "**" "" "" } } */
extern void abort ();
#define force_simd_di(v) asm volatile ("mov %d0, %1.d[0]" :"=w" (v) :"w" (v) :)
@@ -23,8 +23,13 @@ test_lshift_left_sisd_di (UInt64x1 b, UInt64x1 c)
force_simd_di (a);
return a;
}
-/* { dg-final { scan-assembler "shl\td\[0-9\]+,\ d\[0-9\]+,\ 8" } } */
-/* { dg-final { scan-assembler "ushl\td\[0-9\]+,\ d\[0-9\]+,\ d\[0-9\]+" } } */
+/*
+** test_lshift_left_sisd_di:
+** ...
+** shl\t(d[0-9]+), d[0-9]+, 8
+** ushl\td[0-9]+, \1, d[0-9]+
+** ...
+*/
UInt32x1
test_lshift_left_sisd_si (UInt32x1 b, UInt32x1 c)
@@ -38,8 +43,13 @@ test_lshift_left_sisd_si (UInt32x1 b, UInt32x1 c)
force_simd_si (a);
return a;
}
-/* { dg-final { scan-assembler "shl\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ 4" } } */
-/* "ushl\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ v\[0-9\]+\.2s" (counted later) */
+/*
+** test_lshift_left_sisd_si:
+** ...
+** shl\t(v[0-9]+\.2s), v[0-9]+\.2s, 4
+** ushl\tv[0-9]+\.2s, \1, v[0-9]+\.2s
+** ...
+*/
UInt64x1
test_lshift_right_sisd_di (UInt64x1 b, UInt64x1 c)
@@ -53,9 +63,14 @@ test_lshift_right_sisd_di (UInt64x1 b, UInt64x1 c)
force_simd_di (a);
return a;
}
-/* { dg-final { scan-assembler "ushr\td\[0-9\]+,\ d\[0-9\]+,\ 8" } } */
-/* "neg\td\[0-9\]+,\ d\[0-9\]+" (counted later) */
-/* { dg-final { scan-assembler "ushl\td\[0-9\]+,\ d\[0-9\]+,\ d\[0-9\]+" } } */
+/*
+** test_lshift_right_sisd_di:
+** ...
+** ushr\t(d[0-9]+), d[0-9]+, 8
+** neg\t(d[0-9]+), d[0-9]+
+** ushl\td[0-9]+, \1, \2
+** ...
+*/
UInt64x1
test_lshift_right_sisd_si (UInt32x1 b, UInt32x1 c)
@@ -69,9 +84,14 @@ test_lshift_right_sisd_si (UInt32x1 b, UInt32x1 c)
force_simd_si (a);
return a;
}
-/* { dg-final { scan-assembler "ushr\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ 4" } } */
-/* "neg\td\[0-9\]+,\ d\[0-9\]+" (counted later) */
-/* { dg-final { scan-assembler-times "ushl\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ v\[0-9\]+\.2s" 2 } } */
+/*
+** test_lshift_right_sisd_si:
+** ...
+** ushr\t(v[0-9]+\.2s), v[0-9]+\.2s, 4
+** neg\td([0-9]+), d[0-9]+
+** ushl\tv[0-9]+\.2s, \1, v\2\.2s
+** ...
+*/
Int64x1
test_ashift_right_sisd_di (Int64x1 b, Int64x1 c)
@@ -85,9 +105,14 @@ test_ashift_right_sisd_di (Int64x1 b, Int64x1 c)
force_simd_di (a);
return a;
}
-/* { dg-final { scan-assembler "sshr\td\[0-9\]+,\ d\[0-9\]+,\ 8" } } */
-/* "neg\td\[0-9\]+,\ d\[0-9\]+" (counted later) */
-/* { dg-final { scan-assembler "sshl\td\[0-9\]+,\ d\[0-9\]+,\ d\[0-9\]+" } } */
+/*
+** test_ashift_right_sisd_di:
+** ...
+** sshr\t(d[0-9]+), d[0-9]+, 8
+** neg\t(d[0-9]+), d[0-9]+
+** sshl\td[0-9]+, \1, \2
+** ...
+*/
Int32x1
test_ashift_right_sisd_si (Int32x1 b, Int32x1 c)
@@ -101,10 +126,14 @@ test_ashift_right_sisd_si (Int32x1 b, Int32x1 c)
force_simd_si (a);
return a;
}
-/* { dg-final { scan-assembler "sshr\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ 4" } } */
-/* { dg-final { scan-assembler-times "neg\td\[0-9\]+,\ d\[0-9\]+" 4 } } */
-/* { dg-final { scan-assembler "sshl\tv\[0-9\]+\.2s,\ v\[0-9\]+\.2s,\ v\[0-9\]+\.2s" } } */
-
+/*
+** test_ashift_right_sisd_si:
+** ...
+** sshr\t(v[0-9]+\.2s), v[0-9]+\.2s, 4
+** neg\td([0-9]+), d[0-9]+
+** sshl\tv[0-9]+\.2s, \1, v\2\.2s
+** ...
+*/
/* The following are to make sure if the integer instructions lsl/lsr/asr are
generated in non-vector scenarios */
@@ -118,8 +147,12 @@ test_lshift_left_int_di (UInt64x1 b, UInt64x1 c)
a = a << c;
return a;
}
-/* { dg-final { scan-assembler "lsl\tx\[0-9\]+,\ x\[0-9\]+,\ 8" } } */
-/* { dg-final { scan-assembler "lsl\tx\[0-9\]+,\ x\[0-9\]+,\ x\[0-9\]+" } } */
+/*
+** test_lshift_left_int_di:
+** lsl\t(x[0-9]+), x0, 8
+** lsl\tx0, \1, x1
+** ret
+*/
UInt32x1
test_lshift_left_int_si (UInt32x1 b, UInt32x1 c)
@@ -130,8 +163,12 @@ test_lshift_left_int_si (UInt32x1 b, UInt32x1 c)
a = a << c;
return a;
}
-/* { dg-final { scan-assembler "lsl\tw\[0-9\]+,\ w\[0-9\]+,\ 4" } } */
-/* { dg-final { scan-assembler "lsl\tw\[0-9\]+,\ w\[0-9\]+,\ w\[0-9\]+" } } */
+/*
+** test_lshift_left_int_si:
+** lsl\t(w[0-9]+), w0, 4
+** lsl\tw0, \1, w1
+** ret
+*/
UInt64x1
test_lshift_right_int_di (UInt64x1 b, UInt64x1 c)
@@ -142,8 +179,12 @@ test_lshift_right_int_di (UInt64x1 b, UInt64x1 c)
a = a >> c;
return a;
}
-/* { dg-final { scan-assembler "lsr\tx\[0-9\]+,\ x\[0-9\]+,\ 8" } } */
-/* { dg-final { scan-assembler "lsr\tx\[0-9\]+,\ x\[0-9\]+,\ x\[0-9\]+" } } */
+/*
+** test_lshift_right_int_di:
+** lsr\t(x[0-9]+), x0, 8
+** lsr\tx0, \1, x1
+** ret
+*/
UInt32x1
test_lshift_right_int_si (UInt32x1 b, UInt32x1 c)
@@ -154,8 +195,12 @@ test_lshift_right_int_si (UInt32x1 b, UInt32x1 c)
a = a >> c;
return a;
}
-/* { dg-final { scan-assembler "lsr\tw\[0-9\]+,\ w\[0-9\]+,\ 4" } } */
-/* { dg-final { scan-assembler "lsr\tw\[0-9\]+,\ w\[0-9\]+,\ w\[0-9\]+" } } */
+/*
+** test_lshift_right_int_si:
+** lsr\t(w[0-9]+), w0, 4
+** lsr\tw0, \1, w1
+** ret
+*/
Int64x1
test_ashift_right_int_di (Int64x1 b, Int64x1 c)
@@ -166,8 +211,12 @@ test_ashift_right_int_di (Int64x1 b, Int64x1 c)
a = a >> c;
return a;
}
-/* { dg-final { scan-assembler "asr\tx\[0-9\]+,\ x\[0-9\]+,\ 8" } } */
-/* { dg-final { scan-assembler "asr\tx\[0-9\]+,\ x\[0-9\]+,\ x\[0-9\]+" } } */
+/*
+** test_ashift_right_int_di:
+** asr\t(x[0-9]+), x0, 8
+** asr\tx0, \1, x1
+** ret
+*/
Int32x1
test_ashift_right_int_si (Int32x1 b, Int32x1 c)
@@ -178,8 +227,12 @@ test_ashift_right_int_si (Int32x1 b, Int32x1 c)
a = a >> c;
return a;
}
-/* { dg-final { scan-assembler "asr\tw\[0-9\]+,\ w\[0-9\]+,\ 4" } } */
-/* { dg-final { scan-assembler "asr\tw\[0-9\]+,\ w\[0-9\]+,\ w\[0-9\]+" } } */
+/*
+** test_ashift_right_int_si:
+** asr\t(w[0-9]+), w0, 4
+** asr\tw0, \1, w1
+** ret
+*/
#define CHECK(var,val) \
do \
@@ -225,4 +278,3 @@ main ()
return 0;
}
-
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/fold_to_highpart_5.c b/gcc/testsuite/gcc.target/aarch64/simd/fold_to_highpart_5.c
index 4f39b67..1b72527 100644
--- a/gcc/testsuite/gcc.target/aarch64/simd/fold_to_highpart_5.c
+++ b/gcc/testsuite/gcc.target/aarch64/simd/fold_to_highpart_5.c
@@ -68,8 +68,8 @@
/* { dg-final { scan-assembler-times {ssubl2\t} 3} } */
/* { dg-final { scan-assembler-times {usubl2\t} 3} } */
-/* { dg-final { scan-assembler-times {sabdl2\t} 3} } */
-/* { dg-final { scan-assembler-times {uabdl2\t} 3} } */
+/* { dg-final { scan-assembler-times {sabal2\t} 3} } */
+/* { dg-final { scan-assembler-times {uabal2\t} 3} } */
/* { dg-final { scan-assembler-times {saddw2\t} 3} } */
/* { dg-final { scan-assembler-times {uaddw2\t} 3} } */
diff --git a/gcc/testsuite/gcc.target/aarch64/singleton_intrinsics_1.c b/gcc/testsuite/gcc.target/aarch64/singleton_intrinsics_1.c
index 1f21bd3..2736015 100644
--- a/gcc/testsuite/gcc.target/aarch64/singleton_intrinsics_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/singleton_intrinsics_1.c
@@ -298,7 +298,7 @@ test_vrsra_n_s64 (int64x1_t a, int64x1_t b)
return vrsra_n_s64 (a, b, 3);
}
-/* { dg-final { scan-assembler-times "\\tsrsra\\td\[0-9\]+" 1 } } */
+/* { dg-final { scan-assembler-times "\\tursra\\td\[0-9\]+" 1 } } */
uint64x1_t
test_vrsra_n_u64 (uint64x1_t a, uint64x1_t b)
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/arith_1.c b/gcc/testsuite/gcc.target/aarch64/sve/arith_1.c
index c2e1f6c..785b4cc 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/arith_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/arith_1.c
@@ -85,7 +85,7 @@ DO_ARITH_OPS (int64_t, -, minus)
/* { dg-final { scan-assembler-not {\tadd\tz[0-9]+\.d, z[0-9]+\.d, #-1\n} } } */
/* { dg-final { scan-assembler-times {\tsub\tz[0-9]+\.d, z[0-9]+\.d, #1\n} 1 } } */
-/* { dg-final { scan-assembler-not {\tsub\tz[0-9]+\.b, z[0-9]+\.b, #1\n} } } */
+/* Asserted above { scan-assembler-not {\tsub\tz[0-9]+\.b, z[0-9]+\.b, #1\n} } */
/* { dg-final { scan-assembler-not {\tsub\tz[0-9]+\.b, z[0-9]+\.b, #5\n} } } */
/* { dg-final { scan-assembler-not {\tsub\tz[0-9]+\.b, z[0-9]+\.b, #255\n} } } */
/* { dg-final { scan-assembler-not {\tsub\tz[0-9]+\.b, z[0-9]+\.b, #256\n} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_1.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_1.c
index d0db090..e68d5a4 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_1.c
@@ -38,10 +38,6 @@ TEST_ALL (DEF_LOOP)
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.h, p[0-7]/m, z[0-9]+\.h, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.h, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.s, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.d, #2\.0} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_3.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_3.c
index 741f8f6..0ef8991 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_3.c
@@ -47,8 +47,8 @@ TEST_ALL (DEF_LOOP)
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_5.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_5.c
index 4bae7e0..836cd2c 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_5.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_5.c
@@ -11,10 +11,6 @@
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.h, p[0-7]/m, z[0-9]+\.h, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.h, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.s, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.d, #2\.0} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_7.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_7.c
index 30f07f6..9331d9e 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_7.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmaxnm_7.c
@@ -20,8 +20,8 @@
/* { dg-final { scan-assembler-times {\tfmaxnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_1.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_1.c
index d667b20..f6f5839 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_1.c
@@ -12,10 +12,6 @@
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.h, p[0-7]/m, z[0-9]+\.h, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.h, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.s, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.d, #2\.0} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_3.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_3.c
index d39dd18..01d96ec 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_3.c
@@ -21,8 +21,8 @@
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_5.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_5.c
index 290c4be..9865f08 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_5.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_5.c
@@ -12,10 +12,6 @@
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.h, p[0-7]/m, z[0-9]+\.h, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.s, p[0-7]/m, z[0-9]+\.s, #1\.0\n} 1 } } */
-/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, #1\.0\n} 1 } } */
-
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.h, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.s, #2\.0} 1 } } */
/* { dg-final { scan-assembler-times {\tfmov\tz[0-9]+\.d, #2\.0} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_7.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_7.c
index 347a1a3..eae52cd 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_7.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fminnm_7.c
@@ -21,8 +21,8 @@
/* { dg-final { scan-assembler-times {\tfminnm\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmul_3.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmul_3.c
index 4da147e..549950d 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fmul_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fmul_3.c
@@ -43,8 +43,8 @@ TEST_ALL (DEF_LOOP)
/* { dg-final { scan-assembler-times {\tfmul\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/cond_fsubr_3.c b/gcc/testsuite/gcc.target/aarch64/sve/cond_fsubr_3.c
index 328af57..91eee80 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/cond_fsubr_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/cond_fsubr_3.c
@@ -43,8 +43,8 @@ TEST_ALL (DEF_LOOP)
/* { dg-final { scan-assembler-times {\tfsub\tz[0-9]+\.d, p[0-7]/m, z[0-9]+\.d, z[0-9]+\.d\n} 1 } } */
/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
-/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.h, p[0-7], z[0-9]+\.h, z[0-9]+\.h\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.s, p[0-7], z[0-9]+\.s, z[0-9]+\.s\n} 3 } } */
+/* { dg-final { scan-assembler-times {\tsel\tz[0-9]+\.d, p[0-7], z[0-9]+\.d, z[0-9]+\.d\n} 3 } } */
/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */
/* { dg-final { scan-assembler-not {\tmov\tz} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/mixed_size_6.c b/gcc/testsuite/gcc.target/aarch64/sve/mixed_size_6.c
index 837edec..da77820 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/mixed_size_6.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/mixed_size_6.c
@@ -39,9 +39,9 @@ f3 (uint64_t *restrict ptr1, uint32_t *restrict ptr2, uint32_t start)
}
/* { dg-final { scan-assembler {\tindex\tz[0-9]+\.d, x[0-9]+, #1\n} } } */
-/* { dg-final { scan-assembler {\tindex\tz[0-9]+\.d, x[0-9]+, #1\n} } } */
+/* { dg-final { scan-assembler {\tindex\tz[0-9]+\.d, x[0-9]+, #2\n} } } */
/* { dg-final { scan-assembler {\tindex\tz[0-9]+\.d, x[0-9]+, #4\n} } } */
/* { dg-final { scan-assembler-not {\tindex\tz[0-9]+\.d, w[0-9]+, #1\n} } } */
-/* { dg-final { scan-assembler-not {\tindex\tz[0-9]+\.d, w[0-9]+, #1\n} } } */
+/* { dg-final { scan-assembler-not {\tindex\tz[0-9]+\.d, w[0-9]+, #2\n} } } */
/* { dg-final { scan-assembler-not {\tindex\tz[0-9]+\.d, w[0-9]+, #4\n} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pcs/annotate_1.c b/gcc/testsuite/gcc.target/aarch64/sve/pcs/annotate_1.c
index a85d068..6430980 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/pcs/annotate_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pcs/annotate_1.c
@@ -96,7 +96,6 @@ svfloat64x4_t ret_f64x4 (void) { return svundef4_f64 (); }
/* { dg-final { scan-assembler {\t\.variant_pcs\tret_s8x3\n} } } */
/* { dg-final { scan-assembler {\t\.variant_pcs\tret_s16x3\n} } } */
-/* { dg-final { scan-assembler {\t\.variant_pcs\tret_s16x3\n} } } */
/* { dg-final { scan-assembler {\t\.variant_pcs\tret_s32x3\n} } } */
/* { dg-final { scan-assembler {\t\.variant_pcs\tret_s64x3\n} } } */
/* { dg-final { scan-assembler {\t\.variant_pcs\tret_u8x3\n} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pcs/return_6.c b/gcc/testsuite/gcc.target/aarch64/sve/pcs/return_6.c
index 81c0a41..bf7308d 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/pcs/return_6.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/pcs/return_6.c
@@ -55,7 +55,7 @@ CALLEE (s8, svint8_t)
CALLEE (u8, svuint8_t)
/*
-** callee_u8:
+** callee_mf8:
** (
** ld1 ({v.*}), \[x0\]
** st1 \1, \[x8\]
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/struct_move_3.c b/gcc/testsuite/gcc.target/aarch64/sve/struct_move_3.c
index 1901138..d6092e7 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/struct_move_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/struct_move_3.c
@@ -37,7 +37,7 @@ typedef struct { vnx2df a[4]; } vnx8df;
TEST_TYPE (vnx64qi, z0, z4)
TEST_TYPE (vnx32hi, z6, z2)
TEST_TYPE (vnx16si, z12, z16)
-TEST_TYPE (vnx8di, z17, z13)
+TEST_TYPE (vnx8di, z17, z12)
TEST_TYPE (vnx32hf, z18, z1)
TEST_TYPE (vnx16sf, z20, z16)
TEST_TYPE (vnx8df, z24, z28)
@@ -92,11 +92,11 @@ TEST_TYPE (vnx8df, z24, z28)
/* { dg-final { scan-assembler {\tld1d\tz19.d, p[0-7]/z, \[x0, #2, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tld1d\tz20.d, p[0-7]/z, \[x0, #3, mul vl\]\n} } } */
/* { dg-final { scan-assembler { test vnx8di 1 z17\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz13.d, z17.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz14.d, z18.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz15.d, z19.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz16.d, z20.d\n} } } */
-/* { dg-final { scan-assembler { test vnx8di 2 z17, z17, z13\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz12.d, z17.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz13.d, z18.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz14.d, z19.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz15.d, z20.d\n} } } */
+/* { dg-final { scan-assembler { test vnx8di 2 z17, z17, z12\n} } } */
/* { dg-final { scan-assembler {\tst1d\tz17.d, p[0-7], \[x0, #4, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tst1d\tz18.d, p[0-7], \[x0, #5, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tst1d\tz19.d, p[0-7], \[x0, #6, mul vl\]\n} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/struct_move_6.c b/gcc/testsuite/gcc.target/aarch64/sve/struct_move_6.c
index 8336e3f..12b7144 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/struct_move_6.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/struct_move_6.c
@@ -34,7 +34,7 @@ typedef struct { vnx2df a[4]; } vnx8df;
TEST_TYPE (vnx64qi, z0, z4)
TEST_TYPE (vnx32hi, z6, z2)
TEST_TYPE (vnx16si, z12, z16)
-TEST_TYPE (vnx8di, z17, z13)
+TEST_TYPE (vnx8di, z17, z12)
TEST_TYPE (vnx16sf, z20, z16)
TEST_TYPE (vnx8df, z24, z28)
@@ -88,11 +88,11 @@ TEST_TYPE (vnx8df, z24, z28)
/* { dg-final { scan-assembler {\tldr\tz19, \[x0, #2, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tldr\tz20, \[x0, #3, mul vl\]\n} } } */
/* { dg-final { scan-assembler { test vnx8di 1 z17\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz13.d, z17.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz14.d, z18.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz15.d, z19.d\n} } } */
-/* { dg-final { scan-assembler {\tmov\tz16.d, z20.d\n} } } */
-/* { dg-final { scan-assembler { test vnx8di 2 z17, z17, z13\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz12.d, z17.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz13.d, z18.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz14.d, z19.d\n} } } */
+/* { dg-final { scan-assembler {\tmov\tz15.d, z20.d\n} } } */
+/* { dg-final { scan-assembler { test vnx8di 2 z17, z17, z12\n} } } */
/* { dg-final { scan-assembler {\tstr\tz17, \[x0, #4, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tstr\tz18, \[x0, #5, mul vl\]\n} } } */
/* { dg-final { scan-assembler {\tstr\tz19, \[x0, #6, mul vl\]\n} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/uzp1_1.c b/gcc/testsuite/gcc.target/aarch64/sve/uzp1_1.c
index 84c6c6f..83451db 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/uzp1_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/uzp1_1.c
@@ -32,9 +32,6 @@ UZP1 (vnx8hf, ((vnx8hi) { 0, 2, 4, 6, 8, 10, 12, 14,
16, 18, 20, 22, 24, 26, 28, 30 }));
/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
/* { dg-final { scan-assembler-times {\tuzp1\tz[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d\n} 2 } } */
/* { dg-final { scan-assembler-times {\tuzp1\tz[0-9]+\.s, z[0-9]+\.s, z[0-9]+\.s\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/uzp2_1.c b/gcc/testsuite/gcc.target/aarch64/sve/uzp2_1.c
index 1336caf..bfdee40 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/uzp2_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/uzp2_1.c
@@ -31,9 +31,6 @@ UZP2 (vnx8hf, ((vnx8hi) { 1, 3, 5, 7, 9, 11, 13, 15,
17, 19, 21, 23, 25, 27, 29, 31 }));
/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
-/* { dg-final { scan-assembler-not {\ttbl\t} } } */
/* { dg-final { scan-assembler-times {\tuzp2\tz[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d\n} 2 } } */
/* { dg-final { scan-assembler-times {\tuzp2\tz[0-9]+\.s, z[0-9]+\.s, z[0-9]+\.s\n} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/vector-compare-5.c b/gcc/testsuite/gcc.target/aarch64/vector-compare-5.c
index a1a601d..7becd56 100644
--- a/gcc/testsuite/gcc.target/aarch64/vector-compare-5.c
+++ b/gcc/testsuite/gcc.target/aarch64/vector-compare-5.c
@@ -53,15 +53,13 @@ n (v4i *x, v4i const *y, v4i *z, v4i *t)
}
+/* { dg-final { scan-tree-dump-times "\\s*\\*tD\\.\\d+\\s*=\\s*\\{\\s*-1(?:,\\s*-1){3}\\s*\\}\\s*;" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "\\s*\\*tD\\.\\d+\\s*=\\s*\\{\\s*0(?:,\\s*0){3}\\s*\\}\\s*;" 3 "original" } } */
+/* { dg-final { scan-tree-dump-times "\\s*\\*zD\\.\\d+\\s*=\\s*\\{\\s*-1(?:,\\s*-1){3}\\s*\\}\\s*;" 2 "original" } } */
+
/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*>=\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*;" "original" } } */
/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*==\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*<\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*;" "original" } } */
/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*<=\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*;" "original" } } */
/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*!=\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*VEC_COND_EXPR\\s*<\\s*\\*xD\\.\\d+\\s*>=\\s*VIEW_CONVERT_EXPR<v4iD\\.\\d+>\\(\\*yD\\.\\d+\\)\\s*,\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*,\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*>\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*zD\\.\\d+\\s*=\\s*\\{\\s*-1(,\\s*-1){3}\\s*\\}\\s*;" "original" } } */
-/* { dg-final { scan-tree-dump ".*\\*tD\\.\\d+\\s*=\\s*\\{\\s*0(,\\s*0){3}\\s*\\}\\s*;" "original" } } */
diff --git a/gcc/testsuite/gcc.target/riscv/pr121937.c b/gcc/testsuite/gcc.target/riscv/pr121937.c
new file mode 100644
index 0000000..3c0389c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr121937.c
@@ -0,0 +1,66 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-w -march=rv64gcv -mabi=lp64d" { target rv64 } } */
+/* { dg-additional-options "-w -march=rv32gcv -mabi=ilp32" { target rv32 } } */
+
+#include <stdint-gcc.h>
+#define BS_VEC(type, num) type __attribute__((vector_size(num * sizeof(type))))
+typedef int16_t int16;
+typedef uint16_t uint16;
+typedef int32_t int32;
+typedef uint64_t uint64;
+int32_t g_69, g_539;
+int32_t *g_68;
+void func_59(int32_t p_60) {
+ BS_VEC(uint64, 2) BS_VAR_4;
+ BS_VEC(int16, 8) BS_VAR_6;
+ uint64 *LOCAL_CHECKSUM;
+ int32_t *l_108 = &g_69;
+ int64_t l_829 = 10;
+ int32_t l_844 = -1;
+ for (; g_69;) {
+ int32_t l_924;
+ if (p_60 * 2u) {
+ BS_LABEL_0:
+ *LOCAL_CHECKSUM ^= BS_VAR_4[3];
+ for (l_924 = 3; l_924; l_924 -= 1) {
+ BS_VEC(uint64, 8)
+ BS_TEMP_600 = -__builtin_convertvector(BS_VAR_6, BS_VEC(uint64, 8));
+ BS_VEC(uint64, 8)
+ BS_TEMP_601 = __builtin_convertvector((BS_VEC(int32, 8)){p_60},
+ BS_VEC(uint64, 8));
+ BS_VAR_4[356358257141730375] =
+ __builtin_convertvector(
+ __builtin_shufflevector((BS_VEC(uint16, 2))0,
+ (BS_VEC(uint16, 2))0, 1, 3, 0, 1, 2, 0,
+ 0, 2, 0, 0, 1, 2, 3, 3, 3, 2),
+ BS_VEC(uint64, 16))[BS_VAR_6[4]] >
+ (BS_VEC(uint64, 8)){0, BS_TEMP_600[1] ? BS_TEMP_601[1]
+ : 0}[l_829 != 0];
+ }
+ }
+ if (*l_108)
+ *g_68 |= g_539;
+ __asm goto("" : : : : BS_LABEL_0);
+ BS_VEC(int16, 4)
+ BS_TEMP_681 = __builtin_shufflevector(
+ (BS_VEC(int16, 2))__builtin_shufflevector(
+ __builtin_convertvector(
+ __builtin_shufflevector(BS_VAR_6, BS_VAR_6, 8, 6, 5, 8, 1, 3, 6,
+ 2, 0, 1, 2, 5, 8, 6, 5, 1, 5, 0, 3, 5,
+ 8, 2, 2, 4, 6, 0, 6, 4, 3, 3, 1, 2),
+ BS_VEC(uint16, 32)),
+ __builtin_convertvector((BS_VEC(int32, 32)){}, BS_VEC(uint16, 32)),
+ 42, 52) -
+ __builtin_convertvector((BS_VEC(int32, 2)){l_844},
+ BS_VEC(uint16, 2)) *
+ ~0,
+ ~(0 < __builtin_shufflevector(
+ __builtin_convertvector((BS_VEC(int32, 16)){p_60},
+ BS_VEC(uint16, 16)),
+ (BS_VEC(uint16, 16)){20489, 3, 2, 4}, 19, 6)),
+ 1, 2, 0, 3);
+ BS_VAR_6[0] =
+ BS_TEMP_681[0] ^ BS_TEMP_681[1] ^ BS_TEMP_681[2] ^ BS_TEMP_681[3];
+ }
+}
+
diff --git a/gcc/testsuite/gcc.target/riscv/pr122051.c b/gcc/testsuite/gcc.target/riscv/pr122051.c
new file mode 100644
index 0000000..c2f4b87
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr122051.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-additional-options "-mrvv-vector-bits=zvl -mcpu=xt-c920 -w" } */
+
+typedef __attribute__((__vector_size__(4))) char B;
+typedef __attribute__((__vector_size__(16))) long V;
+typedef __attribute__((__vector_size__(32))) double W;
+typedef __attribute__((__vector_size__(32))) char U;
+unsigned u;
+B o;
+char *p;
+int q;
+V v;
+W w;
+
+void
+foo(__int128, __int128, __int128, __int128, B a, B b, B c, B d, B e, B f, B g, B h) {
+ do {
+ w -= q;
+ v ^= u;
+ } while (__builtin_memcmp(p, 1 + p, 7));
+ o = ((U)w)[0] + c + d + e + f + g + h + a + b;
+}
+
+
diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c
index 89af160..bb62ce2 100644
--- a/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c
+++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/vls/math-nearbyint-1.c
@@ -54,5 +54,5 @@ DEF_OP_V (nearbyint, 512, double, __builtin_nearbyint)
/* { dg-final { scan-tree-dump-not "4096,4096" "optimized" } } */
/* { dg-final { scan-assembler-times {vfcvt\.x\.f\.v\s+v[0-9]+,\s*v[0-9]+,\s*v0\.t} 30 } } */
/* { dg-final { scan-assembler-times {vfcvt\.f\.x\.v\s+v[0-9]+,\s*v[0-9]+,\s*v0\.t} 30 } } */
-/* { dg-final { scan-assembler-times {frflags\s+[atx][0-9]+} 32 } } */
-/* { dg-final { scan-assembler-times {fsflags\s+[atx][0-9]+} 32 } } */
+/* { dg-final { scan-assembler-times {frflags\s+[atx][0-9]+} 30 } } */
+/* { dg-final { scan-assembler-times {fsflags\s+[atx][0-9]+} 30 } } */
diff --git a/gcc/testsuite/gfortran.dg/pdt_52.f03 b/gcc/testsuite/gfortran.dg/pdt_52.f03
new file mode 100644
index 0000000..5acdecb
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pdt_52.f03
@@ -0,0 +1,36 @@
+! { dg-do compile }
+!
+! Test the fix for PR122089 in which an error occured in compiling the module
+! because a spurious REAL(KIND=0) was being produced for 'values_'.
+!
+! Other failures are indicated by the comments. For reasons that are not to me,
+! they didn't fail when combined with this test.
+!
+! Contributed by Damian Rouson <damian@archaeologic.codes>
+!
+module tensor_m
+ implicit none
+
+ type tensor_t(k)
+ integer, kind :: k = kind(1.)
+ real(k), allocatable :: values_ ! ICE if not allocatable
+ end type
+
+ type input_output_pair_t(k)
+ integer, kind :: k
+ type(tensor_t(k)) inputs_, expected_outputs_ ! ICE if 2nd component dropped
+ end type
+
+ type mini_batch_t(k)
+ integer, kind :: k
+ type(input_output_pair_t(k)) input_output_pairs_
+ end type
+
+end module tensor_m
+
+ use tensor_m
+ type (mini_batch_t(k = kind(1d0))) :: x
+ allocate (x%input_output_pairs_%inputs_%values_, source = 42d0)
+ print *, kind (x%input_output_pairs_%inputs_%values_), x%input_output_pairs_%inputs_%values_
+ deallocate (x%input_output_pairs_%inputs_%values_)
+end
diff --git a/gcc/testsuite/gfortran.dg/pdt_53.f03 b/gcc/testsuite/gfortran.dg/pdt_53.f03
new file mode 100644
index 0000000..9f3b4ca
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pdt_53.f03
@@ -0,0 +1,28 @@
+! { dg-do compile }
+!
+! Test the fix for PR122089 in which an error occured in compiling the module
+! because a spurious REAL(KIND=0) was being produced for 'values_'.
+!
+! This is a variant of pdt_52.f03. See the comments in that test.
+!
+! Contributed by Damian Rouson <damian@archaeologic.codes>
+!
+module tensor_m
+ implicit none
+
+ type tensor_t(k)
+ integer, kind :: k = kind(1.)
+ real(k) :: values_ ! Used to ICE
+ end type
+
+ type input_output_pair_t(k)
+ integer, kind :: k
+ type(tensor_t(k)) inputs_, expected_outputs_
+ end type
+
+ type mini_batch_t(k)
+ integer, kind :: k
+ type(input_output_pair_t(k)) input_output_pairs_
+ end type
+
+end module tensor_m
diff --git a/gcc/testsuite/gfortran.dg/pdt_54.f03 b/gcc/testsuite/gfortran.dg/pdt_54.f03
new file mode 100644
index 0000000..9631dad
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pdt_54.f03
@@ -0,0 +1,28 @@
+! { dg-do compile }
+!
+! Test the fix for PR122089 in which an error occured in compiling the module
+! because a spurious REAL(KIND=0) was being produced for 'values_'.
+!
+! This is a variant of pdt_52.f03. See the comments in that test.
+!
+! Contributed by Damian Rouson <damian@archaeologic.codes>
+!
+module tensor_m
+ implicit none
+
+ type tensor_t(k)
+ integer, kind :: k = kind(1.)
+ real(k), allocatable :: values_
+ end type
+
+ type input_output_pair_t(k)
+ integer, kind :: k
+ type(tensor_t(k)) inputs_ ! Used to ICE if 2nd component dropped
+ end type
+
+ type mini_batch_t(k)
+ integer, kind :: k
+ type(input_output_pair_t(k)) input_output_pairs_
+ end type
+
+end module tensor_m
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 61cec52..410341d 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -447,7 +447,6 @@ extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_recursion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
-extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_stdarg (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_early_warn_uninitialized (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-ccp.cc b/gcc/tree-ssa-ccp.cc
index 4165e59..6de02e5 100644
--- a/gcc/tree-ssa-ccp.cc
+++ b/gcc/tree-ssa-ccp.cc
@@ -3085,1448 +3085,6 @@ make_pass_ccp (gcc::context *ctxt)
return new pass_ccp (ctxt);
}
-
-
-/* Try to optimize out __builtin_stack_restore. Optimize it out
- if there is another __builtin_stack_restore in the same basic
- block and no calls or ASM_EXPRs are in between, or if this block's
- only outgoing edge is to EXIT_BLOCK and there are no calls or
- ASM_EXPRs after this __builtin_stack_restore. */
-
-static tree
-optimize_stack_restore (gimple_stmt_iterator i)
-{
- tree callee;
- gimple *stmt;
-
- basic_block bb = gsi_bb (i);
- gimple *call = gsi_stmt (i);
-
- if (gimple_code (call) != GIMPLE_CALL
- || gimple_call_num_args (call) != 1
- || TREE_CODE (gimple_call_arg (call, 0)) != SSA_NAME
- || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
- return NULL_TREE;
-
- for (gsi_next (&i); !gsi_end_p (i); gsi_next (&i))
- {
- stmt = gsi_stmt (i);
- if (gimple_code (stmt) == GIMPLE_ASM)
- return NULL_TREE;
- if (gimple_code (stmt) != GIMPLE_CALL)
- continue;
-
- callee = gimple_call_fndecl (stmt);
- if (!callee
- || !fndecl_built_in_p (callee, BUILT_IN_NORMAL)
- /* All regular builtins are ok, just obviously not alloca. */
- || ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callee))
- /* Do not remove stack updates before strub leave. */
- || fndecl_built_in_p (callee, BUILT_IN___STRUB_LEAVE))
- return NULL_TREE;
-
- if (fndecl_built_in_p (callee, BUILT_IN_STACK_RESTORE))
- goto second_stack_restore;
- }
-
- if (!gsi_end_p (i))
- return NULL_TREE;
-
- /* Allow one successor of the exit block, or zero successors. */
- switch (EDGE_COUNT (bb->succs))
- {
- case 0:
- break;
- case 1:
- if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
- return NULL_TREE;
- break;
- default:
- return NULL_TREE;
- }
- second_stack_restore:
-
- /* If there's exactly one use, then zap the call to __builtin_stack_save.
- If there are multiple uses, then the last one should remove the call.
- In any case, whether the call to __builtin_stack_save can be removed
- or not is irrelevant to removing the call to __builtin_stack_restore. */
- if (has_single_use (gimple_call_arg (call, 0)))
- {
- gimple *stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
- if (is_gimple_call (stack_save))
- {
- callee = gimple_call_fndecl (stack_save);
- if (callee && fndecl_built_in_p (callee, BUILT_IN_STACK_SAVE))
- {
- gimple_stmt_iterator stack_save_gsi;
- tree rhs;
-
- stack_save_gsi = gsi_for_stmt (stack_save);
- rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
- replace_call_with_value (&stack_save_gsi, rhs);
- }
- }
- }
-
- /* No effect, so the statement will be deleted. */
- return integer_zero_node;
-}
-
-/* If va_list type is a simple pointer and nothing special is needed,
- optimize __builtin_va_start (&ap, 0) into ap = __builtin_next_arg (0),
- __builtin_va_end (&ap) out as NOP and __builtin_va_copy into a simple
- pointer assignment. */
-
-static tree
-optimize_stdarg_builtin (gimple *call)
-{
- tree callee, lhs, rhs, cfun_va_list;
- bool va_list_simple_ptr;
- location_t loc = gimple_location (call);
-
- callee = gimple_call_fndecl (call);
-
- cfun_va_list = targetm.fn_abi_va_list (callee);
- va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list)
- && (TREE_TYPE (cfun_va_list) == void_type_node
- || TREE_TYPE (cfun_va_list) == char_type_node);
-
- switch (DECL_FUNCTION_CODE (callee))
- {
- case BUILT_IN_VA_START:
- if (!va_list_simple_ptr
- || targetm.expand_builtin_va_start != NULL
- || !builtin_decl_explicit_p (BUILT_IN_NEXT_ARG))
- return NULL_TREE;
-
- if (gimple_call_num_args (call) != 2)
- return NULL_TREE;
-
- lhs = gimple_call_arg (call, 0);
- if (!POINTER_TYPE_P (TREE_TYPE (lhs))
- || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
- != TYPE_MAIN_VARIANT (cfun_va_list))
- return NULL_TREE;
-
- lhs = build_fold_indirect_ref_loc (loc, lhs);
- rhs = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_NEXT_ARG),
- 1, integer_zero_node);
- rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
- return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
-
- case BUILT_IN_VA_COPY:
- if (!va_list_simple_ptr)
- return NULL_TREE;
-
- if (gimple_call_num_args (call) != 2)
- return NULL_TREE;
-
- lhs = gimple_call_arg (call, 0);
- if (!POINTER_TYPE_P (TREE_TYPE (lhs))
- || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
- != TYPE_MAIN_VARIANT (cfun_va_list))
- return NULL_TREE;
-
- lhs = build_fold_indirect_ref_loc (loc, lhs);
- rhs = gimple_call_arg (call, 1);
- if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs))
- != TYPE_MAIN_VARIANT (cfun_va_list))
- return NULL_TREE;
-
- rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
- return build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
-
- case BUILT_IN_VA_END:
- /* No effect, so the statement will be deleted. */
- return integer_zero_node;
-
- default:
- gcc_unreachable ();
- }
-}
-
-/* Attemp to make the block of __builtin_unreachable I unreachable by changing
- the incoming jumps. Return true if at least one jump was changed. */
-
-static bool
-optimize_unreachable (gimple_stmt_iterator i)
-{
- basic_block bb = gsi_bb (i);
- gimple_stmt_iterator gsi;
- gimple *stmt;
- edge_iterator ei;
- edge e;
- bool ret;
-
- if (flag_sanitize & SANITIZE_UNREACHABLE)
- return false;
- gsi = gsi_start_nondebug_after_labels_bb (bb);
- /* Only handle the case that __builtin_unreachable is the first
- statement in the block. We rely on DCE to remove stmts
- without side-effects before __builtin_unreachable. */
- if (*gsi != *i)
- return false;
-
- ret = false;
- FOR_EACH_EDGE (e, ei, bb->preds)
- {
- gsi = gsi_last_bb (e->src);
- if (gsi_end_p (gsi))
- continue;
-
- stmt = gsi_stmt (gsi);
- if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
- {
- if (e->flags & EDGE_TRUE_VALUE)
- gimple_cond_make_false (cond_stmt);
- else if (e->flags & EDGE_FALSE_VALUE)
- gimple_cond_make_true (cond_stmt);
- else
- gcc_unreachable ();
- update_stmt (cond_stmt);
- }
- else
- {
- /* Todo: handle other cases. Note that unreachable switch case
- statements have already been removed. */
- continue;
- }
-
- ret = true;
- }
-
- return ret;
-}
-
-/* Convert
- _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
- _7 = ~_1;
- _5 = (_Bool) _7;
- to
- _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
- _8 = _1 & 1;
- _5 = _8 == 0;
- and convert
- _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
- _7 = ~_1;
- _4 = (_Bool) _7;
- to
- _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
- _8 = _1 & 1;
- _4 = (_Bool) _8;
-
- USE_STMT is the gimplt statement which uses the return value of
- __atomic_fetch_or_*. LHS is the return value of __atomic_fetch_or_*.
- MASK is the mask passed to __atomic_fetch_or_*.
- */
-
-static gimple *
-convert_atomic_bit_not (enum internal_fn fn, gimple *use_stmt,
- tree lhs, tree mask)
-{
- tree and_mask;
- if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
- {
- /* MASK must be ~1. */
- if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs),
- ~HOST_WIDE_INT_1), mask, 0))
- return nullptr;
- and_mask = build_int_cst (TREE_TYPE (lhs), 1);
- }
- else
- {
- /* MASK must be 1. */
- if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs), 1), mask, 0))
- return nullptr;
- and_mask = mask;
- }
-
- tree use_lhs = gimple_assign_lhs (use_stmt);
-
- use_operand_p use_p;
- gimple *use_not_stmt;
-
- if (!single_imm_use (use_lhs, &use_p, &use_not_stmt)
- || !is_gimple_assign (use_not_stmt))
- return nullptr;
-
- if (!CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_not_stmt)))
- return nullptr;
-
- tree use_not_lhs = gimple_assign_lhs (use_not_stmt);
- if (TREE_CODE (TREE_TYPE (use_not_lhs)) != BOOLEAN_TYPE)
- return nullptr;
-
- gimple_stmt_iterator gsi;
- tree var = make_ssa_name (TREE_TYPE (lhs));
- /* use_stmt need to be removed after use_nop_stmt,
- so use_lhs can be released. */
- gimple *use_stmt_removal = use_stmt;
- use_stmt = gimple_build_assign (var, BIT_AND_EXPR, lhs, and_mask);
- gsi = gsi_for_stmt (use_not_stmt);
- gsi_insert_before (&gsi, use_stmt, GSI_NEW_STMT);
- lhs = gimple_assign_lhs (use_not_stmt);
- gimple *g = gimple_build_assign (lhs, EQ_EXPR, var,
- build_zero_cst (TREE_TYPE (mask)));
- gsi_insert_after (&gsi, g, GSI_NEW_STMT);
- gsi = gsi_for_stmt (use_not_stmt);
- gsi_remove (&gsi, true);
- gsi = gsi_for_stmt (use_stmt_removal);
- gsi_remove (&gsi, true);
- return use_stmt;
-}
-
-/* match.pd function to match atomic_bit_test_and pattern which
- has nop_convert:
- _1 = __atomic_fetch_or_4 (&v, 1, 0);
- _2 = (int) _1;
- _5 = _2 & 1;
- */
-extern bool gimple_nop_atomic_bit_test_and_p (tree, tree *,
- tree (*) (tree));
-extern bool gimple_nop_convert (tree, tree*, tree (*) (tree));
-
-/* Optimize
- mask_2 = 1 << cnt_1;
- _4 = __atomic_fetch_or_* (ptr_6, mask_2, _3);
- _5 = _4 & mask_2;
- to
- _4 = .ATOMIC_BIT_TEST_AND_SET (ptr_6, cnt_1, 0, _3);
- _5 = _4;
- If _5 is only used in _5 != 0 or _5 == 0 comparisons, 1
- is passed instead of 0, and the builtin just returns a zero
- or 1 value instead of the actual bit.
- Similarly for __sync_fetch_and_or_* (without the ", _3" part
- in there), and/or if mask_2 is a power of 2 constant.
- Similarly for xor instead of or, use ATOMIC_BIT_TEST_AND_COMPLEMENT
- in that case. And similarly for and instead of or, except that
- the second argument to the builtin needs to be one's complement
- of the mask instead of mask. */
-
-static bool
-optimize_atomic_bit_test_and (gimple_stmt_iterator *gsip,
- enum internal_fn fn, bool has_model_arg,
- bool after)
-{
- gimple *call = gsi_stmt (*gsip);
- tree lhs = gimple_call_lhs (call);
- use_operand_p use_p;
- gimple *use_stmt;
- tree mask;
- optab optab;
-
- if (!flag_inline_atomics
- || optimize_debug
- || !gimple_call_builtin_p (call, BUILT_IN_NORMAL)
- || !lhs
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)
- || !single_imm_use (lhs, &use_p, &use_stmt)
- || !is_gimple_assign (use_stmt)
- || !gimple_vdef (call))
- return false;
-
- switch (fn)
- {
- case IFN_ATOMIC_BIT_TEST_AND_SET:
- optab = atomic_bit_test_and_set_optab;
- break;
- case IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT:
- optab = atomic_bit_test_and_complement_optab;
- break;
- case IFN_ATOMIC_BIT_TEST_AND_RESET:
- optab = atomic_bit_test_and_reset_optab;
- break;
- default:
- return false;
- }
-
- tree bit = nullptr;
-
- mask = gimple_call_arg (call, 1);
- tree_code rhs_code = gimple_assign_rhs_code (use_stmt);
- if (rhs_code != BIT_AND_EXPR)
- {
- if (rhs_code != NOP_EXPR && rhs_code != BIT_NOT_EXPR)
- return false;
-
- tree use_lhs = gimple_assign_lhs (use_stmt);
- if (TREE_CODE (use_lhs) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs))
- return false;
-
- tree use_rhs = gimple_assign_rhs1 (use_stmt);
- if (lhs != use_rhs)
- return false;
-
- if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
- == CODE_FOR_nothing)
- return false;
-
- gimple *g;
- gimple_stmt_iterator gsi;
- tree var;
- int ibit = -1;
-
- if (rhs_code == BIT_NOT_EXPR)
- {
- g = convert_atomic_bit_not (fn, use_stmt, lhs, mask);
- if (!g)
- return false;
- use_stmt = g;
- ibit = 0;
- }
- else if (TREE_CODE (TREE_TYPE (use_lhs)) == BOOLEAN_TYPE)
- {
- tree and_mask;
- if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
- {
- /* MASK must be ~1. */
- if (!operand_equal_p (build_int_cst (TREE_TYPE (lhs),
- ~HOST_WIDE_INT_1),
- mask, 0))
- return false;
-
- /* Convert
- _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
- _4 = (_Bool) _1;
- to
- _1 = __atomic_fetch_and_* (ptr_6, ~1, _3);
- _5 = _1 & 1;
- _4 = (_Bool) _5;
- */
- and_mask = build_int_cst (TREE_TYPE (lhs), 1);
- }
- else
- {
- and_mask = build_int_cst (TREE_TYPE (lhs), 1);
- if (!operand_equal_p (and_mask, mask, 0))
- return false;
-
- /* Convert
- _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
- _4 = (_Bool) _1;
- to
- _1 = __atomic_fetch_or_* (ptr_6, 1, _3);
- _5 = _1 & 1;
- _4 = (_Bool) _5;
- */
- }
- var = make_ssa_name (TREE_TYPE (use_rhs));
- replace_uses_by (use_rhs, var);
- g = gimple_build_assign (var, BIT_AND_EXPR, use_rhs,
- and_mask);
- gsi = gsi_for_stmt (use_stmt);
- gsi_insert_before (&gsi, g, GSI_NEW_STMT);
- use_stmt = g;
- ibit = 0;
- }
- else if (TYPE_PRECISION (TREE_TYPE (use_lhs))
- <= TYPE_PRECISION (TREE_TYPE (use_rhs)))
- {
- gimple *use_nop_stmt;
- if (!single_imm_use (use_lhs, &use_p, &use_nop_stmt)
- || (!is_gimple_assign (use_nop_stmt)
- && gimple_code (use_nop_stmt) != GIMPLE_COND))
- return false;
- /* Handle both
- _4 = _5 < 0;
- and
- if (_5 < 0)
- */
- tree use_nop_lhs = nullptr;
- rhs_code = ERROR_MARK;
- if (is_gimple_assign (use_nop_stmt))
- {
- use_nop_lhs = gimple_assign_lhs (use_nop_stmt);
- rhs_code = gimple_assign_rhs_code (use_nop_stmt);
- }
- if (!use_nop_lhs || rhs_code != BIT_AND_EXPR)
- {
- /* Also handle
- if (_5 < 0)
- */
- if (use_nop_lhs
- && TREE_CODE (use_nop_lhs) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_nop_lhs))
- return false;
- if (use_nop_lhs && rhs_code == BIT_NOT_EXPR)
- {
- /* Handle
- _7 = ~_2;
- */
- g = convert_atomic_bit_not (fn, use_nop_stmt, lhs,
- mask);
- if (!g)
- return false;
- /* Convert
- _1 = __atomic_fetch_or_4 (ptr_6, 1, _3);
- _2 = (int) _1;
- _7 = ~_2;
- _5 = (_Bool) _7;
- to
- _1 = __atomic_fetch_or_4 (ptr_6, ~1, _3);
- _8 = _1 & 1;
- _5 = _8 == 0;
- and convert
- _1 = __atomic_fetch_and_4 (ptr_6, ~1, _3);
- _2 = (int) _1;
- _7 = ~_2;
- _5 = (_Bool) _7;
- to
- _1 = __atomic_fetch_and_4 (ptr_6, 1, _3);
- _8 = _1 & 1;
- _5 = _8 == 0;
- */
- gsi = gsi_for_stmt (use_stmt);
- gsi_remove (&gsi, true);
- use_stmt = g;
- ibit = 0;
- }
- else
- {
- tree cmp_rhs1, cmp_rhs2;
- if (use_nop_lhs)
- {
- /* Handle
- _4 = _5 < 0;
- */
- if (TREE_CODE (TREE_TYPE (use_nop_lhs))
- != BOOLEAN_TYPE)
- return false;
- cmp_rhs1 = gimple_assign_rhs1 (use_nop_stmt);
- cmp_rhs2 = gimple_assign_rhs2 (use_nop_stmt);
- }
- else
- {
- /* Handle
- if (_5 < 0)
- */
- rhs_code = gimple_cond_code (use_nop_stmt);
- cmp_rhs1 = gimple_cond_lhs (use_nop_stmt);
- cmp_rhs2 = gimple_cond_rhs (use_nop_stmt);
- }
- if (rhs_code != GE_EXPR && rhs_code != LT_EXPR)
- return false;
- if (use_lhs != cmp_rhs1)
- return false;
- if (!integer_zerop (cmp_rhs2))
- return false;
-
- tree and_mask;
-
- unsigned HOST_WIDE_INT bytes
- = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (use_rhs)));
- ibit = bytes * BITS_PER_UNIT - 1;
- unsigned HOST_WIDE_INT highest
- = HOST_WIDE_INT_1U << ibit;
-
- if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
- {
- /* Get the signed maximum of the USE_RHS type. */
- and_mask = build_int_cst (TREE_TYPE (use_rhs),
- highest - 1);
- if (!operand_equal_p (and_mask, mask, 0))
- return false;
-
- /* Convert
- _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
- _5 = (signed int) _1;
- _4 = _5 < 0 or _5 >= 0;
- to
- _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
- _6 = _1 & 0x80000000;
- _4 = _6 != 0 or _6 == 0;
- and convert
- _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
- _5 = (signed int) _1;
- if (_5 < 0 or _5 >= 0)
- to
- _1 = __atomic_fetch_and_4 (ptr_6, 0x7fffffff, _3);
- _6 = _1 & 0x80000000;
- if (_6 != 0 or _6 == 0)
- */
- and_mask = build_int_cst (TREE_TYPE (use_rhs),
- highest);
- }
- else
- {
- /* Get the signed minimum of the USE_RHS type. */
- and_mask = build_int_cst (TREE_TYPE (use_rhs),
- highest);
- if (!operand_equal_p (and_mask, mask, 0))
- return false;
-
- /* Convert
- _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
- _5 = (signed int) _1;
- _4 = _5 < 0 or _5 >= 0;
- to
- _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
- _6 = _1 & 0x80000000;
- _4 = _6 != 0 or _6 == 0;
- and convert
- _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
- _5 = (signed int) _1;
- if (_5 < 0 or _5 >= 0)
- to
- _1 = __atomic_fetch_or_4 (ptr_6, 0x80000000, _3);
- _6 = _1 & 0x80000000;
- if (_6 != 0 or _6 == 0)
- */
- }
- var = make_ssa_name (TREE_TYPE (use_rhs));
- gimple* use_stmt_removal = use_stmt;
- g = gimple_build_assign (var, BIT_AND_EXPR, use_rhs,
- and_mask);
- gsi = gsi_for_stmt (use_nop_stmt);
- gsi_insert_before (&gsi, g, GSI_NEW_STMT);
- use_stmt = g;
- rhs_code = rhs_code == GE_EXPR ? EQ_EXPR : NE_EXPR;
- tree const_zero = build_zero_cst (TREE_TYPE (use_rhs));
- if (use_nop_lhs)
- g = gimple_build_assign (use_nop_lhs, rhs_code,
- var, const_zero);
- else
- g = gimple_build_cond (rhs_code, var, const_zero,
- nullptr, nullptr);
- gsi_insert_after (&gsi, g, GSI_NEW_STMT);
- gsi = gsi_for_stmt (use_nop_stmt);
- gsi_remove (&gsi, true);
- gsi = gsi_for_stmt (use_stmt_removal);
- gsi_remove (&gsi, true);
- }
- }
- else
- {
- tree match_op[3];
- gimple *g;
- if (!gimple_nop_atomic_bit_test_and_p (use_nop_lhs,
- &match_op[0], NULL)
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (match_op[2])
- || !single_imm_use (match_op[2], &use_p, &g)
- || !is_gimple_assign (g))
- return false;
- mask = match_op[0];
- if (TREE_CODE (match_op[1]) == INTEGER_CST)
- {
- ibit = tree_log2 (match_op[1]);
- gcc_assert (ibit >= 0);
- }
- else
- {
- g = SSA_NAME_DEF_STMT (match_op[1]);
- gcc_assert (is_gimple_assign (g));
- bit = gimple_assign_rhs2 (g);
- }
- /* Convert
- _1 = __atomic_fetch_or_4 (ptr_6, mask, _3);
- _2 = (int) _1;
- _5 = _2 & mask;
- to
- _1 = __atomic_fetch_or_4 (ptr_6, mask, _3);
- _6 = _1 & mask;
- _5 = (int) _6;
- and convert
- _1 = ~mask_7;
- _2 = (unsigned int) _1;
- _3 = __atomic_fetch_and_4 (ptr_6, _2, 0);
- _4 = (int) _3;
- _5 = _4 & mask_7;
- to
- _1 = __atomic_fetch_and_* (ptr_6, ~mask_7, _3);
- _12 = _3 & mask_7;
- _5 = (int) _12;
-
- and Convert
- _1 = __atomic_fetch_and_4 (ptr_6, ~mask, _3);
- _2 = (short int) _1;
- _5 = _2 & mask;
- to
- _1 = __atomic_fetch_and_4 (ptr_6, ~mask, _3);
- _8 = _1 & mask;
- _5 = (short int) _8;
- */
- gimple_seq stmts = NULL;
- match_op[1] = gimple_convert (&stmts,
- TREE_TYPE (use_rhs),
- match_op[1]);
- var = gimple_build (&stmts, BIT_AND_EXPR,
- TREE_TYPE (use_rhs), use_rhs, match_op[1]);
- gsi = gsi_for_stmt (use_stmt);
- gsi_remove (&gsi, true);
- release_defs (use_stmt);
- use_stmt = gimple_seq_last_stmt (stmts);
- gsi = gsi_for_stmt (use_nop_stmt);
- gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
- gimple_assign_set_rhs_with_ops (&gsi, CONVERT_EXPR, var);
- update_stmt (use_nop_stmt);
- }
- }
- else
- return false;
-
- if (!bit)
- {
- if (ibit < 0)
- gcc_unreachable ();
- bit = build_int_cst (TREE_TYPE (lhs), ibit);
- }
- }
- else if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
- == CODE_FOR_nothing)
- return false;
-
- tree use_lhs = gimple_assign_lhs (use_stmt);
- if (!use_lhs)
- return false;
-
- if (!bit)
- {
- if (TREE_CODE (mask) == INTEGER_CST)
- {
- if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
- mask = const_unop (BIT_NOT_EXPR, TREE_TYPE (mask), mask);
- mask = fold_convert (TREE_TYPE (lhs), mask);
- int ibit = tree_log2 (mask);
- if (ibit < 0)
- return false;
- bit = build_int_cst (TREE_TYPE (lhs), ibit);
- }
- else if (TREE_CODE (mask) == SSA_NAME)
- {
- gimple *g = SSA_NAME_DEF_STMT (mask);
- tree match_op;
- if (gimple_nop_convert (mask, &match_op, NULL))
- {
- mask = match_op;
- if (TREE_CODE (mask) != SSA_NAME)
- return false;
- g = SSA_NAME_DEF_STMT (mask);
- }
- if (!is_gimple_assign (g))
- return false;
-
- if (fn == IFN_ATOMIC_BIT_TEST_AND_RESET)
- {
- if (gimple_assign_rhs_code (g) != BIT_NOT_EXPR)
- return false;
- mask = gimple_assign_rhs1 (g);
- if (TREE_CODE (mask) != SSA_NAME)
- return false;
- g = SSA_NAME_DEF_STMT (mask);
- }
-
- if (!is_gimple_assign (g)
- || gimple_assign_rhs_code (g) != LSHIFT_EXPR
- || !integer_onep (gimple_assign_rhs1 (g)))
- return false;
- bit = gimple_assign_rhs2 (g);
- }
- else
- return false;
-
- tree cmp_mask;
- if (gimple_assign_rhs1 (use_stmt) == lhs)
- cmp_mask = gimple_assign_rhs2 (use_stmt);
- else
- cmp_mask = gimple_assign_rhs1 (use_stmt);
-
- tree match_op;
- if (gimple_nop_convert (cmp_mask, &match_op, NULL))
- cmp_mask = match_op;
-
- if (!operand_equal_p (cmp_mask, mask, 0))
- return false;
- }
-
- bool use_bool = true;
- bool has_debug_uses = false;
- imm_use_iterator iter;
- gimple *g;
-
- if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs))
- use_bool = false;
- FOR_EACH_IMM_USE_STMT (g, iter, use_lhs)
- {
- enum tree_code code = ERROR_MARK;
- tree op0 = NULL_TREE, op1 = NULL_TREE;
- if (is_gimple_debug (g))
- {
- has_debug_uses = true;
- continue;
- }
- else if (is_gimple_assign (g))
- switch (gimple_assign_rhs_code (g))
- {
- case COND_EXPR:
- op1 = gimple_assign_rhs1 (g);
- code = TREE_CODE (op1);
- if (TREE_CODE_CLASS (code) != tcc_comparison)
- break;
- op0 = TREE_OPERAND (op1, 0);
- op1 = TREE_OPERAND (op1, 1);
- break;
- case EQ_EXPR:
- case NE_EXPR:
- code = gimple_assign_rhs_code (g);
- op0 = gimple_assign_rhs1 (g);
- op1 = gimple_assign_rhs2 (g);
- break;
- default:
- break;
- }
- else if (gimple_code (g) == GIMPLE_COND)
- {
- code = gimple_cond_code (g);
- op0 = gimple_cond_lhs (g);
- op1 = gimple_cond_rhs (g);
- }
-
- if ((code == EQ_EXPR || code == NE_EXPR)
- && op0 == use_lhs
- && integer_zerop (op1))
- {
- use_operand_p use_p;
- int n = 0;
- FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
- n++;
- if (n == 1)
- continue;
- }
-
- use_bool = false;
- break;
- }
-
- tree new_lhs = make_ssa_name (TREE_TYPE (lhs));
- tree flag = build_int_cst (TREE_TYPE (lhs), use_bool);
- if (has_model_arg)
- g = gimple_build_call_internal (fn, 5, gimple_call_arg (call, 0),
- bit, flag, gimple_call_arg (call, 2),
- gimple_call_fn (call));
- else
- g = gimple_build_call_internal (fn, 4, gimple_call_arg (call, 0),
- bit, flag, gimple_call_fn (call));
- gimple_call_set_lhs (g, new_lhs);
- gimple_set_location (g, gimple_location (call));
- gimple_move_vops (g, call);
- bool throws = stmt_can_throw_internal (cfun, call);
- gimple_call_set_nothrow (as_a <gcall *> (g),
- gimple_call_nothrow_p (as_a <gcall *> (call)));
- gimple_stmt_iterator gsi = *gsip;
- gsi_insert_after (&gsi, g, GSI_NEW_STMT);
- edge e = NULL;
- if (throws)
- {
- maybe_clean_or_replace_eh_stmt (call, g);
- if (after || (use_bool && has_debug_uses))
- e = find_fallthru_edge (gsi_bb (gsi)->succs);
- }
- if (after)
- {
- /* The internal function returns the value of the specified bit
- before the atomic operation. If we are interested in the value
- of the specified bit after the atomic operation (makes only sense
- for xor, otherwise the bit content is compile time known),
- we need to invert the bit. */
- tree mask_convert = mask;
- gimple_seq stmts = NULL;
- if (!use_bool)
- mask_convert = gimple_convert (&stmts, TREE_TYPE (lhs), mask);
- new_lhs = gimple_build (&stmts, BIT_XOR_EXPR, TREE_TYPE (lhs), new_lhs,
- use_bool ? build_int_cst (TREE_TYPE (lhs), 1)
- : mask_convert);
- if (throws)
- {
- gsi_insert_seq_on_edge_immediate (e, stmts);
- gsi = gsi_for_stmt (gimple_seq_last (stmts));
- }
- else
- gsi_insert_seq_after (&gsi, stmts, GSI_NEW_STMT);
- }
- if (use_bool && has_debug_uses)
- {
- tree temp = NULL_TREE;
- if (!throws || after || single_pred_p (e->dest))
- {
- temp = build_debug_expr_decl (TREE_TYPE (lhs));
- tree t = build2 (LSHIFT_EXPR, TREE_TYPE (lhs), new_lhs, bit);
- g = gimple_build_debug_bind (temp, t, g);
- if (throws && !after)
- {
- gsi = gsi_after_labels (e->dest);
- gsi_insert_before (&gsi, g, GSI_SAME_STMT);
- }
- else
- gsi_insert_after (&gsi, g, GSI_NEW_STMT);
- }
- FOR_EACH_IMM_USE_STMT (g, iter, use_lhs)
- if (is_gimple_debug (g))
- {
- use_operand_p use_p;
- if (temp == NULL_TREE)
- gimple_debug_bind_reset_value (g);
- else
- FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
- SET_USE (use_p, temp);
- update_stmt (g);
- }
- }
- SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_lhs)
- = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs);
- replace_uses_by (use_lhs, new_lhs);
- gsi = gsi_for_stmt (use_stmt);
- gsi_remove (&gsi, true);
- release_defs (use_stmt);
- gsi_remove (gsip, true);
- release_ssa_name (lhs);
- return true;
-}
-
-/* Optimize
- _4 = __atomic_add_fetch_* (ptr_6, arg_2, _3);
- _5 = _4 == 0;
- to
- _4 = .ATOMIC_ADD_FETCH_CMP_0 (EQ_EXPR, ptr_6, arg_2, _3);
- _5 = _4;
- Similarly for __sync_add_and_fetch_* (without the ", _3" part
- in there). */
-
-static bool
-optimize_atomic_op_fetch_cmp_0 (gimple_stmt_iterator *gsip,
- enum internal_fn fn, bool has_model_arg)
-{
- gimple *call = gsi_stmt (*gsip);
- tree lhs = gimple_call_lhs (call);
- use_operand_p use_p;
- gimple *use_stmt;
-
- if (!flag_inline_atomics
- || optimize_debug
- || !gimple_call_builtin_p (call, BUILT_IN_NORMAL)
- || !lhs
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)
- || !single_imm_use (lhs, &use_p, &use_stmt)
- || !gimple_vdef (call))
- return false;
-
- optab optab;
- switch (fn)
- {
- case IFN_ATOMIC_ADD_FETCH_CMP_0:
- optab = atomic_add_fetch_cmp_0_optab;
- break;
- case IFN_ATOMIC_SUB_FETCH_CMP_0:
- optab = atomic_sub_fetch_cmp_0_optab;
- break;
- case IFN_ATOMIC_AND_FETCH_CMP_0:
- optab = atomic_and_fetch_cmp_0_optab;
- break;
- case IFN_ATOMIC_OR_FETCH_CMP_0:
- optab = atomic_or_fetch_cmp_0_optab;
- break;
- case IFN_ATOMIC_XOR_FETCH_CMP_0:
- optab = atomic_xor_fetch_cmp_0_optab;
- break;
- default:
- return false;
- }
-
- if (optab_handler (optab, TYPE_MODE (TREE_TYPE (lhs)))
- == CODE_FOR_nothing)
- return false;
-
- tree use_lhs = lhs;
- if (gimple_assign_cast_p (use_stmt))
- {
- use_lhs = gimple_assign_lhs (use_stmt);
- if (!tree_nop_conversion_p (TREE_TYPE (use_lhs), TREE_TYPE (lhs))
- || (!INTEGRAL_TYPE_P (TREE_TYPE (use_lhs))
- && !POINTER_TYPE_P (TREE_TYPE (use_lhs)))
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use_lhs)
- || !single_imm_use (use_lhs, &use_p, &use_stmt))
- return false;
- }
- enum tree_code code = ERROR_MARK;
- tree op0 = NULL_TREE, op1 = NULL_TREE;
- if (is_gimple_assign (use_stmt))
- switch (gimple_assign_rhs_code (use_stmt))
- {
- case COND_EXPR:
- op1 = gimple_assign_rhs1 (use_stmt);
- code = TREE_CODE (op1);
- if (TREE_CODE_CLASS (code) == tcc_comparison)
- {
- op0 = TREE_OPERAND (op1, 0);
- op1 = TREE_OPERAND (op1, 1);
- }
- break;
- default:
- code = gimple_assign_rhs_code (use_stmt);
- if (TREE_CODE_CLASS (code) == tcc_comparison)
- {
- op0 = gimple_assign_rhs1 (use_stmt);
- op1 = gimple_assign_rhs2 (use_stmt);
- }
- break;
- }
- else if (gimple_code (use_stmt) == GIMPLE_COND)
- {
- code = gimple_cond_code (use_stmt);
- op0 = gimple_cond_lhs (use_stmt);
- op1 = gimple_cond_rhs (use_stmt);
- }
-
- switch (code)
- {
- case LT_EXPR:
- case LE_EXPR:
- case GT_EXPR:
- case GE_EXPR:
- if (!INTEGRAL_TYPE_P (TREE_TYPE (use_lhs))
- || TREE_CODE (TREE_TYPE (use_lhs)) == BOOLEAN_TYPE
- || TYPE_UNSIGNED (TREE_TYPE (use_lhs)))
- return false;
- /* FALLTHRU */
- case EQ_EXPR:
- case NE_EXPR:
- if (op0 == use_lhs && integer_zerop (op1))
- break;
- return false;
- default:
- return false;
- }
-
- int encoded;
- switch (code)
- {
- /* Use special encoding of the operation. We want to also
- encode the mode in the first argument and for neither EQ_EXPR
- etc. nor EQ etc. we can rely it will fit into QImode. */
- case EQ_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_EQ; break;
- case NE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_NE; break;
- case LT_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_LT; break;
- case LE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_LE; break;
- case GT_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_GT; break;
- case GE_EXPR: encoded = ATOMIC_OP_FETCH_CMP_0_GE; break;
- default: gcc_unreachable ();
- }
-
- tree new_lhs = make_ssa_name (boolean_type_node);
- gimple *g;
- tree flag = build_int_cst (TREE_TYPE (lhs), encoded);
- if (has_model_arg)
- g = gimple_build_call_internal (fn, 5, flag,
- gimple_call_arg (call, 0),
- gimple_call_arg (call, 1),
- gimple_call_arg (call, 2),
- gimple_call_fn (call));
- else
- g = gimple_build_call_internal (fn, 4, flag,
- gimple_call_arg (call, 0),
- gimple_call_arg (call, 1),
- gimple_call_fn (call));
- gimple_call_set_lhs (g, new_lhs);
- gimple_set_location (g, gimple_location (call));
- gimple_move_vops (g, call);
- bool throws = stmt_can_throw_internal (cfun, call);
- gimple_call_set_nothrow (as_a <gcall *> (g),
- gimple_call_nothrow_p (as_a <gcall *> (call)));
- gimple_stmt_iterator gsi = *gsip;
- gsi_insert_after (&gsi, g, GSI_SAME_STMT);
- if (throws)
- maybe_clean_or_replace_eh_stmt (call, g);
- if (is_gimple_assign (use_stmt))
- switch (gimple_assign_rhs_code (use_stmt))
- {
- case COND_EXPR:
- gimple_assign_set_rhs1 (use_stmt, new_lhs);
- break;
- default:
- gsi = gsi_for_stmt (use_stmt);
- if (tree ulhs = gimple_assign_lhs (use_stmt))
- if (useless_type_conversion_p (TREE_TYPE (ulhs),
- boolean_type_node))
- {
- gimple_assign_set_rhs_with_ops (&gsi, SSA_NAME, new_lhs);
- break;
- }
- gimple_assign_set_rhs_with_ops (&gsi, NOP_EXPR, new_lhs);
- break;
- }
- else if (gimple_code (use_stmt) == GIMPLE_COND)
- {
- gcond *use_cond = as_a <gcond *> (use_stmt);
- gimple_cond_set_code (use_cond, NE_EXPR);
- gimple_cond_set_lhs (use_cond, new_lhs);
- gimple_cond_set_rhs (use_cond, boolean_false_node);
- }
-
- update_stmt (use_stmt);
- if (use_lhs != lhs)
- {
- gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (use_lhs));
- gsi_remove (&gsi, true);
- release_ssa_name (use_lhs);
- }
- gsi_remove (gsip, true);
- release_ssa_name (lhs);
- return true;
-}
-
-/* A simple pass that attempts to fold all builtin functions. This pass
- is run after we've propagated as many constants as we can. */
-
-namespace {
-
-const pass_data pass_data_fold_builtins =
-{
- GIMPLE_PASS, /* type */
- "fab", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_NONE, /* tv_id */
- ( PROP_cfg | PROP_ssa ), /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_update_ssa, /* todo_flags_finish */
-};
-
-class pass_fold_builtins : public gimple_opt_pass
-{
-public:
- pass_fold_builtins (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_fold_builtins, ctxt)
- {}
-
- /* opt_pass methods: */
- opt_pass * clone () final override { return new pass_fold_builtins (m_ctxt); }
- unsigned int execute (function *) final override;
-
-}; // class pass_fold_builtins
-
-/* Optimize memcmp STMT into memcmp_eq if it is only used with
- `== 0` or `!= 0`. */
-
-static void
-optimize_memcmp_eq (gcall *stmt)
-{
- /* Make sure memcmp arguments are the correct type. */
- if (gimple_call_num_args (stmt) != 3)
- return;
- tree arg1 = gimple_call_arg (stmt, 0);
- tree arg2 = gimple_call_arg (stmt, 1);
- tree len = gimple_call_arg (stmt, 2);
-
- if (!POINTER_TYPE_P (TREE_TYPE (arg1)))
- return;
- if (!POINTER_TYPE_P (TREE_TYPE (arg2)))
- return;
- if (!INTEGRAL_TYPE_P (TREE_TYPE (len)))
- return;
- /* The return value of the memcmp has to be used
- equality comparison to zero. */
- tree res = gimple_call_lhs (stmt);
-
- if (!res || !use_in_zero_equality (res))
- return;
-
- gimple_call_set_fndecl (stmt, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
- update_stmt (stmt);
-}
-
-unsigned int
-pass_fold_builtins::execute (function *fun)
-{
- bool cfg_changed = false;
- basic_block bb;
- unsigned int todoflags = 0;
-
- /* Set last full fold prop if not already set. */
- fun->curr_properties |= PROP_last_full_fold;
-
- FOR_EACH_BB_FN (bb, fun)
- {
- gimple_stmt_iterator i;
- for (i = gsi_start_bb (bb); !gsi_end_p (i); )
- {
- gimple *stmt, *old_stmt;
- tree callee;
- enum built_in_function fcode;
-
- stmt = gsi_stmt (i);
-
- if (gimple_code (stmt) != GIMPLE_CALL)
- {
- gsi_next (&i);
- continue;
- }
-
- callee = gimple_call_fndecl (stmt);
- if (!callee
- && gimple_call_internal_p (stmt, IFN_ASSUME))
- {
- gsi_remove (&i, true);
- continue;
- }
- if (!callee || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
- {
- gsi_next (&i);
- continue;
- }
-
- fcode = DECL_FUNCTION_CODE (callee);
- if (fold_stmt (&i))
- ;
- else
- {
- tree result = NULL_TREE;
- switch (DECL_FUNCTION_CODE (callee))
- {
-
- case BUILT_IN_STACK_RESTORE:
- result = optimize_stack_restore (i);
- if (result)
- break;
- gsi_next (&i);
- continue;
-
- case BUILT_IN_MEMCMP:
- optimize_memcmp_eq (as_a<gcall*>(stmt));
- break;
-
- case BUILT_IN_UNREACHABLE:
- if (optimize_unreachable (i))
- cfg_changed = true;
- break;
-
- case BUILT_IN_ATOMIC_ADD_FETCH_1:
- case BUILT_IN_ATOMIC_ADD_FETCH_2:
- case BUILT_IN_ATOMIC_ADD_FETCH_4:
- case BUILT_IN_ATOMIC_ADD_FETCH_8:
- case BUILT_IN_ATOMIC_ADD_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_ADD_FETCH_CMP_0,
- true);
- break;
- case BUILT_IN_SYNC_ADD_AND_FETCH_1:
- case BUILT_IN_SYNC_ADD_AND_FETCH_2:
- case BUILT_IN_SYNC_ADD_AND_FETCH_4:
- case BUILT_IN_SYNC_ADD_AND_FETCH_8:
- case BUILT_IN_SYNC_ADD_AND_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_ADD_FETCH_CMP_0,
- false);
- break;
-
- case BUILT_IN_ATOMIC_SUB_FETCH_1:
- case BUILT_IN_ATOMIC_SUB_FETCH_2:
- case BUILT_IN_ATOMIC_SUB_FETCH_4:
- case BUILT_IN_ATOMIC_SUB_FETCH_8:
- case BUILT_IN_ATOMIC_SUB_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_SUB_FETCH_CMP_0,
- true);
- break;
- case BUILT_IN_SYNC_SUB_AND_FETCH_1:
- case BUILT_IN_SYNC_SUB_AND_FETCH_2:
- case BUILT_IN_SYNC_SUB_AND_FETCH_4:
- case BUILT_IN_SYNC_SUB_AND_FETCH_8:
- case BUILT_IN_SYNC_SUB_AND_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_SUB_FETCH_CMP_0,
- false);
- break;
-
- case BUILT_IN_ATOMIC_FETCH_OR_1:
- case BUILT_IN_ATOMIC_FETCH_OR_2:
- case BUILT_IN_ATOMIC_FETCH_OR_4:
- case BUILT_IN_ATOMIC_FETCH_OR_8:
- case BUILT_IN_ATOMIC_FETCH_OR_16:
- optimize_atomic_bit_test_and (&i,
- IFN_ATOMIC_BIT_TEST_AND_SET,
- true, false);
- break;
- case BUILT_IN_SYNC_FETCH_AND_OR_1:
- case BUILT_IN_SYNC_FETCH_AND_OR_2:
- case BUILT_IN_SYNC_FETCH_AND_OR_4:
- case BUILT_IN_SYNC_FETCH_AND_OR_8:
- case BUILT_IN_SYNC_FETCH_AND_OR_16:
- optimize_atomic_bit_test_and (&i,
- IFN_ATOMIC_BIT_TEST_AND_SET,
- false, false);
- break;
-
- case BUILT_IN_ATOMIC_FETCH_XOR_1:
- case BUILT_IN_ATOMIC_FETCH_XOR_2:
- case BUILT_IN_ATOMIC_FETCH_XOR_4:
- case BUILT_IN_ATOMIC_FETCH_XOR_8:
- case BUILT_IN_ATOMIC_FETCH_XOR_16:
- optimize_atomic_bit_test_and
- (&i, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, true, false);
- break;
- case BUILT_IN_SYNC_FETCH_AND_XOR_1:
- case BUILT_IN_SYNC_FETCH_AND_XOR_2:
- case BUILT_IN_SYNC_FETCH_AND_XOR_4:
- case BUILT_IN_SYNC_FETCH_AND_XOR_8:
- case BUILT_IN_SYNC_FETCH_AND_XOR_16:
- optimize_atomic_bit_test_and
- (&i, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, false, false);
- break;
-
- case BUILT_IN_ATOMIC_XOR_FETCH_1:
- case BUILT_IN_ATOMIC_XOR_FETCH_2:
- case BUILT_IN_ATOMIC_XOR_FETCH_4:
- case BUILT_IN_ATOMIC_XOR_FETCH_8:
- case BUILT_IN_ATOMIC_XOR_FETCH_16:
- if (optimize_atomic_bit_test_and
- (&i, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, true, true))
- break;
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_XOR_FETCH_CMP_0,
- true);
- break;
- case BUILT_IN_SYNC_XOR_AND_FETCH_1:
- case BUILT_IN_SYNC_XOR_AND_FETCH_2:
- case BUILT_IN_SYNC_XOR_AND_FETCH_4:
- case BUILT_IN_SYNC_XOR_AND_FETCH_8:
- case BUILT_IN_SYNC_XOR_AND_FETCH_16:
- if (optimize_atomic_bit_test_and
- (&i, IFN_ATOMIC_BIT_TEST_AND_COMPLEMENT, false, true))
- break;
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_XOR_FETCH_CMP_0,
- false);
- break;
-
- case BUILT_IN_ATOMIC_FETCH_AND_1:
- case BUILT_IN_ATOMIC_FETCH_AND_2:
- case BUILT_IN_ATOMIC_FETCH_AND_4:
- case BUILT_IN_ATOMIC_FETCH_AND_8:
- case BUILT_IN_ATOMIC_FETCH_AND_16:
- optimize_atomic_bit_test_and (&i,
- IFN_ATOMIC_BIT_TEST_AND_RESET,
- true, false);
- break;
- case BUILT_IN_SYNC_FETCH_AND_AND_1:
- case BUILT_IN_SYNC_FETCH_AND_AND_2:
- case BUILT_IN_SYNC_FETCH_AND_AND_4:
- case BUILT_IN_SYNC_FETCH_AND_AND_8:
- case BUILT_IN_SYNC_FETCH_AND_AND_16:
- optimize_atomic_bit_test_and (&i,
- IFN_ATOMIC_BIT_TEST_AND_RESET,
- false, false);
- break;
-
- case BUILT_IN_ATOMIC_AND_FETCH_1:
- case BUILT_IN_ATOMIC_AND_FETCH_2:
- case BUILT_IN_ATOMIC_AND_FETCH_4:
- case BUILT_IN_ATOMIC_AND_FETCH_8:
- case BUILT_IN_ATOMIC_AND_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_AND_FETCH_CMP_0,
- true);
- break;
- case BUILT_IN_SYNC_AND_AND_FETCH_1:
- case BUILT_IN_SYNC_AND_AND_FETCH_2:
- case BUILT_IN_SYNC_AND_AND_FETCH_4:
- case BUILT_IN_SYNC_AND_AND_FETCH_8:
- case BUILT_IN_SYNC_AND_AND_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_AND_FETCH_CMP_0,
- false);
- break;
-
- case BUILT_IN_ATOMIC_OR_FETCH_1:
- case BUILT_IN_ATOMIC_OR_FETCH_2:
- case BUILT_IN_ATOMIC_OR_FETCH_4:
- case BUILT_IN_ATOMIC_OR_FETCH_8:
- case BUILT_IN_ATOMIC_OR_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_OR_FETCH_CMP_0,
- true);
- break;
- case BUILT_IN_SYNC_OR_AND_FETCH_1:
- case BUILT_IN_SYNC_OR_AND_FETCH_2:
- case BUILT_IN_SYNC_OR_AND_FETCH_4:
- case BUILT_IN_SYNC_OR_AND_FETCH_8:
- case BUILT_IN_SYNC_OR_AND_FETCH_16:
- optimize_atomic_op_fetch_cmp_0 (&i,
- IFN_ATOMIC_OR_FETCH_CMP_0,
- false);
- break;
-
- case BUILT_IN_VA_START:
- case BUILT_IN_VA_END:
- case BUILT_IN_VA_COPY:
- /* These shouldn't be folded before pass_stdarg. */
- result = optimize_stdarg_builtin (stmt);
- break;
-
- default:;
- }
-
- if (!result)
- {
- gsi_next (&i);
- continue;
- }
-
- gimplify_and_update_call_from_tree (&i, result);
- }
-
- todoflags |= TODO_update_address_taken;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Simplified\n ");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
- }
-
- old_stmt = stmt;
- stmt = gsi_stmt (i);
- update_stmt (stmt);
-
- if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt)
- && gimple_purge_dead_eh_edges (bb))
- cfg_changed = true;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "to\n ");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
- fprintf (dump_file, "\n");
- }
-
- /* Retry the same statement if it changed into another
- builtin, there might be new opportunities now. */
- if (gimple_code (stmt) != GIMPLE_CALL)
- {
- gsi_next (&i);
- continue;
- }
- callee = gimple_call_fndecl (stmt);
- if (!callee
- || !fndecl_built_in_p (callee, fcode))
- gsi_next (&i);
- }
- }
-
- /* Delete unreachable blocks. */
- if (cfg_changed)
- todoflags |= TODO_cleanup_cfg;
-
- return todoflags;
-}
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_fold_builtins (gcc::context *ctxt)
-{
- return new pass_fold_builtins (ctxt);
-}
-
/* A simple pass that emits some warnings post IPA. */
namespace {
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 4c438a0..2c6e1ea 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -1842,7 +1842,15 @@ simplify_builtin_memcmp (gimple_stmt_iterator *gsi_p, gcall *stmt)
return true;
}
}
- return false;
+
+ /* Replace memcmp with memcmp_eq if the above fails. */
+ if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) == BUILT_IN_MEMCMP_EQ)
+ return false;
+ if (!(cfun->curr_properties & (PROP_last_full_fold)))
+ return false;
+ gimple_call_set_fndecl (stmt, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
+ update_stmt (stmt);
+ return true;
}
/* Optimizes builtin memchrs for small constant sizes with a const string.
@@ -2124,6 +2132,116 @@ simplify_builtin_memcpy_memset (gimple_stmt_iterator *gsi_p, gcall *stmt2)
}
}
+
+/* Try to optimize out __builtin_stack_restore. Optimize it out
+ if there is another __builtin_stack_restore in the same basic
+ block and no calls or ASM_EXPRs are in between, or if this block's
+ only outgoing edge is to EXIT_BLOCK and there are no calls or
+ ASM_EXPRs after this __builtin_stack_restore.
+ Note restore right before a noreturn function is not needed.
+ And skip some cheap calls that will most likely become an instruction.
+ Restoring the stack before a call is important to be able to keep
+ stack usage down so that call does not run out of stack. */
+
+
+static bool
+optimize_stack_restore (gimple_stmt_iterator *gsi, gimple *call)
+{
+ if (!(cfun->curr_properties & PROP_last_full_fold))
+ return false;
+ tree callee;
+ gimple *stmt;
+
+ basic_block bb = gsi_bb (*gsi);
+
+ if (gimple_call_num_args (call) != 1
+ || TREE_CODE (gimple_call_arg (call, 0)) != SSA_NAME
+ || !POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, 0))))
+ return false;
+
+ gimple_stmt_iterator i = *gsi;
+ for (gsi_next (&i); !gsi_end_p (i); gsi_next (&i))
+ {
+ stmt = gsi_stmt (i);
+ if (is_a<gasm*> (stmt))
+ return false;
+ gcall *call = dyn_cast<gcall*>(stmt);
+ if (!call)
+ continue;
+
+ /* We can remove the restore in front of noreturn
+ calls. Since the restore will happen either
+ via an unwind/longjmp or not at all. */
+ if (gimple_call_noreturn_p (call))
+ break;
+
+ /* Internal calls are ok, to bypass
+ check first since fndecl will be null. */
+ if (gimple_call_internal_p (call))
+ continue;
+
+ callee = gimple_call_fndecl (call);
+ /* Non-builtin calls are not ok. */
+ if (!callee
+ || !fndecl_built_in_p (callee))
+ return false;
+
+ /* Do not remove stack updates before strub leave. */
+ if (fndecl_built_in_p (callee, BUILT_IN___STRUB_LEAVE)
+ /* Alloca calls are not ok either. */
+ || fndecl_builtin_alloc_p (callee))
+ return false;
+
+ if (fndecl_built_in_p (callee, BUILT_IN_STACK_RESTORE))
+ goto second_stack_restore;
+
+ /* If not a simple or inexpensive builtin, then it is not ok either. */
+ if (!is_simple_builtin (callee)
+ && !is_inexpensive_builtin (callee))
+ return false;
+ }
+
+ /* Allow one successor of the exit block, or zero successors. */
+ switch (EDGE_COUNT (bb->succs))
+ {
+ case 0:
+ break;
+ case 1:
+ if (single_succ_edge (bb)->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ second_stack_restore:
+
+ /* If there's exactly one use, then zap the call to __builtin_stack_save.
+ If there are multiple uses, then the last one should remove the call.
+ In any case, whether the call to __builtin_stack_save can be removed
+ or not is irrelevant to removing the call to __builtin_stack_restore. */
+ if (has_single_use (gimple_call_arg (call, 0)))
+ {
+ gimple *stack_save = SSA_NAME_DEF_STMT (gimple_call_arg (call, 0));
+ if (is_gimple_call (stack_save))
+ {
+ callee = gimple_call_fndecl (stack_save);
+ if (callee && fndecl_built_in_p (callee, BUILT_IN_STACK_SAVE))
+ {
+ gimple_stmt_iterator stack_save_gsi;
+ tree rhs;
+
+ stack_save_gsi = gsi_for_stmt (stack_save);
+ rhs = build_int_cst (TREE_TYPE (gimple_call_arg (call, 0)), 0);
+ replace_call_with_value (&stack_save_gsi, rhs);
+ }
+ }
+ }
+
+ /* No effect, so the statement will be deleted. */
+ replace_call_with_value (gsi, NULL_TREE);
+ return true;
+}
+
/* *GSI_P is a GIMPLE_CALL to a builtin function.
Optimize
memcpy (p, "abcd", 4);
@@ -2155,6 +2273,8 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2, bool full_walk
switch (DECL_FUNCTION_CODE (callee2))
{
+ case BUILT_IN_STACK_RESTORE:
+ return optimize_stack_restore (gsi_p, as_a<gcall*>(stmt2));
case BUILT_IN_MEMCMP:
case BUILT_IN_MEMCMP_EQ:
return simplify_builtin_memcmp (gsi_p, as_a<gcall*>(stmt2));
@@ -4786,6 +4906,49 @@ public:
bool m_full_walk = false;
}; // class pass_forwprop
+/* Attemp to make the BB block of __builtin_unreachable unreachable by changing
+ the incoming jumps. Return true if at least one jump was changed. */
+
+static bool
+optimize_unreachable (basic_block bb)
+{
+ gimple_stmt_iterator gsi;
+ gimple *stmt;
+ edge_iterator ei;
+ edge e;
+ bool ret;
+
+ ret = false;
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ gsi = gsi_last_bb (e->src);
+ if (gsi_end_p (gsi))
+ continue;
+
+ stmt = gsi_stmt (gsi);
+ if (gcond *cond_stmt = dyn_cast <gcond *> (stmt))
+ {
+ if (e->flags & EDGE_TRUE_VALUE)
+ gimple_cond_make_false (cond_stmt);
+ else if (e->flags & EDGE_FALSE_VALUE)
+ gimple_cond_make_true (cond_stmt);
+ else
+ gcc_unreachable ();
+ update_stmt (cond_stmt);
+ }
+ else
+ {
+ /* Todo: handle other cases. Note that unreachable switch case
+ statements have already been removed. */
+ continue;
+ }
+
+ ret = true;
+ }
+
+ return ret;
+}
+
unsigned int
pass_forwprop::execute (function *fun)
{
@@ -4853,6 +5016,21 @@ pass_forwprop::execute (function *fun)
if (!any)
continue;
+ /* Remove conditions that go directly to unreachable when this is the last forwprop. */
+ if (last_p
+ && !(flag_sanitize & SANITIZE_UNREACHABLE))
+ {
+ gimple_stmt_iterator gsi;
+ gsi = gsi_start_nondebug_after_labels_bb (bb);
+ if (!gsi_end_p (gsi)
+ && gimple_call_builtin_p (*gsi, BUILT_IN_UNREACHABLE)
+ && optimize_unreachable (bb))
+ {
+ cfg_changed = true;
+ continue;
+ }
+ }
+
/* Record degenerate PHIs in the lattice. */
for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);
gsi_next (&si))
@@ -5285,6 +5463,11 @@ pass_forwprop::execute (function *fun)
if (fold_stmt (&gsi, fwprop_ssa_val, simple_dce_worklist))
{
changed = true;
+ /* There is no updating of the address
+ taken after the last forwprop so update
+ the addresses when a folding happened. */
+ if (last_p)
+ todoflags |= TODO_update_address_taken;
stmt = gsi_stmt (gsi);
/* Cleanup the CFG if we simplified a condition to
true or false. */
diff --git a/gcc/tree-ssa-pre.cc b/gcc/tree-ssa-pre.cc
index 64ffaca..2a7dcbe 100644
--- a/gcc/tree-ssa-pre.cc
+++ b/gcc/tree-ssa-pre.cc
@@ -2049,8 +2049,12 @@ prune_clobbered_mems (bitmap_set_t set, basic_block block, bool clean_traps)
the bitmap_find_leader way to see if there's still an expression
for it. For some ratio of to be removed values and number of
values/expressions in the set this might be faster than rebuilding
- the value-set. */
- if (any_removed)
+ the value-set.
+ Note when there's a MAX solution on one edge (clean_traps) do not
+ prune values as we need to consider the resulting expression set MAX
+ as well. This avoids a later growing ANTIC_IN value-set during
+ iteration, when the explicitly represented expression set grows. */
+ if (any_removed && !clean_traps)
{
bitmap_clear (&set->values);
FOR_EACH_EXPR_ID_IN_SET (set, i, bi)
@@ -2080,6 +2084,7 @@ compute_antic_aux (basic_block block, bool block_has_abnormal_pred_edge)
edge e;
edge_iterator ei;
+ bool was_visited = BB_VISITED (block);
bool changed = ! BB_VISITED (block);
bool any_max_on_edge = false;
@@ -2215,6 +2220,32 @@ compute_antic_aux (basic_block block, bool block_has_abnormal_pred_edge)
/* clean (ANTIC_IN (block)) is defered to after the iteration converged
because it can cause non-convergence, see for example PR81181. */
+ if (was_visited
+ && bitmap_and_into (&ANTIC_IN (block)->values, &old->values))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "warning: intersecting with old ANTIC_IN "
+ "shrinks the set\n");
+ /* Prune expressions not in the value set. */
+ bitmap_iterator bi;
+ unsigned int i;
+ unsigned int to_clear = -1U;
+ FOR_EACH_EXPR_ID_IN_SET (ANTIC_IN (block), i, bi)
+ {
+ if (to_clear != -1U)
+ {
+ bitmap_clear_bit (&ANTIC_IN (block)->expressions, to_clear);
+ to_clear = -1U;
+ }
+ pre_expr expr = expression_for_id (i);
+ unsigned int value_id = get_expr_value_id (expr);
+ if (!bitmap_bit_p (&ANTIC_IN (block)->values, value_id))
+ to_clear = i;
+ }
+ if (to_clear != -1U)
+ bitmap_clear_bit (&ANTIC_IN (block)->expressions, to_clear);
+ }
+
if (!bitmap_set_equal (old, ANTIC_IN (block)))
changed = true;
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index 1d549e4..df45adb 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -7177,6 +7177,15 @@ vectorizable_reduction (loop_vec_info loop_vinfo,
tree vectype_out = SLP_TREE_VECTYPE (slp_for_stmt_info);
VECT_REDUC_INFO_VECTYPE (reduc_info) = vectype_out;
+ /* We do not handle mask reductions correctly in the epilogue. */
+ if (VECTOR_BOOLEAN_TYPE_P (vectype_out))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "mask reduction not supported.\n");
+ return false;
+ }
+
gimple_match_op op;
if (!gimple_extract_op (stmt_info->stmt, &op))
gcc_unreachable ();
@@ -7187,8 +7196,7 @@ vectorizable_reduction (loop_vec_info loop_vinfo,
return false;
/* Do not try to vectorize bit-precision reductions. */
- if (!VECTOR_BOOLEAN_TYPE_P (vectype_out)
- && !type_has_mode_precision_p (op.type)
+ if (!type_has_mode_precision_p (op.type)
&& op.code != BIT_AND_EXPR
&& op.code != BIT_IOR_EXPR
&& op.code != BIT_XOR_EXPR)
diff --git a/gcc/tree.h b/gcc/tree.h
index 4c8ad98..6e46374 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7005,6 +7005,15 @@ fndecl_built_in_p (const_tree node, built_in_function name1, F... names)
name1, names...));
}
+/* Returns true if the function decl NODE is an alloca. */
+inline bool
+fndecl_builtin_alloc_p (const_tree node)
+{
+ if (!fndecl_built_in_p (node, BUILT_IN_NORMAL))
+ return false;
+ return ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (node));
+}
+
/* A struct for encapsulating location information about an operator
and the operation built from it.