diff options
Diffstat (limited to 'gcc')
137 files changed, 11594 insertions, 1263 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0ac20fd..76fd227 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,548 @@ +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * diagnostic-format-html.cc: Include "diagnostic-format-sarif.h", + Replace include of "diagnostic-state.h" with includes of + "diagnostic-digraphs.h" and "diagnostic-state-graphs.h". + (html_generation_options::html_generation_options): Update for + field renaming. + (html_builder::m_body_element): New field. + (html_builder::html_builder): Initialize m_body_element. + (html_builder::maybe_make_state_diagram): Port from XML + implementation to state graph implementation. + (html_builder::make_element_for_diagnostic): Add any + per-diagnostic graphs. + (html_builder::add_graph): New. + (html_builder::emit_global_graph): New. + (html_output_format::report_global_digraph): New. + * diagnostic-format-html.h + (html_generation_options::m_show_state_diagram_xml): Replace + with... + (html_generation_options::m_show_state_diagrams_sarif): ...this. + (html_generation_options::m_show_state_diagram_dot_src): Rename + to... + (html_generation_options::m_show_state_diagrams_dot_src): ...this. + * diagnostic-format-sarif.cc: Include "diagnostic-digraphs.h" and + "diagnostic-state-graphs.h". + (sarif_builder::m_run_graphs): New field. + (sarif_result::on_nested_diagnostic): Update call to + make_location_object to pass arg by pointer. + (sarif_builder::sarif_builder): Initialize m_run_graphs. + (sarif_builder::report_global_digraph): New. + (sarif_builder::make_result_object): Add any graphs to + the result object. + (sarif_builder::make_locations_arr): Update call to + make_location_object to pass arg by pointer. + (sarif_builder::make_location_object): Pass param "loc_mgr" by + pointer rather than by reference so that it can be null, and + handle this case. + (copy_any_property_bag): New. + (make_sarif_graph): New. + (make_sarif_node): New. + (make_sarif_edge): New. + (sarif_property_bag::set_graph): New. + (populate_thread_flow_location_object): Port from XML + implementation to state graph implementation. + (make_run_object): Store any graphs. + (sarif_output_format::report_global_digraph): New. + (sarif_generation_options::sarif_generation_options): Rename + m_xml_state to m_state_graph. + (selftest::test_make_location_object): Update for change to + make_location_object. + * diagnostic-format-sarif.h: + (sarif_generation_options::m_xml_state): Replace with... + (sarif_generation_options::m_state_graph): ...this. + (class sarif_location_manager): Add forward decl. + (diagnostics::digraphs::digraph): New forward decl. + (diagnostics::digraphs::node): New forward decl. + (diagnostics::digraphs::edge): New forward decl. + (sarif_property_bag::set_graph): New decl. + (class sarif_graph): New. + (class sarif_node): New. + (class sarif_edge): New. + (make_sarif_graph): New decl. + (make_sarif_node): New decl. + (make_sarif_edge): New decl. + * diagnostic-format-text.h + (diagnostic_text_output_format::report_global_digraph): New. + * diagnostic-format.h + (diagnostic_output_format::report_global_digraph): New vfunc. + * diagnostic-digraphs.cc: New file. + * diagnostic-digraphs.h: New file. + * diagnostic-metadata.h (diagnostics::digraphs::lazy_digraphs): + New forward decl. + (diagnostic_metadata::diagnostic_metadata): Initialize + m_lazy_digraphs. + (diagnostic_metadata::set_lazy_digraphs): New. + (diagnostic_metadata::get_lazy_digraphs): New. + (diagnostic_metadata::m_lazy_digraphs): New field. + * diagnostic-output-spec.cc (sarif_scheme_handler::make_sink): + Update for XML to state graph changes. + (sarif_scheme_handler::make_sarif_gen_opts): Likewise. + (html_scheme_handler::make_sink): Rename "show-state-diagram-xml" + to "show-state-diagrams-sarif" and use pluralization consistently. + * diagnostic-path.cc: Replace include of "xml.h" with + "diagnostic-state-graphs.h". + (diagnostic_event::maybe_make_xml_state): Replace with... + (diagnostic_event::maybe_make_diagnostic_state_graph): ...this. + * diagnostic-path.h (diagnostics::digraphs::digraph): New forward + decl. + (diagnostic_event::maybe_make_xml_state): Replace with... + (diagnostic_event::maybe_make_diagnostic_state_graph): ...this. + * diagnostic-state-graphs.cc: New file. + * diagnostic-state-graphs.h: New file. + * diagnostic-state-to-dot.cc: Port implementation from XML to + state graphs. + * diagnostic-state.h: Deleted file. + * diagnostic.cc (diagnostic_context::report_global_digraph): New. + * diagnostic.h (diagnostics::digraphs::lazy_digraph): New forward + decl. + (diagnostic_context::report_global_digraph): New decl. + * doc/analyzer.texi (Debugging the Analyzer): Update to reflect + change from XML to state graphs. + * doc/invoke.texi ("sarif" diagnostics sink): Replace "xml-state" + with "state-graphs". + ("experimental-html" diagnostics sink): Replace + "show-state-diagrams-xml" with "show-state-diagrams-sarif" + * doc/libgdiagnostics/topics/compatibility.rst + (LIBGDIAGNOSTICS_ABI_3): New. + * doc/libgdiagnostics/topics/graphs.rst: New file. + * doc/libgdiagnostics/topics/index.rst: Add graphs.rst. + * graphviz.h (node_id::operator=): New. + * json.h (json::value::dyn_cast_string): New. + (json::object::get_num_keys): New accessor. + (json::object::get_key): New accessor. + (json::string::dyn_cast_string): New. + * libgdiagnostics++.h (class libgdiagnostics::graph): New. + (class libgdiagnostics::node): New. + (class libgdiagnostics::edge): New. + (class libgdiagnostics::diagnostic::take_graph): New. + (class libgdiagnostics::manager::take_global_graph): New. + (class libgdiagnostics::graph::set_description): New. + (class libgdiagnostics::graph::get_node_by_id): New. + (class libgdiagnostics::graph::get_edge_by_id): New. + (class libgdiagnostics::graph::add_edge): New. + (class libgdiagnostics::node::set_label): New. + (class libgdiagnostics::node::set_location): New. + (class libgdiagnostics::node::set_logical_location): New. + * libgdiagnostics-private.h: New file. + * libgdiagnostics.cc: Define INCLUDE_STRING. Include + "diagnostic-digraphs.h", "diagnostic-state-graphs.h", and + "libgdiagnostics-private.h". + (struct diagnostic_graph): New. + (struct diagnostic_node): New. + (struct diagnostic_edge): New. + (libgdiagnostics_path_event::libgdiagnostics_path_event): Add + state_graph param. + (libgdiagnostics_path_event::maybe_make_diagnostic_state_graph): + New. + (libgdiagnostics_path_event::m_state_graph): New field. + (diagnostic_execution_path::add_event_va): Add state_graph param. + (class prebuilt_digraphs): New. + (diagnostic::diagnostic): Use m_graphs in m_metadata. + (diagnostic::take_graph): New. + (diagnostic::get_graphs): New accessor. + (diagnostic::m_graphs): New field. + (diagnostic_manager::take_global_graph): New. + (diagnostic_execution_path_add_event): Update for new param to + add_event_va. + (diagnostic_execution_path_add_event_va): Likewise. + (diagnostic_graph::add_node_with_id): New public entrypoint. + (diagnostic_graph::add_edge_with_label): New public entrypoint. + (diagnostic_manager_new_graph): New public entrypoint. + (diagnostic_manager_take_global_graph): New public entrypoint. + (diagnostic_take_graph): New public entrypoint. + (diagnostic_graph_release): New public entrypoint. + (diagnostic_graph_set_description): New public entrypoint. + (diagnostic_graph_add_node): New public entrypoint. + (diagnostic_graph_add_edge): New public entrypoint. + (diagnostic_graph_get_node_by_id): New public entrypoint. + (diagnostic_graph_get_edge_by_id): New public entrypoint. + (diagnostic_node_set_location): New public entrypoint. + (diagnostic_node_set_label): New public entrypoint. + (diagnostic_node_set_logical_location): New public entrypoint. + (private_diagnostic_execution_path_add_event_2): New private + entrypoint. + (private_diagnostic_graph_set_property_bag): New private + entrypoint. + (private_diagnostic_node_set_property_bag): New private + entrypoint. + (private_diagnostic_edge_set_property_bag): New private + entrypoint. + * libgdiagnostics.h (diagnostic_graph): New typedef. + (diagnostic_node): New typedef. + (diagnostic_edge): New typedef. + (diagnostic_manager_new_graph): New decl. + (diagnostic_manager_take_global_graph): New decl. + (diagnostic_take_graph): New decl. + (diagnostic_graph_release): New decl. + (diagnostic_graph_set_description): New decl. + (diagnostic_graph_add_node): New decl. + (diagnostic_graph_add_edge): New decl. + (diagnostic_graph_get_node_by_id): New decl. + (diagnostic_graph_get_edge_by_id): New decl. + (diagnostic_node_set_label): New decl. + (diagnostic_node_set_location): New decl. + (diagnostic_node_set_logical_location): New decl. + * libgdiagnostics.map (LIBGDIAGNOSTICS_ABI_3): New. + * libsarifreplay.cc: Include "libgdiagnostics-private.h". + (id_map): New "using". + (sarif_replayer::report_invalid_sarif): Update for change to + report_problem params. + (sarif_replayer::report_unhandled_sarif): Likewise. + (sarif_replayer::report_note): New. + (sarif_replayer::report_problem): Pass param "ref" by + pointer rather than reference and handle it being null. + (sarif_replayer::maybe_get_property_bag): New. + (sarif_replayer::maybe_get_property_bag_value): New. + (sarif_replayer::handle_run_obj): Handle run-level "graphs" as per + §3.14.20. + (sarif_replayer::handle_result_obj): Handle result-level "graphs" + as per §3.27.19. + (handle_thread_flow_location_object): Optionally handle graphs + stored in property "gcc/diagnostic_event/state_graph" as state + graphs. + (sarif_replayer::handle_graph_object): New. + (sarif_replayer::handle_node_object): New. + (sarif_replayer::handle_edge_object): New. + (sarif_replayer::get_graph_node_by_id_property): New. + * selftest-run-tests.cc (selftest::run_tests): Call + selftest::diagnostic_graph_cc_tests and + selftest::diagnostic_state_graph_cc_tests. + * selftest.h (selftest::diagnostic_graph_cc_tests): New decl. + (selftest::diagnostic_state_graph_cc_tests): New decl. + +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * Makefile.in (OBJS-libcommon): Add diagnostic-digraphs.o and + diagnostic-state-graphs.o. + +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * json.cc (json::object::clone): New. + (json::object::clone_as_object): New. + (json::array::clone): New. + (json::float_number::clone): New. + (json::integer_number::clone): New. + (json::string::clone): New. + (json::literal::clone): New. + (selftest::test_cloning): New test. + (selftest::json_cc_tests): Call it. + * json.h (json::value::clone): New vfunc. + (json::object::clone): New decl. + (json::object::clone_as_object): New decl. + (json::array::clone): New decl. + (json::float_number::clone): New decl. + (json::integer_number::clone): New decl. + (json::string::clone): New decl. + (json::literal::clone): New decl. + +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * json.cc (string::string): When constructing from pointer and + length, ensure the new buffer is null-terminated. + (selftest::test_strcmp): New. + (selftest::json_cc_tests): Likewise. + +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * doc/libgdiagnostics/topics/compatibility.rst + (_LIBGDIAGNOSTICS_ABI_2): Add missing anchor. + * doc/libgdiagnostics/topics/diagnostic-manager.rst + (diagnostic_manager_add_sink_from_spec): Add links to GCC's + documentation of "-fdiagnostics-add-output=". Fix parameter + markup. + (diagnostic_manager_set_analysis_target): Fix parameter markup. + Add link to SARIF spec. + * doc/libgdiagnostics/topics/logical-locations.rst: Markup fix. + * doc/libgdiagnostics/tutorial/02-physical-locations.rst: Clarify + wording of what "the source file" means, and that a range can't + have multiple files. + +2025-07-11 Vladimir N. Makarov <vmakarov@redhat.com> + + * lra-constraints.cc (process_address_1): When changing base reg + on a reg of the base class, fall back to reload of whole inner address. + (process_address): Constrain the iteration number. + +2025-07-11 Jakub Jelinek <jakub@redhat.com> + + PR c++/119064 + * doc/invoke.texi (Wc++26-compat): Document. + +2025-07-11 Richard Sandiford <richard.sandiford@arm.com> + + PR target/121027 + * config/aarch64/aarch64.cc (aarch64_evpc_sve_tbl): Punt on 2-input + operations that can be handled by vec_perm. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * config/aarch64/aarch64-simd.md (*eor3qdi4): New + define_insn_and_split. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * config/aarch64/aarch64-simd.md (*bcaxqdi4): New + define_insn_and_split. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * config/aarch64/aarch64-simd.md (eor3q<mode>4): Use VDQ_I mode + iterator. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * config/aarch64/aarch64-simd.md (bcaxq<mode>4): Use VDQ_I mode + iterator. + +2025-07-11 Richard Biener <rguenther@suse.de> + + PR tree-optimization/121034 + * tree-vect-loop.cc (vectorizable_reduction): Cleanup + reduction chain following code. + +2025-07-11 Jan Hubicka <hubicka@ucw.cz> + + * opts.cc (finish_options): Enable debug_nonbind_markers_p for + auto-profile. + * tree-cfg.cc (struct locus_discrim_map): Remove. + (struct locus_discrim_hasher): Remove. + (locus_discrim_hasher::hash): Remove. + (locus_discrim_hasher::equal): Remove. + (first_non_label_nondebug_stmt): Remove. + (build_gimple_cfg): Do not allocate discriminator tables. + (next_discriminator_for_locus): Remove. + (same_line_p): Remove. + (struct discrim_entry): New structure. + (assign_discriminator): Rewrite. + (assign_discriminators): Rewrite. + +2025-07-11 Jan Hubicka <hubicka@ucw.cz> + + PR ipa/114790 + * cgraph.cc (cgraph_update_edges_for_call_stmt_node): Resolve devirtualization + if call statement was optimized out or turned to direct call. + +2025-07-11 Jakub Jelinek <jakub@redhat.com> + Martin Jambor <mjambor@suse.cz> + + PR ipa/121023 + * ipa-fnsummary.cc (compute_fn_summary): Disallow signature changes + on cfun->has_musttail functions. + +2025-07-11 Hu, Lin1 <lin1.hu@intel.com> + + PR target/91384 + * config/i386/i386.md: Add new peeophole2 for optimize *negsi_1 + followed by *cmpsi_ccno_1 with APX_F. + +2025-07-11 Richard Biener <rguenther@suse.de> + + * config/i386/i386.cc (ix86_vector_costs::add_stmt_cost): Use + the LHS of a scalar stmt to determine mode and whether it is FP. + +2025-07-10 Richard Sandiford <richard.sandiford@arm.com> + + * config/aarch64/aarch64.cc (aarch64_vector_costs::add_stmt_cost): + Guard VF-based costing with !m_costing_for_scalar. + +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + * internal-fn.cc (expand_ACCESS_WITH_SIZE): Update comments. + * internal-fn.def (ACCESS_WITH_SIZE): Update comments. + * tree-object-size.cc (access_with_size_object_size): Update comments. + Adjust the arguments per the new design. + +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + PR middle-end/121000 + * internal-fn.cc (expand_ACCESS_WITH_SIZE): Update comments. + * internal-fn.def (ACCESS_WITH_SIZE): Update comments. + * tree-object-size.cc (access_with_size_object_size): Update comments. + Get the element_size from the 6th argument directly. + +2025-07-10 Richard Sandiford <richard.sandiford@arm.com> + + * config/aarch64/aarch64-sve2.md (aarch64_gather_ld1q): Replace with... + (@aarch64_gather_ld1q<mode>): ...this, parameterizing based on mode. + * config/aarch64/aarch64-sve-builtins-sve2.cc + (svld1q_gather_impl::expand): Update accordingly. + (svst1q_scatter_impl::expand): Use aarch64_sve_reinterpret + instead of force_lowpart_subreg. + +2025-07-10 Jan Hubicka <hubicka@ucw.cz> + + * auto-profile.cc: Include output.h. + (function_instance::set_call_location): Also sanity check + that location is known. + (raw_symbol_name): Two new static functions. + (dump_inline_stack): Use it. + (string_table::get_index_by_decl): Likewise. + (function_instance::get_cgraph_node): Likewise. + (function_instance::get_function_instance_by_decl): Fix typo + in warning; use raw names; fix lineno decoding. + (match_with_target): Add containing funciton parameter; + correctly output function and call location in warning. + (function_instance::lookup_count): Fix warning locations. + (function_instance::match): Fix warning locations; avoid + crash with mismatched callee; do not warn about broken callsites + twice. + (autofdo_source_profile::offline_external_functions): Use + raw_assembler_name. + (walk_block): Use raw_assembler_name. + +2025-07-10 Robin Dapp <rdapp@ventanamicro.com> + + PR target/121014 + * cfgexpand.cc (expand_debug_expr): Assert FLOAT_MODE_P. + * optabs-tree.cc (optab_for_tree_code): Assert FLOAT_TYPE_P. + * tree-vect-loop.cc (vect_get_loop_len): Use EXACT_DIV_EXPR. + +2025-07-10 Robin Dapp <rdapp@ventanamicro.com> + + PR target/118734 + * config/riscv/constraints.md (Wdm): Use tunable for Wdm + constraint. + * config/riscv/riscv-protos.h (emit_avltype_insn): Declare. + (can_be_broadcasted_p): Rename to... + (can_be_broadcast_p): ...this. + * config/riscv/predicates.md: Use renamed function. + (strided_load_broadcast_p): Declare. + * config/riscv/riscv-selftests.cc (run_broadcast_selftests): + Only run broadcast selftest if strided broadcasts are OK. + * config/riscv/riscv-v.cc (emit_avltype_insn): New function. + (sew64_scalar_helper): Only emit a pred_broadcast if the new + tunable says so. + (can_be_broadcasted_p): Rename to... + (can_be_broadcast_p): ...this and use new tunable. + * config/riscv/riscv.cc (struct riscv_tune_param): Add strided + broad tunable. + (strided_load_broadcast_p): Implement. + * config/riscv/vector.md: Use strided_load_broadcast_p () and + work around 64-bit broadcast on rv32 targets. + +2025-07-10 Co-authored-by: Jeff Law <jlaw@ventanamicro.com> + + * config/riscv/riscv.cc (riscv_fusion_pairs): Add new cases. + (riscv_set_is_add): New function. + (riscv_set_is_addi, riscv_set_is_adduw, riscv_set_is_shNadd): Likewise. + (riscv_set_is_shNadduw): Likewise. + (riscv_macro_fusion_pair_p): Add new fusion cases. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-slp.cc (vect_analyze_slp): Fail for non-canonical + gconds. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-slp.cc (vect_build_slp_instance): Do not use + SLP_TREE_VECTYPE to determine the conversion back to the + reduction IV. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.cc (vectorizable_reduction): Avoid + vect_is_simple_use and record a vector type if we come + up with one. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-stmts.cc (get_load_store_type): Do not use + vect_is_simple_use to fill gather/scatter offset operand + vectype and dt. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.cc (vect_model_reduction_cost): Get SLP + node instead of stmt_info and use that when recording costs. + +2025-07-10 Kyrylo Tkachov <ktkachov@nvidia.com> + + PR target/120999 + * config/aarch64/aarch64-sve2.md (*aarch64_sve2_nor<mode>): + Adjust movprfx alternative. + +2025-07-10 Richard Sandiford <richard.sandiford@arm.com> + + * doc/sourcebuild.texi (aarch64_sve2_hw, aarch64_sve2p1_hw): Document. + * config/aarch64/aarch64.cc (aarch64_evpc_hvla): Extend to + BYTES_BIG_ENDIAN. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vectorizer.h (vect_analyze_stmt): Remove stmt-info + and need_to_vectorize arguments. + * tree-vect-slp.cc (vect_slp_analyze_node_operations_1): + Adjust. + * tree-vect-stmts.cc (can_vectorize_live_stmts): Remove + stmt_info argument and remove non-SLP path. + (vect_analyze_stmt): Remove stmt_info and need_to_vectorize + argument and prune paths no longer reachable. + (vect_transform_stmt): Adjust. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * config/i386/x86-tune.def: Change "Tunning the" to "tuning" in + comment and use semicolon instead of dot in comment. + * loop-unroll.cc (decide_unroll_stupid): Comment spelling fix, + tunning -> tuning. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * tree-vect-loop.cc (scale_profile_for_vect_loop): Comment + spelling fix: bellow -> below. + * ipa-polymorphic-call.cc (record_known_type): Likewise. + * config/i386/x86-tune.def: Likewise. + * config/riscv/vector.md (*vsetvldi_no_side_effects_si_extend): + Likewise. + * tree-scalar-evolution.cc (iv_can_overflow_p): Likewise. + * ipa-devirt.cc (add_type_duplicate): Likewise. + * tree-ssa-loop-niter.cc (maybe_lower_iteration_bound): Likewise. + * gimple-ssa-sccopy.cc: Likewise. + * cgraphunit.cc: Likewise. + * graphite.h (struct poly_dr): Likewise. + * ipa-reference.cc (ignore_edge_p): Likewise. + * tree-ssa-alias.cc (ao_compare::compare_ao_refs): Likewise. + * profile-count.h (profile_probability::probably_reliable_p): + Likewise. + * ipa-inline-transform.cc (inline_call): Likewise. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.cc (vect_dissolve_slp_only_groups): Remove. + (vect_analyze_loop_2): Do not call it. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.cc (vect_active_double_reduction_p): Remove. + (vect_analyze_loop_operations): Remove. + (vect_analyze_loop_2): Do not call it. + +2025-07-10 Richard Biener <rguenther@suse.de> + + * tree-vect-loop.cc (vect_determine_vf_for_stmt_1): Rename + to ... + (vect_determine_vectype_for_stmt_1): ... this and only set + STMT_VINFO_VECTYPE. Fail for single-element vector types. + (vect_determine_vf_for_stmt): Rename to ... + (vect_determine_vectype_for_stmt): ... this and only set + STMT_VINFO_VECTYPE. Fail for single-element vector types. + (vect_determine_vectorization_factor): Rename to ... + (vect_set_stmts_vectype): ... this and only set STMT_VINFO_VECTYPE. + (vect_update_vf_for_slp): Remove. + (vect_analyze_loop_operations): Remove walk over stmts. + (vect_analyze_loop_2): Call vect_set_stmts_vectype instead of + vect_determine_vectorization_factor. Set vectorization factor + from LOOP_VINFO_SLP_UNROLLING_FACTOR. Fail if vect_detect_hybrid_slp + detects hybrid stmts or when vect_make_slp_decision finds + nothing to SLP. + * tree-vect-slp.cc (vect_detect_hybrid_slp): Move check + whether we have any hybrid stmts here from vect_update_vf_for_slp + * tree-vect-stmts.cc (vect_analyze_stmt): Remove loop over + stmts. + * tree-vectorizer.h (vect_detect_hybrid_slp): Update. + 2025-07-09 Richard Sandiford <richard.sandiford@arm.com> * config/aarch64/aarch64.cc (aarch64_simd_valid_imm): Account diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index 12032ce..e177aad 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20250710 +20250712 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index d5ceccc..bdb5292 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1856,11 +1856,13 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \ diagnostic-format-sarif.o \ diagnostic-format-text.o \ diagnostic-global-context.o \ + diagnostic-digraphs.o \ diagnostic-macro-unwinding.o \ diagnostic-output-spec.o \ diagnostic-path.o \ diagnostic-path-output.o \ diagnostic-show-locus.o \ + diagnostic-state-graphs.o \ diagnostic-state-to-dot.o \ edit-context.o \ graphviz.o pex.o \ diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index d88e73f..49b4b95 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,8 @@ +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * par-load.adb: Comment spelling fix: bellow -> below. + * libgnarl/s-taskin.ads: Likewise. + 2025-07-04 Eric Botcazou <ebotcazou@adacore.com> * gcc-interface/Make-lang.in (ACATSDIR): Change to acats-4. diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index d31cbbc..a34fa13 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,41 @@ +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * ana-state-to-diagnostic-state.cc: Reimplement, replacing + XML-based implementation with one based on state graphs. + * ana-state-to-diagnostic-state.h: Likewise. + * checker-event.cc: Replace include of "xml.h" with include of + "diagnostic-state-graphs.h". + (checker_event::maybe_make_xml_state): Replace with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * checker-event.h: Add include of "diagnostic-digraphs.h". + (checker_event::maybe_make_xml_state): Replace decl with... + (checker_event::maybe_make_diagnostic_state_graph): ...this. + * engine.cc (exploded_node::on_stmt_pre): Replace + "_analyzer_dump_xml" with "__analyzer_dump_sarif". + * program-state.cc: Replace include of "diagnostic-state.h" with + "diagnostic-state-graphs.h". + (program_state::dump_dot): Port from XML to state graphs. + * program-state.h: Drop reduntant forward decl of xml::document. + (program_state::make_xml): Replace decl with... + (program_state::make_diagnostic_state_graph): ...this. + (program_state::dump_xml_to_pp): Drop decl. + (program_state::dump_xml_to_file): Drop decl. + (program_state::dump_xml): Drop decl. + (program_state::dump_dump_sarif): New decl. + * sm-malloc.cc (get_dynalloc_state_for_state): New. + (malloc_state_machine::add_state_to_xml): Replace with... + (malloc_state_machine::add_state_to_state_graph): ...this. + * sm.cc (state_machine::add_state_to_xml): Replace with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace with... + (state_machine::add_global_state_to_state_graph): ...this. + * sm.h (class xml_state): Drop forward decl. + (class analyzer_state_graph): New forward decl. + (state_machine::add_state_to_xml): Replace decl with... + (state_machine::add_state_to_state_graph): ...this. + (state_machine::add_global_state_to_xml): Replace decl with... + (state_machine::add_global_state_to_state_graph): ...this. + 2025-06-30 David Malcolm <dmalcolm@redhat.com> * access-diagram.cc: Use nullptr rather than NULL where diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.cc b/gcc/analyzer/ana-state-to-diagnostic-state.cc index b85a2f1..2701259 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.cc +++ b/gcc/analyzer/ana-state-to-diagnostic-state.cc @@ -1,4 +1,4 @@ -/* Converting ana::program_state to XML state documents. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -23,8 +23,8 @@ along with GCC; see the file COPYING3. If not see #define INCLUDE_SET #include "analyzer/common.h" -#include "xml.h" -#include "xml-printer.h" +#include "diagnostic-state-graphs.h" +#include "diagnostic-format-sarif.h" #include "analyzer/region-model.h" #include "analyzer/program-state.h" @@ -39,57 +39,47 @@ along with GCC; see the file COPYING3. If not see namespace ana { +using namespace ::diagnostics::state_graphs; + static void -set_wi_attr (xml::element &e, +set_wi_attr (state_node_ref state_node, const char *attr_name, const wide_int_ref &w, signop sgn) { pretty_printer pp; pp_wide_int (&pp, w, sgn); - e.set_attr (attr_name, pp_formatted_text (&pp)); + state_node.set_attr (attr_name, pp_formatted_text (&pp)); } static void -set_type_attr (xml::element &e, const_tree type) +set_type_attr (state_node_ref state_node, const_tree type) { gcc_assert (type); pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%T", type); - e.set_attr ("type", pp_formatted_text (&pp)); + state_node.set_type (pp_formatted_text (&pp)); } static void -set_bits_attr (xml::element &e, +set_bits_attr (state_node_ref state_node, bit_range bits) { pretty_printer pp; bits.dump_to_pp (&pp); - e.set_attr ("bits", pp_formatted_text (&pp)); + state_node.set_attr ("bits", pp_formatted_text (&pp)); } -static void -set_region_id_attr (xml::element &e, - const region ®) -{ - e.set_attr ("region_id", std::to_string (reg.get_id ())); -} +// class analyzer_state_graph : public diagnostics::digraphs::digraph -// class xml_state : public xml::document - -xml_state::xml_state (const program_state &state, - const extrinsic_state &ext_state) -: xml::document (), - m_state (state), +analyzer_state_graph::analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state) +: m_state (state), m_ext_state (ext_state), m_mgr (*ext_state.get_engine ()->get_model_manager ()), - m_root (nullptr) + m_next_id (0) { - auto root = std::make_unique<xml::element> ("state-diagram", false); - m_root = root.get (); - add_child (std::move (root)); - /* Find pointers to heap-allocated regions, and record their types, so that we have a user-friendly way of showing the memory (by field, rather than by byte offset). */ @@ -117,14 +107,14 @@ xml_state::xml_state (const program_state &state, for (int i = state.m_region_model->get_stack_depth () - 1; i >= 0; --i) { const frame_region *reg = state.m_region_model->get_frame_at_index (i); - get_or_create_element (*reg); + get_or_create_state_node (*reg); } /* Create bound memory. */ for (auto iter : *state.m_region_model->get_store ()) { const bool create_all = false; // "true" for verbose, for debugging - create_elements_for_binding_cluster (*iter.second, create_all); + create_state_nodes_for_binding_cluster (*iter.second, create_all); } /* TODO: Constraints. */ @@ -137,52 +127,165 @@ xml_state::xml_state (const program_state &state, { auto &sm = ext_state.get_sm (i); for (const auto &iter : *smap) - sm.add_state_to_xml (*this, *iter.first, iter.second.m_state); + sm.add_state_to_state_graph (*this, *iter.first, iter.second.m_state); if (auto s = smap->get_global_state ()) - sm.add_global_state_to_xml (*this, s); + sm.add_global_state_to_state_graph (*this, s); } } + + /* Process pending edges. */ + while (m_pending_edges.size () > 0) + { + pending_edge item = m_pending_edges.back (); + m_pending_edges.pop_back (); + + /* 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); + } } -xml::element & -xml_state::get_or_create_element (const region ®) +state_node_ref +analyzer_state_graph::get_or_create_state_node (const region ®) { - auto existing = m_region_to_element_map.find (®); - if (existing != m_region_to_element_map.end ()) + auto existing = m_region_to_state_node_map.find (®); + if (existing != m_region_to_state_node_map.end ()) return *existing->second; - auto &e = create_and_add_element (reg); - m_region_to_element_map[®] = &e; - return e; + auto ref = create_and_add_state_node (reg); + m_region_to_state_node_map[®] = &ref.m_node; + return ref; } -xml::element& -xml_state::create_and_add_element (const region ®) +state_node_ref +analyzer_state_graph::create_and_add_state_node (const region ®) { - auto e = create_element (reg); - xml::element &result = *e; + auto node = create_state_node (reg); + + state_node_ref 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)); + return result; + } + add_node (std::move (node)); + return result; +} + +std::string +analyzer_state_graph::make_node_id (const char *prefix) +{ + return std::string (prefix) + "-" + std::to_string (m_next_id++); +} + +std::string +analyzer_state_graph::make_node_id (const region ®) +{ + const char *prefix = nullptr; + switch (reg.get_kind ()) { - auto parent_element = &get_or_create_element (*parent_reg); - parent_element->add_child (std::move (e)); + case RK_ROOT: + default: + gcc_unreachable (); + break; + + case RK_GLOBALS: + return "globals"; + case RK_CODE: + return "code"; + case RK_STACK: + return "stack"; + case RK_HEAP: + return "heap"; + + case RK_FRAME: + prefix = "frame-region"; + break; + case RK_FUNCTION: + prefix = "function-region"; + break; + case RK_LABEL: + prefix = "label-region"; + break; + case RK_THREAD_LOCAL: + prefix = "thread-local-region"; + break; + case RK_SYMBOLIC: + prefix = "symbolic-region"; + break; + case RK_DECL: + prefix = "decl-region"; + break; + case RK_FIELD: + prefix = "field-region"; + break; + case RK_ELEMENT: + prefix = "element-region"; + break; + case RK_OFFSET: + prefix = "offset-region"; + break; + case RK_SIZED: + prefix = "sized-region"; + break; + case RK_CAST: + prefix = "cast-region"; + break; + case RK_HEAP_ALLOCATED: + prefix = "heap-allocated-region"; + break; + case RK_ALLOCA: + prefix = "alloca-region"; + break; + case RK_STRING: + prefix = "string-region"; + break; + case RK_BIT_RANGE: + prefix = "bit-range-region"; + break; + case RK_VAR_ARG: + prefix = "var-arg-region"; + break; + case RK_ERRNO: + prefix = "errno-region"; + break; + case RK_PRIVATE: + prefix = "private-region"; + break; + case RK_UNKNOWN: + prefix = "unknown-region"; + break; } - else - m_root->add_child (std::move (e)); - return result; + return std::string (prefix) + "-" + std::to_string (reg.get_id ()); +} + +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_state_node (diagnostics::state_graphs::node_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); + return node; } -std::unique_ptr<xml::element> -xml_state::make_memory_space_element (const char *label) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph:: +make_memspace_state_node (const region ®, + diagnostics::state_graphs::node_kind kind) { - auto e = std::make_unique<xml::element> ("memory-space", false); - e->set_attr ("label", label); - return e; + return make_state_node (kind, make_node_id (reg)); } -std::unique_ptr<xml::element> -xml_state::create_element (const region ®) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node (const region ®) { - std::unique_ptr<xml::element> e; + std::unique_ptr<diagnostics::digraphs::node> node; + switch (reg.get_kind ()) { default: @@ -190,110 +293,102 @@ xml_state::create_element (const region ®) case RK_FRAME: { - e = std::make_unique<xml::element> ("stack-frame", false); const frame_region &frame_reg = static_cast<const frame_region &> (reg); + + node = make_state_node (diagnostics::state_graphs::node_kind::stack_frame, + make_node_id (reg)); + node->set_logical_loc + (m_logical_loc_mgr.key_from_tree (frame_reg.get_fndecl ())); { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_printf (&pp, "%E", frame_reg.get_fndecl ()); - e->set_attr ("function", pp_formatted_text (&pp)); + node->set_attr (STATE_NODE_PREFIX, "function", + pp_formatted_text (&pp)); } } break; + case RK_GLOBALS: - e = make_memory_space_element ("Globals"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::globals); break; case RK_CODE: - e = make_memory_space_element ("Code"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::code); break; case RK_FUNCTION: - e = std::make_unique<xml::element> ("function", false); - // TODO - break; - case RK_LABEL: - e = std::make_unique<xml::element> ("label", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::function); // TODO break; + case RK_STACK: - e = std::make_unique<xml::element> ("stack", false); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::stack); break; case RK_HEAP: - e = make_memory_space_element ("Heap"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::heap_); break; case RK_THREAD_LOCAL: - e = make_memory_space_element ("Thread-local"); + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::thread_local_); break; case RK_ROOT: - e = std::make_unique<xml::element> ("memory-regions", false); + gcc_unreachable (); break; case RK_SYMBOLIC: - e = std::make_unique<xml::element> ("symbolic-region", false); - // TODO + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::other); break; + case RK_DECL: { - e = std::make_unique<xml::element> ("variable", false); + node = make_state_node (diagnostics::state_graphs::node_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 ()); - e->set_attr ("name", pp_formatted_text (&pp)); + node_ref.set_name (pp_formatted_text (&pp)); } - set_type_attr (*e, TREE_TYPE (decl_reg.get_decl ())); + set_type_attr (*node, TREE_TYPE (decl_reg.get_decl ())); } break; + case RK_FIELD: - e = std::make_unique<xml::element> ("field", false); - break; case RK_ELEMENT: - e = std::make_unique<xml::element> ("element", false); + /* These should be handled in populate_state_node_for_typed_region. */ + gcc_unreachable (); break; + + case RK_LABEL: case RK_OFFSET: - e = std::make_unique<xml::element> ("offset-region", false); - // TODO - break; case RK_SIZED: - e = std::make_unique<xml::element> ("sized-region", false); - // TODO - break; case RK_CAST: - e = std::make_unique<xml::element> ("cast-region", false); - // TODO - break; - case RK_HEAP_ALLOCATED: - e = std::make_unique<xml::element> ("heap-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; - case RK_ALLOCA: - e = std::make_unique<xml::element> ("alloca-buffer", false); - set_attr_for_dynamic_extents (reg, *e); - break; case RK_STRING: - e = std::make_unique<xml::element> ("string-region", false); - // TODO - break; case RK_BIT_RANGE: - e = std::make_unique<xml::element> ("RK_BIT_RANGE", false); // TODO - break; case RK_VAR_ARG: - e = std::make_unique<xml::element> ("RK_VAR_ARG", false); // TODO - break; case RK_ERRNO: - e = std::make_unique<xml::element> ("errno", false); - break; case RK_PRIVATE: - e = std::make_unique<xml::element> ("RK_PRIVATE", false); // TODO - break; case RK_UNKNOWN: - e = std::make_unique<xml::element> ("RK_UNKNOWN", false); // TODO + node = make_state_node (diagnostics::state_graphs::node_kind::other, + make_node_id (reg)); break; - } - gcc_assert (e); - set_region_id_attr (*e, reg); + case RK_HEAP_ALLOCATED: + case RK_ALLOCA: + node = make_memspace_state_node (reg, + diagnostics::state_graphs::node_kind::dynalloc_buffer); + set_attr_for_dynamic_extents (reg, *node); + break; + } + gcc_assert (node); if (reg.get_base_region () == ®) if (!reg.get_type ()) @@ -303,16 +398,17 @@ xml_state::create_element (const region ®) if (search != m_types_for_untyped_regions.end ()) { tree type_to_use = search->second; - set_type_attr (*e, type_to_use); + set_type_attr (*node, type_to_use); } } - return e; + return node; } void -xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all) +analyzer_state_graph:: +create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all) { /* TODO: - symbolic bindings @@ -326,12 +422,12 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, if (auto conc_key = key->dyn_cast_concrete_binding ()) conc_bindings[conc_key->get_bit_range ()] = svalue; if (const region *reg = svalue->maybe_get_region ()) - get_or_create_element (*reg); + get_or_create_state_node (*reg); } - auto &e = get_or_create_element (*cluster.get_base_region ()); + auto ref = get_or_create_state_node (*cluster.get_base_region ()); - e.add_child (create_element_for_conc_bindings (conc_bindings)); + ref.m_node.add_child (create_state_node_for_conc_bindings (conc_bindings)); const region *typed_reg = cluster.get_base_region (); if (!typed_reg->get_type ()) @@ -346,44 +442,45 @@ xml_state::create_elements_for_binding_cluster (const binding_cluster &cluster, } if (typed_reg->get_type ()) - populate_element_for_typed_region (e, - *typed_reg, - conc_bindings, - create_all); + populate_state_node_for_typed_region (ref, + *typed_reg, + conc_bindings, + create_all); else { // TODO } } -std::unique_ptr<xml::element> -xml_state::create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings) +std::unique_ptr<diagnostics::digraphs::node> +analyzer_state_graph::create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings) { - auto e = std::make_unique<xml::element> ("concrete-bindings", false); + auto node = make_state_node (diagnostics::state_graphs::node_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_element - = std::make_unique<xml::element> ("binding", false); - set_bits_attr (*binding_element, bits); + auto binding_state_node + = make_state_node (diagnostics::state_graphs::node_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_element->set_attr ("value", pp_formatted_text (&pp)); - if (auto svalue_element = create_element_for_svalue (sval)) - binding_element->add_child (std::move (svalue_element)); + binding_state_node->set_attr (STATE_NODE_PREFIX, "value", + pp_formatted_text (&pp)); } - e->add_child (std::move (binding_element)); + node->add_child (std::move (binding_state_node)); } - return e; + return node; } // Try to get the bit_range of REG within its base region bool -xml_state::get_bit_range_within_base_region (const region ®, - bit_range &out) +analyzer_state_graph::get_bit_range_within_base_region (const region ®, + bit_range &out) { region_offset start_offset = reg.get_offset (&m_mgr); if (!start_offset.concrete_p ()) @@ -398,30 +495,28 @@ xml_state::get_bit_range_within_base_region (const region ®, } void -xml_state::populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all) +analyzer_state_graph:: +populate_state_node_for_typed_region (state_node_ref node, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all) { const_tree reg_type = reg.get_type (); gcc_assert (reg_type); - set_type_attr (e, reg_type); + set_type_attr (node, reg_type); bit_range bits (0, 0); if (get_bit_range_within_base_region (reg, bits)) { - set_bits_attr (e, bits); + set_bits_attr (node, bits); auto search = conc_bindings.find (bits); if (search != conc_bindings.end ()) { const svalue *bound_sval = search->second; - if (auto svalue_element = create_element_for_svalue (bound_sval)) - { - xml::printer xp (e); - xp.push_tag ("value-of-region"); - xp.append (std::move (svalue_element)); - } + node.set_json_attr ("value", bound_sval->to_json ()); + if (const region *dst_reg = bound_sval->maybe_get_region ()) + m_pending_edges.push_back ({node, *dst_reg}); } } @@ -454,22 +549,23 @@ xml_state::populate_element_for_typed_region (xml::element &e, = m_mgr.get_element_region (®, const_cast<tree> (element_type), sval_index); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - // Here "element" is in the xml sense - auto child_element - = std::make_unique<xml::element> ("element", false); - set_wi_attr (*child_element, "index", idx, UNSIGNED); - set_region_id_attr (*child_element, *child_reg); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::element, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "index", idx, UNSIGNED); + // Recurse: gcc_assert (element_type); - populate_element_for_typed_region (*child_element, - *child_reg, - conc_bindings, - create_all); - e.add_child (std::move (child_element)); + 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)); } } } @@ -485,15 +581,17 @@ xml_state::populate_element_for_typed_region (xml::element &e, const bit_range bits (0, item.m_bit_range.m_size_in_bits); const region *child_reg = m_mgr.get_bit_range (®, NULL_TREE, bits); - if (show_child_element_for_child_region_p (*child_reg, - conc_bindings, - create_all)) + if (show_child_state_node_for_child_region_p (*child_reg, + conc_bindings, + create_all)) { - auto child_element - = std::make_unique<xml::element> ("padding", false); - set_wi_attr (*child_element, "num_bits", + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_kind::padding, + make_node_id (*child_reg)); + set_wi_attr (*child_state_node, "num_bits", item.m_bit_range.m_size_in_bits, SIGNED); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } else @@ -501,26 +599,28 @@ xml_state::populate_element_for_typed_region (xml::element &e, const region *child_reg = m_mgr.get_field_region (®, const_cast<tree> (item.m_field)); - if (show_child_element_for_child_region_p (*child_reg, + if (show_child_state_node_for_child_region_p (*child_reg, conc_bindings, create_all)) { - auto child_element - = std::make_unique<xml::element> ("field", false); + auto child_state_node + = make_state_node + (diagnostics::state_graphs::node_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_element->set_attr ("name", - pp_formatted_text (&pp)); + child_state_node->set_attr (STATE_NODE_PREFIX, "name", + pp_formatted_text (&pp)); } - set_region_id_attr (*child_element, *child_reg); + // Recurse: - populate_element_for_typed_region (*child_element, + populate_state_node_for_typed_region (*child_state_node, *child_reg, conc_bindings, create_all); - e.add_child (std::move (child_element)); + node.m_node.add_child (std::move (child_state_node)); } } } @@ -530,7 +630,8 @@ xml_state::populate_element_for_typed_region (xml::element &e, } void -xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) +analyzer_state_graph::set_attr_for_dynamic_extents (const region ®, + state_node_ref node_ref) { const svalue *sval = m_state.m_region_model->get_dynamic_extents (®); if (sval) @@ -541,13 +642,13 @@ xml_state::set_attr_for_dynamic_extents (const region ®, xml::element &e) pp_wide_int (&pp, wi::to_wide (cst), UNSIGNED); else sval->dump_to_pp (&pp, true); - e.set_attr ("dynamic-extents", pp_formatted_text (&pp)); + node_ref.set_attr ("dynamic-extents", pp_formatted_text (&pp)); } } bool -xml_state:: -show_child_element_for_child_region_p (const region ®, +analyzer_state_graph:: +show_child_state_node_for_child_region_p (const region ®, const concrete_bindings_t &conc_bindings, bool create_all) { @@ -569,216 +670,18 @@ show_child_element_for_child_region_p (const region ®, return false; } -std::unique_ptr<xml::element> -xml_state::create_element_for_svalue (const svalue *sval) +std::unique_ptr<diagnostics::digraphs::digraph> +program_state:: +make_diagnostic_state_graph (const extrinsic_state &ext_state) const { - if (!sval) - return nullptr; - - std::unique_ptr<xml::element> result; - switch (sval->get_kind ()) - { - default: - gcc_unreachable (); - case SK_REGION: - { - const region_svalue *region_sval = (const region_svalue *)sval; - result - = std::make_unique<xml::element> ("pointer-to-region", false); - set_region_id_attr (*result, *region_sval->get_pointee ()); - } - break; - case SK_CONSTANT: - { - const constant_svalue *constant_sval = (const constant_svalue *)sval; - result = std::make_unique<xml::element> ("constant", false); - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - pp_printf (&pp, "%E", constant_sval->get_constant ()); - result->set_attr ("value", pp_formatted_text (&pp)); - } - break; - case SK_UNKNOWN: - result = std::make_unique<xml::element> ("unknown", false); - break; - case SK_POISONED: - { - const poisoned_svalue *poisoned_sval = (const poisoned_svalue *)sval; - switch (poisoned_sval->get_poison_kind ()) - { - default: - gcc_unreachable (); - case poison_kind::uninit: - result = std::make_unique<xml::element> ("uninitialized", false); - break; - case poison_kind::freed: - result = std::make_unique<xml::element> ("freed", false); - break; - case poison_kind::deleted: - result = std::make_unique<xml::element> ("deleted", false); - break; - case poison_kind::popped_stack: - result = std::make_unique<xml::element> ("popped-stack", false); - break; - } - } - break; - case SK_SETJMP: - { - //const setjmp_svalue *setjmp_sval = (const setjmp_svalue *)sval; - result = std::make_unique<xml::element> ("setjmp-buffer", false); - // TODO - } - break; - case SK_INITIAL: - { - const initial_svalue *initial_sval = (const initial_svalue *)sval; - result = std::make_unique<xml::element> ("initial-value-of", false); - set_region_id_attr (*result, *initial_sval->get_region ()); - } - break; - case SK_UNARYOP: - { - const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval; - result = std::make_unique<xml::element> ("unary-op", false); - result->set_attr ("op", get_tree_code_name (unaryop_sval->get_op ())); - result->add_child - (create_element_for_svalue (unaryop_sval->get_arg ())); - } - break; - case SK_BINOP: - { - const binop_svalue *binop_sval = (const binop_svalue *)sval; - result = std::make_unique<xml::element> ("binary-op", false); - result->set_attr ("op", get_tree_code_name (binop_sval->get_op ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg0 ())); - result->add_child (create_element_for_svalue (binop_sval->get_arg1 ())); - } - break; - case SK_SUB: - { - //const sub_svalue *sub_sval = (const sub_svalue *)sval; - result = std::make_unique<xml::element> ("subregion-value", false); - // TODO - } - break; - case SK_REPEATED: - { - const repeated_svalue *repeated_sval = (const repeated_svalue *)sval; - result = std::make_unique<xml::element> ("repeated-value", false); - result->add_child - (create_element_for_svalue (repeated_sval->get_outer_size ())); - result->add_child - (create_element_for_svalue (repeated_sval->get_inner_svalue ())); - } - break; - case SK_BITS_WITHIN: - { - const bits_within_svalue *bits_within_sval - = (const bits_within_svalue *)sval; - result = std::make_unique<xml::element> ("bits-within", false); - set_bits_attr (*result, bits_within_sval->get_bits ()); - result->add_child - (create_element_for_svalue (bits_within_sval->get_inner_svalue ())); - } - break; - case SK_UNMERGEABLE: - { - const unmergeable_svalue *unmergeable_sval - = (const unmergeable_svalue *)sval; - result = std::make_unique<xml::element> ("unmergeable", false); - result->add_child - (create_element_for_svalue (unmergeable_sval->get_arg ())); - } - break; - case SK_PLACEHOLDER: - { - const placeholder_svalue *placeholder_sval - = (const placeholder_svalue *)sval; - result = std::make_unique<xml::element> ("placeholder", false); - result->set_attr ("name", placeholder_sval->get_name ()); - } - break; - case SK_WIDENING: - { - //const widening_svalue *widening_sval = (const widening_svalue *)sval; - result = std::make_unique<xml::element> ("iterating-value", false); - // TODO - } - break; - case SK_COMPOUND: - { - //const compound_svalue *compound_sval = (const compound_svalue *)sval; - result = std::make_unique<xml::element> ("compound-value", false); - // TODO - } - break; - case SK_CONJURED: - { - //const conjured_svalue *conjured_sval = (const conjured_svalue *)sval; - result = std::make_unique<xml::element> ("conjured-value", false); - // TODO - } - break; - case SK_ASM_OUTPUT: - { - /* const asm_output_svalue *asm_output_sval - = (const asm_output_svalue *)sval; */ - result = std::make_unique<xml::element> ("asm-output", false); - // TODO - } - break; - case SK_CONST_FN_RESULT: - { - /* const const_fn_result_svalue *const_fn_result_sval - = (const const_fn_result_svalue *)sval; */ - result = std::make_unique<xml::element> ("const-fn-result", false); - // TODO - } - } - - if (result) - { - if (sval->get_type ()) - set_type_attr (*result, sval->get_type ()); - - pretty_printer pp; - pp_format_decoder (&pp) = default_tree_printer; - sval->dump_to_pp (&pp, true); - result->set_attr ("dump-text", pp_formatted_text (&pp)); - } - - return result; -} - -std::unique_ptr<xml::document> -program_state::make_xml (const extrinsic_state &ext_state) const -{ - return std::make_unique<xml_state> (*this, ext_state); -} - -void -program_state::dump_xml_to_pp (const extrinsic_state &ext_state, - pretty_printer *pp) const -{ - auto doc = make_xml (ext_state); - doc->write_as_xml (pp, 0, true); -} - -void -program_state::dump_xml_to_file (const extrinsic_state &ext_state, - FILE *outf) const -{ - pretty_printer pp; - pp.set_output_stream (outf); - dump_xml_to_pp (ext_state, &pp); - pp_flush (&pp); + return std::make_unique<analyzer_state_graph> (*this, ext_state); } void -program_state::dump_xml (const extrinsic_state &ext_state) const +program_state::dump_sarif (const extrinsic_state &ext_state) const { - dump_xml_to_file (ext_state, stderr); + auto g = make_diagnostic_state_graph (ext_state); + g->dump (); } } // namespace ana diff --git a/gcc/analyzer/ana-state-to-diagnostic-state.h b/gcc/analyzer/ana-state-to-diagnostic-state.h index bd6aa46..186e19d 100644 --- a/gcc/analyzer/ana-state-to-diagnostic-state.h +++ b/gcc/analyzer/ana-state-to-diagnostic-state.h @@ -1,4 +1,4 @@ -/* XML documents for dumping state in an easier-to-read form. +/* Creating diagnostic state graphs from ana::program_state. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -18,41 +18,52 @@ 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_ANALYZER_ANA_STATE_TO_XML_STATE_H -#define GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H +#ifndef GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H +#define GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H -#include "xml.h" +#include "diagnostic-state-graphs.h" +#include "tree-logical-location.h" namespace ana { -class xml_state : public xml::document +class analyzer_state_graph : public diagnostics::digraphs::digraph { public: - xml_state (const program_state &state, - const extrinsic_state &ext_state); - - xml::element & - get_or_create_element (const region ®); + analyzer_state_graph (const program_state &state, + const extrinsic_state &ext_state); + diagnostics::state_graphs::state_node_ref + get_or_create_state_node (const region ®); private: - xml::element& - create_and_add_element (const region ®); - - static std::unique_ptr<xml::element> - make_memory_space_element (const char *label); - - std::unique_ptr<xml::element> - create_element (const region ®); + struct pending_edge + { + diagnostics::state_graphs::state_node_ref m_src_node; + const region &m_dst_reg; + }; + + diagnostics::state_graphs::state_node_ref + create_and_add_state_node (const region ®); + + std::unique_ptr<diagnostics::digraphs::node> + make_state_node (diagnostics::state_graphs::node_kind kind, + std::string id); + + std::unique_ptr<diagnostics::digraphs::node> + make_memspace_state_node (const region ®, + enum diagnostics::state_graphs::node_kind kind); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node (const region ®); /* Spatially sorted concrete bindings. */ typedef std::map<bit_range, const svalue *> concrete_bindings_t; void - create_elements_for_binding_cluster (const binding_cluster &cluster, - bool create_all); + create_state_nodes_for_binding_cluster (const binding_cluster &cluster, + bool create_all); - std::unique_ptr<xml::element> - create_element_for_conc_bindings (const concrete_bindings_t &conc_bindings); + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_conc_bindings (const concrete_bindings_t &conc_bindings); // Try to get the bit_range of REG within its base region bool @@ -60,30 +71,36 @@ private: bit_range &out); void - populate_element_for_typed_region (xml::element &e, - const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + populate_state_node_for_typed_region (diagnostics::state_graphs::state_node_ref, + const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); void - set_attr_for_dynamic_extents (const region ®, xml::element &e); + set_attr_for_dynamic_extents (const region ®, + diagnostics::state_graphs::state_node_ref); bool - show_child_element_for_child_region_p (const region ®, - const concrete_bindings_t &conc_bindings, - bool create_all); + show_child_state_node_for_child_region_p (const region ®, + const concrete_bindings_t &conc_bindings, + bool create_all); + + std::unique_ptr<diagnostics::digraphs::node> + create_state_node_for_svalue (const svalue *sval); - std::unique_ptr<xml::element> - create_element_for_svalue (const svalue *sval); + std::string make_node_id (const region ®); + std::string make_node_id (const char *prefix); + tree_logical_location_manager m_logical_loc_mgr; const program_state &m_state; const extrinsic_state &m_ext_state; region_model_manager &m_mgr; - xml::element *m_root; - std::map<const region *, xml::element *> m_region_to_element_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; }; } // namespace ana -#endif /* GCC_ANALYZER_ANA_STATE_TO_XML_STATE_H */ +#endif /* GCC_ANALYZER_ANA_STATE_TO_DIAGNOSTIC_STATE_H */ diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index af336df..8cc5ac2 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "inlining-iterator.h" #include "tree-logical-location.h" #include "diagnostic-format-sarif.h" -#include "xml.h" +#include "diagnostic-state-graphs.h" #include "analyzer/analyzer-logging.h" #include "analyzer/sm.h" @@ -225,8 +225,8 @@ checker_event::prepare_for_emission (checker_path *path, print_desc (*pp.get ()); } -std::unique_ptr<xml::document> -checker_event::maybe_make_xml_state (bool debug) const +std::unique_ptr<diagnostics::digraphs::digraph> +checker_event::maybe_make_diagnostic_state_graph (bool debug) const { const program_state *state = get_program_state (); if (!state) @@ -235,14 +235,16 @@ checker_event::maybe_make_xml_state (bool debug) const gcc_assert (m_path); const extrinsic_state &ext_state = m_path->get_ext_state (); - auto result = state->make_xml (ext_state); + auto result = state->make_diagnostic_state_graph (ext_state); if (debug) { pretty_printer pp; text_art::theme *theme = global_dc->get_diagram_theme (); text_art::dump_to_pp (*state, theme, &pp); - result->add_comment (pp_formatted_text (&pp)); + result->set_attr (STATE_GRAPH_PREFIX, + "analyzer/program_state/", + pp_formatted_text (&pp)); } return result; diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index 7c44f1e..cf24e77 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-logical-location.h" #include "analyzer/program-state.h" #include "analyzer/event-loc-info.h" +#include "diagnostic-digraphs.h" namespace ana { @@ -128,12 +129,12 @@ public: virtual bool is_function_entry_p () const { return false; } virtual bool is_return_p () const { return false; } + std::unique_ptr<diagnostics::digraphs::digraph> + maybe_make_diagnostic_state_graph (bool debug) const final override; + virtual const program_state * get_program_state () const { return nullptr; } - std::unique_ptr<xml::document> - maybe_make_xml_state (bool debug) const final override; - /* For use with %@. */ const diagnostic_event_id_t *get_id_ptr () const { diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 67024e9..745ef7e 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -1580,9 +1580,9 @@ exploded_node::on_stmt_pre (exploded_graph &eg, state->dump (eg.get_ext_state (), true); return; } - else if (is_special_named_call_p (call, "__analyzer_dump_xml", 0)) + else if (is_special_named_call_p (call, "__analyzer_dump_sarif", 0)) { - state->dump_xml (eg.get_ext_state ()); + state->dump_sarif (eg.get_ext_state ()); return; } else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0)) diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index c0befac..85cbe75 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "digraph.h" #include "diagnostic-event-id.h" -#include "diagnostic-state.h" +#include "diagnostic-state-graphs.h" #include "graphviz.h" #include "text-art/tree-widget.h" @@ -1230,8 +1230,14 @@ program_state::make_dump_widget (const text_art::dump_widget_info &dwi) const void program_state::dump_dot (const extrinsic_state &ext_state) const { - auto doc = make_xml (ext_state); - auto graph = make_dot_graph_from_xml_state (*doc); + auto state_graph = make_diagnostic_state_graph (ext_state); + + gcc_assert (global_dc); + auto logical_loc_mgr = global_dc->get_logical_location_manager (); + gcc_assert (logical_loc_mgr); + + auto graph = diagnostics::state_graphs::make_dot_graph (*state_graph, + *logical_loc_mgr); pretty_printer pp; dot::writer w (pp); diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index e2076c1..4278237 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -26,8 +26,6 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/store.h" -namespace xml { class document; } - namespace ana { /* Data shared by all program_state instances. */ @@ -248,11 +246,14 @@ public: void dump (const extrinsic_state &ext_state, bool simple) const; void dump () const; - std::unique_ptr<xml::document> make_xml (const extrinsic_state &ext_state) const; - void dump_xml_to_pp (const extrinsic_state &ext_state, pretty_printer *pp) const; - void dump_xml_to_file (const extrinsic_state &ext_state, FILE *outf) const; - void dump_xml (const extrinsic_state &ext_state) const; - void dump_dot (const extrinsic_state &ext_state) const; + std::unique_ptr<diagnostics::digraphs::digraph> + make_diagnostic_state_graph (const extrinsic_state &ext_state) const; + + void + dump_sarif (const extrinsic_state &ext_state) const; + + void + dump_dot (const extrinsic_state &ext_state) const; std::unique_ptr<json::object> to_json (const extrinsic_state &ext_state) const; diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 3581dbb..2a218d0 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -436,9 +436,9 @@ public: const extrinsic_state &ext_state) const; void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const final override; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const final override; standard_deallocator_set m_free; standard_deallocator_set m_scalar_delete; @@ -2735,27 +2735,57 @@ 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 +get_dynalloc_state_for_state (enum resource_state rs) +{ + switch (rs) + { + default: + gcc_unreachable (); + case RS_START: + case RS_NULL: + case RS_NON_HEAP: + case RS_STOP: + return diagnostics::state_graphs::node_dynalloc_state::unknown; + + case RS_ASSUMED_NON_NULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + + case RS_UNCHECKED: + return diagnostics::state_graphs::node_dynalloc_state::unchecked; + case RS_NONNULL: + return diagnostics::state_graphs::node_dynalloc_state::nonnull; + case RS_FREED: + return diagnostics::state_graphs::node_dynalloc_state::freed; + } +} + void -malloc_state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +malloc_state_machine:: +add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { if (const region *reg = sval.maybe_get_region ()) { - auto ®_element = out_xml.get_or_create_element (*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_element.set_attr ("dynamic-alloc-state", state->get_name ()); + reg_node.set_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_element.set_attr ("expected-deallocators", pp_formatted_text (&pp)); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "expected-deallocators", + pp_formatted_text (&pp)); } if (alloc_state->m_deallocator) - reg_element.set_attr ("deallocator", - alloc_state->m_deallocator->m_name); + reg_node.m_node.set_attr (STATE_NODE_PREFIX, + "deallocator", + alloc_state->m_deallocator->m_name); } } diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index 840806a..c93e9c2 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -161,16 +161,16 @@ state_machine::to_json () const } void -state_machine::add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const +state_machine::add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const { // no-op } void -state_machine::add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const +state_machine::add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const { // no-op } diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h index 6298fb6..4633fac 100644 --- a/gcc/analyzer/sm.h +++ b/gcc/analyzer/sm.h @@ -28,7 +28,7 @@ namespace ana { class state_machine; class sm_context; class pending_diagnostic; -class xml_state; +class analyzer_state_graph; extern bool any_pointer_p (tree expr); extern bool any_pointer_p (const svalue *sval); @@ -188,13 +188,13 @@ public: state_t get_start_state () const { return m_start; } virtual void - add_state_to_xml (xml_state &out_xml, - const svalue &sval, - state_machine::state_t state) const; + add_state_to_state_graph (analyzer_state_graph &out_state_graph, + const svalue &sval, + state_machine::state_t state) const; virtual void - add_global_state_to_xml (xml_state &out_xml, - state_machine::state_t state) const; + add_global_state_to_state_graph (analyzer_state_graph &out_state_graph, + state_machine::state_t state) const; protected: state_t add_state (const char *name); diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 54dcb52..ed79d36 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,24 @@ +2025-07-11 Jakub Jelinek <jakub@redhat.com> + + PR c++/119064 + * c.opt (Wc++26-compat): New option. + * c.opt.urls: Regenerate. + * c-opts.cc (c_common_post_options): Clear warn_cxx26_compat for + C++26 or later. + * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine + __cpp_trivial_relocatability=202502L. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/117785 + * c-cppbuiltin.cc (c_cpp_builtins): Predefine + __cpp_constexpr_exceptions=202411L for C++26. + +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + * c-ubsan.cc (get_bound_from_access_with_size): Adjust the position + of the arguments per the new design. + 2025-07-07 Qing Zhao <qing.zhao@oracle.com> Revert: diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 0c84020..2f86b1f 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1096,6 +1096,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_pack_indexing=202311L"); cpp_define (pfile, "__cpp_pp_embed=202502L"); cpp_define (pfile, "__cpp_constexpr_virtual_inheritance=202506L"); + cpp_define (pfile, "__cpp_trivial_relocatability=202502L"); } if (flag_concepts && cxx_dialect > cxx14) cpp_define (pfile, "__cpp_concepts=202002L"); diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 3ee9444..872bdf4 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1165,6 +1165,9 @@ c_common_post_options (const char **pfilename) warn_cxx20_compat = 0; cpp_opts->cpp_warn_cxx20_compat = 0; } + if (cxx_dialect >= cxx26) + /* Don't warn about C++26 compatibility changes in C++26 or later. */ + warn_cxx26_compat = 0; /* C++17 has stricter evaluation order requirements; let's use some of them for earlier C++ as well, so chaining works as expected. */ diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 6a55e71..12877eb 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -497,6 +497,10 @@ Wc++20-compat C++ ObjC++ Var(warn_cxx20_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Init(0) CPP(cpp_warn_cxx20_compat) CppReason(CPP_W_CXX20_COMPAT) Warn about C++ constructs whose meaning differs between ISO C++ 2017 and ISO C++ 2020. +Wc++26-compat +C++ ObjC++ Var(warn_cxx26_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Init(0) +Warn about C++ constructs whose meaning differs between ISO C++ 2023 and ISO C++ 2026. + Wc++11-extensions C++ ObjC++ Var(warn_cxx11_extensions) Warning Init(1) Warn about C++11 constructs in code compiled with an older standard. diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls index 401e6e7..5c97593 100644 --- a/gcc/c-family/c.opt.urls +++ b/gcc/c-family/c.opt.urls @@ -190,6 +190,9 @@ UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b17-compat) Wc++20-compat UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b20-compat) +Wc++26-compat +UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b26-compat) + Wc++11-extensions UrlSuffix(gcc/Warning-Options.html#index-Wc_002b_002b11-extensions) diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index dcef284..bd139a7 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,14 @@ +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + * c-typeck.cc (build_access_with_size_for_counted_by): Update comments. + Adjust the arguments per the new design. + +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + PR middle-end/121000 + * c-typeck.cc (build_access_with_size_for_counted_by): Update comments. + Pass TYPE_SIZE_UNIT of the element as the 6th argument. + 2025-07-07 Qing Zhao <qing.zhao@oracle.com> Revert: diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc index 94a2e6e..32071a8 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -1790,6 +1790,19 @@ cgraph_update_edges_for_call_stmt_node (cgraph_node *node, if (e) { + /* If call was devirtualized during cloning, mark edge + as resolved. */ + if (e->speculative) + { + if (new_stmt && is_gimple_call (new_stmt)) + { + tree decl = gimple_call_fndecl (new_stmt); + if (decl) + e = cgraph_edge::resolve_speculation (e, decl); + } + else + e = cgraph_edge::resolve_speculation (e, NULL); + } /* Keep calls marked as dead dead. */ if (new_stmt && is_gimple_call (new_stmt) && e->callee && fndecl_built_in_p (e->callee->decl, BUILT_IN_UNREACHABLE, diff --git a/gcc/cobol/ChangeLog b/gcc/cobol/ChangeLog index 6c3e1bd..5555b8e 100644 --- a/gcc/cobol/ChangeLog +++ b/gcc/cobol/ChangeLog @@ -1,3 +1,71 @@ +2025-07-11 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> + + PR cobol/120621 + * lexio.cc (parse_replace_pairs): Cast mfile.lineno() to fmt_size_t. + * parse.y (intrinsic): Print ptrdiff_t using %ld, cast arg to long. + * scan_ante.h (numstr_of): Print nx using %ld, cast arg to long. + * util.cc (cbl_field_t::report_invalid_initial_value): Print + ptrdiff_t using %ld, cast arg to long. + +2025-07-10 James K. Lowden <jklowden@cobolworx.com> + + PR cobol/120765 + * cdf.y: Extend grammar for new CDF syntax, relocate dictionary. + * cdfval.h (cdf_dictionary): Use new CDF dictionary. + * dts.h: Remove useless assignment, note incorrect behavior. + * except.cc: Remove obsolete EC state. + * gcobol.1: Document CDF in its own section. + * genapi.cc (parser_statement_begin): Use new EC state function. + (parser_file_merge): Same. + (parser_check_fatal_exception): Same. + * genutil.cc (get_and_check_refstart_and_reflen): Same. + (get_depending_on_value_from_odo): Same. + (get_data_offset): Same. + (process_this_exception): Same. + * lexio.cc (check_push_pop_directive): New function. + (check_source_format_directive): Restrict regex search to 1 line. + (cdftext::free_form_reference_format): Use new function. + * parse.y: Define new CDF tokens, use new CDF state. + * parse_ante.h (cdf_tokens): Use new CDF state. + (redefined_token): Same. + (class prog_descr_t): Remove obsolete CDF state. + (class program_stack_t): Same. + (current_call_convention): Same. + * scan.l: Recognize new CDF tokens. + * scan_post.h (is_cdf_token): Same. + * symbols.h (cdf_current_tokens): Change current_call_convention to return void. + * token_names.h: Regenerate. + * udf/stored-char-length.cbl: Use new PUSH/POP CDF functionality. + * util.cc (class cdf_directives_t): Define cdf_directives_t. + (current_call_convention): Same. + (cdf_current_tokens): Same. + (cdf_dictionary): Same. + (cdf_enabled_exceptions): Same. + (cdf_push): Same. + (cdf_push_call_convention): Same. + (cdf_push_current_tokens): Same. + (cdf_push_dictionary): Same. + (cdf_push_enabled_exceptions): Same. + (cdf_push_source_format): Same. + (cdf_pop): Same. + (cdf_pop_call_convention): Same. + (cdf_pop_current_tokens): Same. + (cdf_pop_dictionary): Same. + (cdf_pop_enabled_exceptions): Same. + (cdf_pop_source_format): Same. + * util.h (cdf_push): Declare cdf_directives_t. + (cdf_push_call_convention): Same. + (cdf_push_current_tokens): Same. + (cdf_push_dictionary): Same. + (cdf_push_enabled_exceptions): Same. + (cdf_push_source_format): Same. + (cdf_pop): Same. + (cdf_pop_call_convention): Same. + (cdf_pop_current_tokens): Same. + (cdf_pop_dictionary): Same. + (cdf_pop_source_format): Same. + (cdf_pop_enabled_exceptions): Same. + 2025-07-09 Robert Dubner <rdubner@symas.com> James K. Lowden <jklowden@cobolworx.com> diff --git a/gcc/cobol/lexio.cc b/gcc/cobol/lexio.cc index 0aebe52..dc632c2 100644 --- a/gcc/cobol/lexio.cc +++ b/gcc/cobol/lexio.cc @@ -1904,7 +1904,7 @@ cdftext::process_file( filespan_t mfile, int output, bool second_pass ) { []( char ch ) { return ch == '\n'; } ); struct { int in, out; filespan_t mfile; } copy; dbgmsg("%s:%d: line " HOST_SIZE_T_PRINT_UNSIGNED ", opening %s on fd %d", - __func__, __LINE__,mfile.lineno(), + __func__, __LINE__, (fmt_size_t)mfile.lineno(), copybook.source(), copybook.current()->fd); copy.in = copybook.current()->fd; copy.mfile = free_form_reference_format( copy.in ); diff --git a/gcc/cobol/parse.y b/gcc/cobol/parse.y index 83bffdf..7bcbf74 100644 --- a/gcc/cobol/parse.y +++ b/gcc/cobol/parse.y @@ -10316,8 +10316,8 @@ intrinsic: function_udf if( p != NULL ) { auto loc = symbol_field_location(field_index(p->field)); error_msg(loc, "FUNCTION %qs has " - "inconsistent parameter type %zu (%qs)", - keyword_str($1), p - args.data(), name_of(p->field) ); + "inconsistent parameter type %ld (%qs)", + keyword_str($1), (long)(p - args.data()), name_of(p->field) ); YYERROR; } $$ = is_numeric(args[0].field)? diff --git a/gcc/cobol/scan_ante.h b/gcc/cobol/scan_ante.h index 6960739..88a8e8c 100644 --- a/gcc/cobol/scan_ante.h +++ b/gcc/cobol/scan_ante.h @@ -149,7 +149,7 @@ numstr_of( const char string[], radix_t radix = decimal_e ) { } auto nx = std::count_if(input, p, fisdigit); if( 36 < nx ) { - error_msg(yylloc, "significand of %s has more than 36 digits (%zu)", input, nx); + error_msg(yylloc, "significand of %s has more than 36 digits (%ld)", input, (long)nx); return NO_CONDITION; } diff --git a/gcc/cobol/util.cc b/gcc/cobol/util.cc index c85b4cb..854bd7f 100644 --- a/gcc/cobol/util.cc +++ b/gcc/cobol/util.cc @@ -1058,8 +1058,8 @@ cbl_field_t::report_invalid_initial_value(const YYLTYPE& loc) const { return TOUPPER(ch) == 'E'; } ); if( !has_exponent && data.precision() < pend - p ) { - error_msg(loc, "%s cannot represent VALUE %qs exactly (max %c%zu)", - name, data.initial, '.', pend - p); + error_msg(loc, "%s cannot represent VALUE %qs exactly (max %c%ld)", + name, data.initial, '.', (long)(pend - p)); } } } diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md index 8de79ca..75192dd 100644 --- a/gcc/config/aarch64/aarch64-simd.md +++ b/gcc/config/aarch64/aarch64-simd.md @@ -9180,17 +9180,42 @@ ;; sha3 (define_insn "eor3q<mode>4" - [(set (match_operand:VQ_I 0 "register_operand" "=w") - (xor:VQ_I - (xor:VQ_I - (match_operand:VQ_I 2 "register_operand" "w") - (match_operand:VQ_I 3 "register_operand" "w")) - (match_operand:VQ_I 1 "register_operand" "w")))] + [(set (match_operand:VDQ_I 0 "register_operand" "=w") + (xor:VDQ_I + (xor:VDQ_I + (match_operand:VDQ_I 2 "register_operand" "w") + (match_operand:VDQ_I 3 "register_operand" "w")) + (match_operand:VDQ_I 1 "register_operand" "w")))] "TARGET_SHA3" "eor3\\t%0.16b, %1.16b, %2.16b, %3.16b" [(set_attr "type" "crypto_sha3")] ) +(define_insn_and_split "*eor3qdi4" + [(set (match_operand:DI 0 "register_operand") + (xor:DI + (xor:DI + (match_operand:DI 2 "register_operand") + (match_operand:DI 3 "register_operand")) + (match_operand:DI 1 "register_operand")))] + "TARGET_SHA3" + {@ [ cons: =0, 1, 2 , 3 ; attrs: type ] + [ w , w, w , w ; crypto_sha3 ] eor3\t%0.16b, %1.16b, %2.16b, %3.16b + [ &r , r, r0, r0 ; multiple ] # + } + "&& REG_P (operands[0]) && GP_REGNUM_P (REGNO (operands[0]))" + [(set (match_dup 4) (xor:DI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (xor:DI (match_dup 4) (match_dup 1)))] + { + if (reload_completed) + operands[4] = operands[0]; + else if (can_create_pseudo_p ()) + operands[4] = gen_reg_rtx (DImode); + else + FAIL; + } +) + (define_insn "aarch64_rax1qv2di" [(set (match_operand:V2DI 0 "register_operand" "=w") (xor:V2DI @@ -9241,17 +9266,46 @@ ) (define_insn "bcaxq<mode>4" - [(set (match_operand:VQ_I 0 "register_operand" "=w") - (xor:VQ_I - (and:VQ_I - (not:VQ_I (match_operand:VQ_I 3 "register_operand" "w")) - (match_operand:VQ_I 2 "register_operand" "w")) - (match_operand:VQ_I 1 "register_operand" "w")))] + [(set (match_operand:VDQ_I 0 "register_operand" "=w") + (xor:VDQ_I + (and:VDQ_I + (not:VDQ_I (match_operand:VDQ_I 3 "register_operand" "w")) + (match_operand:VDQ_I 2 "register_operand" "w")) + (match_operand:VDQ_I 1 "register_operand" "w")))] "TARGET_SHA3" "bcax\\t%0.16b, %1.16b, %2.16b, %3.16b" [(set_attr "type" "crypto_sha3")] ) +(define_insn_and_split "*bcaxqdi4" + [(set (match_operand:DI 0 "register_operand") + (xor:DI + (and:DI + (not:DI (match_operand:DI 3 "register_operand")) + (match_operand:DI 2 "register_operand")) + (match_operand:DI 1 "register_operand")))] + "TARGET_SHA3" + {@ [ cons: =0, 1, 2 , 3 ; attrs: type ] + [ w , w, w , w ; crypto_sha3 ] bcax\t%0.16b, %1.16b, %2.16b, %3.16b + [ &r , r, r0, r0 ; multiple ] # + } + "&& REG_P (operands[0]) && GP_REGNUM_P (REGNO (operands[0]))" + [(set (match_dup 4) + (and:DI (not:DI (match_dup 3)) + (match_dup 2))) + (set (match_dup 0) + (xor:DI (match_dup 4) + (match_dup 1)))] + { + if (reload_completed) + operands[4] = operands[0]; + else if (can_create_pseudo_p ()) + operands[4] = gen_reg_rtx (DImode); + else + FAIL; + } +) + ;; SM3 (define_insn "aarch64_sm3ss1qv4si" diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 10b8ed5..6e16763 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -26960,12 +26960,23 @@ aarch64_evpc_tbl (struct expand_vec_perm_d *d) static bool aarch64_evpc_sve_tbl (struct expand_vec_perm_d *d) { - unsigned HOST_WIDE_INT nelt; + if (!d->one_vector_p) + { + /* aarch64_expand_sve_vec_perm does not yet handle variable-length + vectors. */ + if (!d->perm.length ().is_constant ()) + return false; - /* Permuting two variable-length vectors could overflow the - index range. */ - if (!d->one_vector_p && !d->perm.length ().is_constant (&nelt)) - return false; + /* This permutation reduces to the vec_perm optab if the elements are + large enough to hold all selector indices. Do not handle that case + here, since the general TBL+SUB+TBL+ORR sequence is too expensive to + be considered a "native" constant permutation. + + Not doing this would undermine code that queries can_vec_perm_const_p + with allow_variable_p set to false. See PR121027. */ + if (selector_fits_mode_p (d->vmode, d->perm)) + return false; + } if (d->testing_p) return true; diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index ad7360e..313522b 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -25798,6 +25798,14 @@ ix86_vector_costs::add_stmt_cost (int count, vect_cost_for_stmt kind, if (scalar_p) mode = TYPE_MODE (TREE_TYPE (vectype)); } + /* When we are costing a scalar stmt use the scalar stmt to get at the + type of the operation. */ + else if (scalar_p && stmt_info) + if (tree lhs = gimple_get_lhs (stmt_info->stmt)) + { + fp = FLOAT_TYPE_P (TREE_TYPE (lhs)); + mode = TYPE_MODE (TREE_TYPE (lhs)); + } if ((kind == vector_stmt || kind == scalar_stmt) && stmt_info diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 5825aca..83c438b 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -14769,6 +14769,17 @@ (compare:CCZ (neg:SWI (match_dup 0)) (const_int 0))) (set (match_dup 0) (neg:SWI (match_dup 0)))])]) +;; Optimize *negsi_1 followed by *cmpsi_ccno_1 (PR target/91384) with APX_F +(define_peephole2 + [(parallel [(set (match_operand:SWI 0 "general_reg_operand") + (neg:SWI (match_operand:SWI 1 "general_reg_operand"))) + (clobber (reg:CC FLAGS_REG))]) + (set (reg:CCZ FLAGS_REG) (compare:CCZ (match_dup 1) (const_int 0)))] + "TARGET_APX_NDD" + [(parallel [(set (reg:CCZ FLAGS_REG) + (compare:CCZ (neg:SWI (match_dup 1)) (const_int 0))) + (set (match_dup 0) (neg:SWI (match_dup 1)))])]) + ;; Special expand pattern to handle integer mode abs (define_expand "abs<mode>2" diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 79873a2..b0928d0 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,262 @@ +2025-07-11 Jakub Jelinek <jakub@redhat.com> + + PR c++/119064 + * cp-tree.h: Implement C++26 P2786R13 - Trivial Relocatability. + (struct lang_type): Add trivially_relocatable, + trivially_relocatable_computed, replaceable and replaceable_computed + bitfields. Change width of dummy from 2 to 30. + (CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT, + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED, CLASSTYPE_REPLACEABLE_BIT, + CLASSTYPE_REPLACEABLE_COMPUTED): Define. + (enum virt_specifier): Add VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE + and VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE enumerators. + (trivially_relocatable_type_p, replaceable_type_p): Declare. + * cp-trait.def (IS_NOTHROW_RELOCATABLE, IS_REPLACEABLE, + IS_TRIVIALLY_RELOCATABLE): New traits. + * parser.cc (cp_parser_class_property_specifier_seq_opt): Handle + trivially_relocatable_if_eligible, + __trivially_relocatable_if_eligible, replaceable_if_eligible and + __replaceable_if_eligible. + (cp_parser_class_head): Set CLASSTYPE_REPLACEABLE_BIT + and/or CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT if corresponding + conditional keywords were parsed and assert corresponding *_COMPUTED + macro is false. + * pt.cc (instantiate_class_template): Copy over also + CLASSTYPE_TRIVIALLY_RELOCATABLE_{BIT,COMPUTED} and + CLASSTYPE_REPLACEABLE_{BIT,COMPUTED} bits. + * semantics.cc (referenceable_type_p): Move definition earlier. + (trait_expr_value): Handle CPTK_IS_NOTHROW_RELOCATABLE, + CPTK_IS_REPLACEABLE and CPTK_IS_TRIVIALLY_RELOCATABLE. + (finish_trait_expr): Likewise. + * tree.cc (default_movable_type_p): New function. + (union_with_no_declared_special_member_fns): Likewise. + (trivially_relocatable_type_p): Likewise. + (replaceable_type_p): Likewise. + * constraint.cc (diagnose_trait_expr): Handle + CPTK_IS_NOTHROW_RELOCATABLE, CPTK_IS_REPLACEABLE and + CPTK_IS_TRIVIALLY_RELOCATABLE. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * cp-tree.h (struct lang_type): Add comment before key_method. + Remove lambda_expr. + (CLASSTYPE_KEY_METHOD): Give NULL_TREE if not TYPE_POLYMORPHIC_P. + (SET_CLASSTYPE_KEY_METHOD): Define. + (CLASSTYPE_LAMBDA_EXPR): Give NULL_TREE if TYPE_POLYMORPHIC_P. + Use key_method member instead of lambda_expr. + (SET_CLASSTYPE_LAMBDA_EXPR): Define. + * class.cc (determine_key_method): Use SET_CLASSTYPE_KEY_METHOD + macro. + * decl.cc (xref_tag): Use SET_CLASSTYPE_LAMBDA_EXPR macro. + * lambda.cc (begin_lambda_type): Likewise. + * module.cc (trees_in::read_class_def): Use SET_CLASSTYPE_LAMBDA_EXPR + and SET_CLASSTYPE_KEY_METHOD macros, assert lambda is NULL if + TYPE_POLYMORPHIC_P and otherwise assert key_method is NULL. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120628 + * parser.cc (cp_parser_elaborated_type_specifier): Use + cp_parser_nth_token_starts_class_definition_p with extra argument 1 + instead of cp_parser_next_token_starts_class_definition_p. + (cp_parser_class_property_specifier_seq_opt): For final conditional + keyword in C++98 check if the token after it isn't + cp_parser_nth_token_starts_class_definition_p nor CPP_NAME and in + that case break without consuming it nor warning. + (cp_parser_class_head): Use + cp_parser_nth_token_starts_class_definition_p with extra argument 1 + instead of cp_parser_next_token_starts_class_definition_p. + (cp_parser_next_token_starts_class_definition_p): Renamed to ... + (cp_parser_nth_token_starts_class_definition_p): ... this. Add N + argument. Use cp_lexer_peek_nth_token instead of cp_lexer_peek_token. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120569 + * parser.cc (cp_parser_class_property_specifier_seq_opt): New + function. + (cp_parser_class_head): Use it instead of + cp_parser_property_specifier_seq_opt. Don't diagnose + VIRT_SPEC_OVERRIDE here. Formatting fix. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/117785 + * constexpr.cc: Implement C++26 P3068R5 - constexpr exceptions. + (class constexpr_global_ctx): Add caught_exceptions and + uncaught_exceptions members. + (constexpr_global_ctx::constexpr_global_ctx): Initialize + uncaught_exceptions. + (returns, breaks, continues, switches): Move earlier. + (throws): New function. + (exception_what_str, diagnose_std_terminate, + diagnose_uncaught_exception): New functions. + (enum cxa_builtin): New type. + (cxx_cxa_builtin_fn_p, cxx_eval_cxa_builtin_fn): New functions. + (cxx_eval_builtin_function_call): Add jump_target argument. Call + cxx_eval_cxa_builtin_fn for __builtin_eh_ptr_adjust_ref. Adjust + cxx_eval_constant_expression calls, if it results in jmp_target, + set *jump_target to it and return. + (cxx_bind_parameters_in_call): Add jump_target argument. Pass + it through to cxx_eval_constant_expression. If it sets *jump_target, + break. + (fold_operand): Adjust cxx_eval_constant_expression caller. + (cxx_eval_assert): Likewise. If it set jmp_target, return true. + (cxx_eval_internal_function): Add jump_target argument. Pass it + through to cxx_eval_constant_expression. Return early if *jump_target + after recursing on args. + (cxx_eval_dynamic_cast_fn): Likewise. Don't set reference_p for + C++26 with -fexceptions. + (cxx_eval_thunk_call): Add jump_target argument. Pass it through + to cxx_eval_constant_expression. + (cxx_set_object_constness): Likewise. Don't set TREE_READONLY if + throws (jump_target). + (cxx_eval_call_expression): Add jump_target argument. Pass it + through to cxx_eval_internal_function, cxx_eval_builtin_function_call, + cxx_eval_thunk_call, cxx_eval_dynamic_cast_fn and + cxx_set_object_constness. Pass it through also + cxx_eval_constant_expression on arguments, cxx_bind_parameters_in_call + and cxx_fold_indirect_ref and for those cases return early + if *jump_target. Call cxx_eval_cxa_builtin_fn for cxx_cxa_builtin_fn_p + functions. For cxx_eval_constant_expression on body, pass address of + cleared jmp_target automatic variable, if it throws propagate + to *jump_target and make it non-cacheable. For C++26 don't diagnose + calls to non-constexpr functions before cxx_bind_parameters_in_call + could report some argument throwing an exception. + (cxx_eval_unary_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and return early + if *jump_target after the call. + (cxx_fold_pointer_plus_expression): Likewise. + (cxx_eval_binary_expression): Likewise and similarly for + cxx_fold_pointer_plus_expression call. + (cxx_eval_conditional_expression): Pass jump_target to + cxx_eval_constant_expression on first operand and return early + if *jump_target after the call. + (cxx_eval_vector_conditional_expression): Add jump_target argument. + Pass it through to cxx_eval_constant_expression for all 3 arguments + and return early if *jump_target after any of those calls. + (get_array_or_vector_nelts): Add jump_target argument. Pass it + through to cxx_eval_constant_expression. + (eval_and_check_array_index): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + each of them if *jump_target. + (cxx_eval_array_reference): Likewise. + (cxx_eval_component_reference): Likewise. + (cxx_eval_bit_field_ref): Likewise. + (cxx_eval_bit_cast): Likewise. Assert CHECKING_P call doesn't + throw or return. + (cxx_eval_logical_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + each of them if *jump_target. + (cxx_eval_bare_aggregate): Likewise. + (cxx_eval_vec_init_1): Add jump_target argument. Pass it through + to cxx_eval_bare_aggregate and recursive call. Pass it through + to get_array_or_vector_nelts and cxx_eval_constant_expression + and return early after it if *jump_target. + (cxx_eval_vec_init): Add jump_target argument. Pass it through + to cxx_eval_constant_expression and cxx_eval_vec_init_1. + (cxx_union_active_member): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and return early after it + if *jump_target. + (cxx_fold_indirect_ref_1): Add jump_target argument. Pass it + through to cxx_union_active_member and recursive calls. + (cxx_eval_indirect_ref): Add jump_target argument. Pass it through + to cxx_fold_indirect_ref_1 calls and to recursive call, in which + case return early after it if *jump_target. + (cxx_fold_indirect_ref): Add jump_target argument. Pass it through + to cxx_fold_indirect_ref and cxx_eval_constant_expression calls and + return early after those if *jump_target. + (cxx_eval_trinary_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + those if *jump_target. + (cxx_eval_store_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and eval_and_check_array_index + calls and return early after those if *jump_target. + (cxx_eval_increment_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + those if *jump_target. + (label_matches): Handle VAR_DECL case. + (cxx_eval_statement_list): Remove local_target variable and + !jump_target handling. Handle throws (jump_target) like returns or + breaks. + (cxx_eval_loop_expr): Remove local_target variable and !jump_target + handling. Pass it through to cxx_eval_constant_expression. Handle + throws (jump_target) like returns. + (cxx_eval_switch_expr): Pass jump_target through to + cxx_eval_constant_expression on cond, return early after it + if *jump_target. + (build_new_constexpr_heap_type): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls, return early after + those if *jump_target. + (merge_jump_target): New function. + (cxx_eval_constant_expression): Make jump_target argument no longer + defaulted, don't test jump_target for NULL. Pass jump_target + through to recursive calls, cxx_eval_call_expression, + cxx_eval_store_expression, cxx_eval_indirect_ref, + cxx_eval_unary_expression, cxx_eval_binary_expression, + cxx_eval_logical_expression, cxx_eval_array_reference, + cxx_eval_component_reference, cxx_eval_bit_field_ref, + cxx_eval_vector_conditional_expression, cxx_eval_bare_aggregate, + cxx_eval_vec_init, cxx_eval_trinary_expression, cxx_fold_indirect_ref, + build_new_constexpr_heap_type, cxx_eval_increment_expression, + cxx_eval_bit_cast and return earlyu after some of those + if *jump_target as needed. + (cxx_eval_constant_expression) <case TARGET_EXPR>: For C++26 push + also CLEANUP_EH_ONLY cleanups, with NULL_TREE marker after them. + (cxx_eval_constant_expression) <case RETURN_EXPR>: Don't + override *jump_target if throws (jump_target). + (cxx_eval_constant_expression) <case TRY_CATCH_EXPR, case TRY_BLOCK, + case MUST_NOT_THROW_EXPR, case TRY_FINALLY_EXPR, case CLEANUP_STMT>: + Handle C++26 constant expressions. + (cxx_eval_constant_expression) <case CLEANUP_POINT_EXPR>: For C++26 + with throws (jump_target) evaluate the CLEANUP_EH_ONLY cleanups as + well, and if not throws (jump_target) skip those. Set *jump_target + if some of the cleanups threw. + (cxx_eval_constant_expression) <case THROW_EXPR>: Recurse on operand + for C++26. + (cxx_eval_outermost_constant_expr): Diagnose uncaught exceptions both + from main expression and cleanups, diagnose also + break/continue/returns from the main expression. Handle + CLEANUP_EH_ONLY cleanup markers. Don't diagnose mutable poison stuff + if non_constant_p. Use different diagnostics for non-deleted heap + allocations if they were allocated by __cxa_allocate_exception. + (callee_might_throw): New function. + (struct check_for_return_continue_data): Add could_throw field. + (check_for_return_continue): Handle AGGR_INIT_EXPR and CALL_EXPR and + set d->could_throw if they could throw. + (potential_constant_expression_1): For CALL_EXPR allow + cxx_dynamic_cast_fn_p calls. For C++26 set *jump_target to void_node + for calls that could throw. For C++26 if call to non-constexpr call + is seen, try to evaluate arguments first and if they could throw, + don't diagnose call to non-constexpr function nor return false. + Adjust check_for_return_continue_data initializers and + set *jump_target to void_node if data.could_throw_p. For C++26 + recurse on THROW_EXPR argument. Add comment explaining TRY_BLOCK + handling with C++26 exceptions. Handle throws like returns in some + cases. + * cp-tree.h (MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P, + MUST_NOT_THROW_CATCH_P, DECL_EXCEPTION_REFCOUNT): Define. + (DECL_LOCAL_DECL_P): Fix comment typo, VARIABLE_DECL -> VAR_DECL. + (enum cp_built_in_function): Add CP_BUILT_IN_EH_PTR_ADJUST_REF, + (handler_match_for_exception_type): Declare. + * call.cc (handler_match_for_exception_type): New function. + * except.cc (initialize_handler_parm): Set MUST_NOT_THROW_CATCH_P + on newly created MUST_NOT_THROW_EXPR. + (begin_eh_spec_block): Set MUST_NOT_THROW_NOEXCEPT_P. + (wrap_cleanups_r): Set MUST_NOT_THROW_THROW_P. + (build_throw): Add another TARGET_EXPR whose scope spans + until after the __cxa_throw call and copy pointer value from ptr + to it and use it in __cxa_throw argument. + * tree.cc (builtin_valid_in_constant_expr_p): Handle + CP_BUILT_IN_EH_PTR_ADJUST_REF. + * decl.cc (cxx_init_decl_processing): Initialize + __builtin_eh_ptr_adjust_ref FE builtin. + * pt.cc (tsubst_stmt) <case MUST_NOT_THROW_EXPR>: Copy the + MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P and + MUST_NOT_THROW_CATCH_P flags. + * cp-gimplify.cc (cp_gimplify_expr) <case CALL_EXPR>: Error on + non-folded CP_BUILT_IN_EH_PTR_ADJUST_REF calls. + 2025-07-09 Jason Merrill <jason@redhat.com> PR c++/121012 diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index c8eef24..8d7aec3 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3205,6 +3205,9 @@ diagnose_trait_expr (tree expr, tree args) else inform (loc, " %qT is not nothrow invocable by %qE", t1, t2); break; + case CPTK_IS_NOTHROW_RELOCATABLE: + inform (loc, " %qT is not nothrow relocatable", t1); + break; case CPTK_IS_OBJECT: inform (loc, " %qT is not an object type", t1); break; @@ -3224,6 +3227,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_IS_REFERENCE: inform (loc, " %qT is not a reference", t1); break; + case CPTK_IS_REPLACEABLE: + inform (loc, " %qT is not replaceable", t1); + break; case CPTK_IS_SAME: inform (loc, " %qT is not the same as %qT", t1, t2); break; @@ -3251,6 +3257,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: inform (loc, " %qT is not trivially destructible", t1); break; + case CPTK_IS_TRIVIALLY_RELOCATABLE: + inform (loc, " %qT is not trivially relocatable", t1); + break; case CPTK_IS_UNBOUNDED_ARRAY: inform (loc, " %qT is not an unbounded array", t1); break; diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def index e71b28c..9fedfd7 100644 --- a/gcc/cp/cp-trait.def +++ b/gcc/cp/cp-trait.def @@ -87,12 +87,14 @@ DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1) DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2) DEFTRAIT_EXPR (IS_NOTHROW_DESTRUCTIBLE, "__is_nothrow_destructible", 1) DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1) +DEFTRAIT_EXPR (IS_NOTHROW_RELOCATABLE, "__builtin_is_nothrow_relocatable", 1) DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1) DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2) DEFTRAIT_EXPR (IS_POD, "__is_pod", 1) DEFTRAIT_EXPR (IS_POINTER, "__is_pointer", 1) DEFTRAIT_EXPR (IS_POLYMORPHIC, "__is_polymorphic", 1) DEFTRAIT_EXPR (IS_REFERENCE, "__is_reference", 1) +DEFTRAIT_EXPR (IS_REPLACEABLE, "__builtin_is_replaceable", 1) DEFTRAIT_EXPR (IS_SAME, "__is_same", 2) DEFTRAIT_EXPR (IS_SCOPED_ENUM, "__is_scoped_enum", 1) DEFTRAIT_EXPR (IS_STD_LAYOUT, "__is_standard_layout", 1) @@ -101,6 +103,7 @@ DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2) DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1) DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1) DEFTRAIT_EXPR (IS_TRIVIALLY_DESTRUCTIBLE, "__is_trivially_destructible", 1) +DEFTRAIT_EXPR (IS_TRIVIALLY_RELOCATABLE, "__builtin_is_trivially_relocatable", 1) DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1) DEFTRAIT_EXPR (IS_UNION, "__is_union", 1) DEFTRAIT_EXPR (IS_VIRTUAL_BASE_OF, "__builtin_is_virtual_base_of", 2) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9081628..5708aa1 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2500,6 +2500,11 @@ struct GTY(()) lang_type { bool erroneous : 1; bool non_pod_aggregate : 1; bool non_aggregate_pod : 1; + bool trivially_relocatable : 1; + bool trivially_relocatable_computed : 1; + + bool replaceable : 1; + bool replaceable_computed : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -2508,7 +2513,7 @@ struct GTY(()) lang_type { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 2; + unsigned dummy : 30; tree primary_base; vec<tree_pair_s, va_gc> *vcall_indices; @@ -2855,6 +2860,29 @@ struct GTY(()) lang_type { above (c++/120012). This could also be a hash_set. */ #define CLASSTYPE_NON_AGGREGATE_POD(NODE) \ (LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate_pod) + +/* If CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED, true if this class is + trivially relocatable. + If !CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED, true if this class + is marked with trivially_relocatable_if_eligible conditional keyword. */ +#define CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->trivially_relocatable) + +/* True if whether this class is trivially relocatable or not + has been computed already. */ +#define CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->trivially_relocatable_computed) + +/* If CLASSTYPE_REPLACEABLE_COMPUTED, true if this class is replaceable. + If !CLASSTYPE_REPLACEABLE_COMPUTED, true if this class is marked with + replaceable_if_eligible conditional keyword. */ +#define CLASSTYPE_REPLACEABLE_BIT(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->replaceable) + +/* True if whether this class is replaceable or not has been computed + already. */ +#define CLASSTYPE_REPLACEABLE_COMPUTED(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->replaceable_computed) /* Additional macros for inheritance information. */ @@ -6508,7 +6536,9 @@ enum virt_specifier { VIRT_SPEC_UNSPECIFIED = 0x0, VIRT_SPEC_FINAL = 0x1, - VIRT_SPEC_OVERRIDE = 0x2 + VIRT_SPEC_OVERRIDE = 0x2, + VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE = 0x4, + VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE = 0x8 }; /* A type-qualifier, or bitmask therefore, using the VIRT_SPEC @@ -8282,6 +8312,8 @@ extern bool pod_type_p (const_tree); extern bool layout_pod_type_p (const_tree); extern bool std_layout_type_p (const_tree); extern bool trivial_type_p (const_tree); +extern bool trivially_relocatable_type_p (tree); +extern bool replaceable_type_p (tree); extern bool trivially_copyable_p (const_tree); extern bool type_has_unique_obj_representations (const_tree); extern bool scalarish_type_p (const_tree); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 21bec72c..0d9ed2e 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -28075,6 +28075,8 @@ cp_parser_class_specifier (cp_parser* parser) class-property-specifier: final + trivially_relocatable_if_eligible (C++26) + replaceable_if_eligible (C++26) Returns a bitmask representing the class-property-specifiers. */ @@ -28107,6 +28109,38 @@ cp_parser_class_property_specifier_seq_opt (cp_parser *parser) } else if (id_equal (token->u.value, "__final")) virt_specifier = VIRT_SPEC_FINAL; + else if (id_equal (token->u.value, "trivially_relocatable_if_eligible")) + { + if (cxx_dialect < cxx26) + { + /* Warn about the C++26 conditional keyword (but don't parse + it). */ + warning_at (token->location, OPT_Wc__26_compat, + "identifier %qE is a conditional keyword in C++26", + token->u.value); + break; + } + virt_specifier = VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE; + } + else if (id_equal (token->u.value, + "__trivially_relocatable_if_eligible")) + virt_specifier = VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE; + else if (id_equal (token->u.value, "replaceable_if_eligible")) + { + if (cxx_dialect < cxx26) + { + /* Warn about the C++26 conditional keyword (but don't parse + it). */ + warning_at (token->location, OPT_Wc__26_compat, + "identifier %qE is a conditional keyword in C++26", + token->u.value); + break; + } + virt_specifier = VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE; + } + else if (id_equal (token->u.value, + "__replaceable_if_eligible")) + virt_specifier = VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE; else break; @@ -28662,6 +28696,16 @@ cp_parser_class_head (cp_parser* parser, DECL_SOURCE_LOCATION (TYPE_NAME (type)) = type_start_token->location; if (type && (virt_specifiers & VIRT_SPEC_FINAL)) CLASSTYPE_FINAL (type) = 1; + if (type && (virt_specifiers & VIRT_SPEC_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE)) + { + gcc_assert (!CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (type)); + CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (type) = 1; + } + if (type && (virt_specifiers & VIRT_SPEC_REPLACEABLE_IF_ELIGIBLE)) + { + gcc_assert (!CLASSTYPE_REPLACEABLE_COMPUTED (type)); + CLASSTYPE_REPLACEABLE_BIT (type) = 1; + } out: parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p; return type; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 8f3822c..ca8d61d 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -12699,7 +12699,17 @@ instantiate_class_template (tree type) determine_visibility (TYPE_MAIN_DECL (type)); } if (CLASS_TYPE_P (type)) - CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern); + { + CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern); + CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (type) + = CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (pattern); + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (type) + = CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (pattern); + CLASSTYPE_REPLACEABLE_BIT (type) + = CLASSTYPE_REPLACEABLE_BIT (pattern); + CLASSTYPE_REPLACEABLE_COMPUTED (type) + = CLASSTYPE_REPLACEABLE_COMPUTED (pattern); + } pbinfo = TYPE_BINFO (pattern); diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index b57547c..77fac45 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -13361,6 +13361,18 @@ object_type_p (const_tree type) && !VOID_TYPE_P (type)); } +/* [defns.referenceable] True iff TYPE is a referenceable type. */ + +static bool +referenceable_type_p (const_tree type) +{ + return (TYPE_REF_P (type) + || object_type_p (type) + || (FUNC_OR_METHOD_TYPE_P (type) + && type_memfn_quals (type) == TYPE_UNQUALIFIED + && type_memfn_rqual (type) == REF_QUAL_NONE)); +} + /* Actually evaluates the trait. */ static bool @@ -13530,6 +13542,21 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_NOTHROW_INVOCABLE: return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none); + case CPTK_IS_NOTHROW_RELOCATABLE: + if (trivially_relocatable_type_p (type1)) + return true; + else + { + type1 = strip_array_types (type1); + if (!referenceable_type_p (type1)) + return false; + tree arg = make_tree_vec (1); + TREE_VEC_ELT (arg, 0) + = cp_build_reference_type (type1, /*rval=*/true); + return (is_nothrow_xible (INIT_EXPR, type1, arg) + && is_nothrow_xible (BIT_NOT_EXPR, type1, NULL_TREE)); + } + case CPTK_IS_OBJECT: return object_type_p (type1); @@ -13548,6 +13575,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_REFERENCE: return type_code1 == REFERENCE_TYPE; + case CPTK_IS_REPLACEABLE: + return replaceable_type_p (type1); + case CPTK_IS_SAME: return same_type_p (type1, type2); @@ -13572,6 +13602,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: return is_trivially_xible (BIT_NOT_EXPR, type1, NULL_TREE); + case CPTK_IS_TRIVIALLY_RELOCATABLE: + return trivially_relocatable_type_p (type1); + case CPTK_IS_UNBOUNDED_ARRAY: return array_of_unknown_bound_p (type1); @@ -13702,18 +13735,6 @@ same_type_ref_bind_p (cp_trait_kind kind, tree type1, tree type2) (non_reference (to), non_reference (from)))); } -/* [defns.referenceable] True iff TYPE is a referenceable type. */ - -static bool -referenceable_type_p (const_tree type) -{ - return (TYPE_REF_P (type) - || object_type_p (type) - || (FUNC_OR_METHOD_TYPE_P (type) - && (type_memfn_quals (type) == TYPE_UNQUALIFIED - && type_memfn_rqual (type) == REF_QUAL_NONE))); -} - /* Process a trait expression. */ tree @@ -13762,8 +13783,11 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_LITERAL_TYPE: case CPTK_IS_POD: case CPTK_IS_STD_LAYOUT: + case CPTK_IS_REPLACEABLE: + case CPTK_IS_NOTHROW_RELOCATABLE: case CPTK_IS_TRIVIAL: case CPTK_IS_TRIVIALLY_COPYABLE: + case CPTK_IS_TRIVIALLY_RELOCATABLE: case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS: if (!check_trait_type (type1, /* kind = */ 2)) return error_mark_node; diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index accce0e..a7b8908 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -4716,6 +4716,293 @@ trivial_type_p (const_tree t) return scalarish_type_p (t); } +/* Returns 1 iff type T is a default-movable type, as defined in + [class.prop]. */ + +static bool +default_movable_type_p (tree t) +{ + if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t)) + return false; + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (user_provided_p (dtor) || DECL_DELETED_FN (dtor)) + return false; + + tree copy_ctor = NULL_TREE, move_ctor = NULL_TREE; + tree copy_assign = NULL_TREE, move_assign = NULL_TREE; + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + move_ctor = lazily_declare_fn (sfk_move_constructor, t); + if (CLASSTYPE_LAZY_MOVE_ASSIGN (t)) + move_assign = lazily_declare_fn (sfk_move_assignment, t); + if (!move_ctor) + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_ctor = *iter; + else if (move_fn_p (*iter)) + { + move_ctor = *iter; + break; + } + } + if (!move_assign) + for (ovl_iterator iter (get_class_binding_direct (t, + assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_assign = *iter; + else if (move_fn_p (*iter)) + { + move_assign = *iter; + break; + } + } + if (!move_ctor) + { + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + copy_ctor = lazily_declare_fn (sfk_copy_constructor, t); + if (!copy_ctor) + return false; + if (user_provided_p (copy_ctor) + || DECL_DELETED_FN (copy_ctor) + || DECL_CONTEXT (copy_ctor) != t + || DECL_INHERITED_CTOR (copy_ctor)) + return false; + } + else if (user_provided_p (move_ctor) + || DECL_DELETED_FN (move_ctor) + || DECL_CONTEXT (move_ctor) != t + || DECL_INHERITED_CTOR (move_ctor)) + return false; + if (!move_assign) + { + if (CLASSTYPE_LAZY_COPY_ASSIGN (t)) + copy_assign = lazily_declare_fn (sfk_copy_assignment, t); + if (!copy_assign) + return false; + if (user_provided_p (copy_assign) + || DECL_DELETED_FN (copy_assign) + || DECL_CONTEXT (copy_assign) != t) + return false; + } + else if (user_provided_p (move_assign) + || DECL_DELETED_FN (move_assign) + || DECL_CONTEXT (move_assign) != t) + return false; + return true; +} + +/* Returns 1 iff type T is a union with no user declared special member + functions. */ + +static bool +union_with_no_declared_special_member_fns (tree t) +{ + if (TREE_CODE (t) != UNION_TYPE) + return false; + + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL + && !DECL_ARTIFICIAL (*iter) + && (default_ctor_p (*iter) || copy_fn_p (*iter) || move_fn_p (*iter))) + return false; + + for (ovl_iterator iter (get_class_binding_direct (t, assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL + && !DECL_ARTIFICIAL (*iter) + && (copy_fn_p (*iter) || move_fn_p (*iter))) + return false; + + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (!DECL_ARTIFICIAL (dtor)) + return false; + + return true; +} + +/* Returns 1 iff type T is a trivially relocatable type, as defined in + [basic.types.general] and [class.prop]. */ + +bool +trivially_relocatable_type_p (tree t) +{ + t = strip_array_types (t); + + if (!CLASS_TYPE_P (t)) + return scalarish_type_p (t); + + t = TYPE_MAIN_VARIANT (t); + if (CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t)) + return CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t); + if (!COMPLETE_TYPE_P (t)) + return false; + + if (!CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t) + && !union_with_no_declared_special_member_fns (t) + && !default_movable_type_p (t)) + { + nontriv: + CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t) = 0; + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t) = 1; + return false; + } + + if (CLASSTYPE_VBASECLASSES (t)) + goto nontriv; + + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (DECL_DELETED_FN (dtor)) + goto nontriv; + + tree binfo, base_binfo; + unsigned int i; + for (binfo = TYPE_BINFO (t), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree basetype = TREE_TYPE (base_binfo); + if (!trivially_relocatable_type_p (basetype)) + goto nontriv; + } + + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && !DECL_ARTIFICIAL (field) + && !DECL_UNNAMED_BIT_FIELD (field)) + { + tree type = TREE_TYPE (field); + if (type == error_mark_node) + goto nontriv; + if (!TYPE_REF_P (type) && !trivially_relocatable_type_p (type)) + goto nontriv; + } + + CLASSTYPE_TRIVIALLY_RELOCATABLE_BIT (t) = 1; + CLASSTYPE_TRIVIALLY_RELOCATABLE_COMPUTED (t) = 1; + return true; +} + +/* Returns 1 iff type T is a replaceable type, as defined in [basic.types] + and [class]. */ + +bool +replaceable_type_p (tree t) +{ + t = strip_array_types (t); + + if (cv_qualified_p (t)) + return false; + + if (!CLASS_TYPE_P (t)) + return scalarish_type_p (t); + + t = TYPE_MAIN_VARIANT (t); + if (CLASSTYPE_REPLACEABLE_COMPUTED (t)) + return CLASSTYPE_REPLACEABLE_BIT (t); + if (!COMPLETE_TYPE_P (t)) + return false; + + if (!CLASSTYPE_REPLACEABLE_BIT (t) + && !union_with_no_declared_special_member_fns (t) + && !default_movable_type_p (t)) + { + nonrepl: + CLASSTYPE_REPLACEABLE_BIT (t) = 0; + CLASSTYPE_REPLACEABLE_COMPUTED (t) = 1; + return false; + } + + if (CLASSTYPE_LAZY_DESTRUCTOR (t)) + lazily_declare_fn (sfk_destructor, t); + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + if (DECL_DELETED_FN (dtor)) + goto nonrepl; + + tree copy_ctor = NULL_TREE, move_ctor = NULL_TREE; + tree copy_assign = NULL_TREE, move_assign = NULL_TREE; + if (CLASSTYPE_LAZY_MOVE_CTOR (t)) + move_ctor = lazily_declare_fn (sfk_move_constructor, t); + if (CLASSTYPE_LAZY_MOVE_ASSIGN (t)) + move_assign = lazily_declare_fn (sfk_move_assignment, t); + if (!move_ctor) + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_ctor = *iter; + else if (move_fn_p (*iter)) + { + move_ctor = *iter; + break; + } + } + if (!move_assign) + for (ovl_iterator iter (get_class_binding_direct (t, + assign_op_identifier)); + iter; ++iter) + if (TREE_CODE (*iter) == FUNCTION_DECL) + { + if (copy_fn_p (*iter)) + copy_assign = *iter; + else if (move_fn_p (*iter)) + { + move_assign = *iter; + break; + } + } + if (!move_ctor) + { + if (CLASSTYPE_LAZY_COPY_CTOR (t)) + copy_ctor = lazily_declare_fn (sfk_copy_constructor, t); + if (!copy_ctor || DECL_DELETED_FN (copy_ctor)) + goto nonrepl; + } + else if (DECL_DELETED_FN (move_ctor)) + goto nonrepl; + if (!move_assign) + { + if (CLASSTYPE_LAZY_COPY_ASSIGN (t)) + copy_assign = lazily_declare_fn (sfk_copy_assignment, t); + if (!copy_assign || DECL_DELETED_FN (copy_assign)) + goto nonrepl; + } + else if (DECL_DELETED_FN (move_assign)) + goto nonrepl; + + tree binfo, base_binfo; + unsigned int i; + for (binfo = TYPE_BINFO (t), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) + { + tree basetype = TREE_TYPE (base_binfo); + if (!replaceable_type_p (basetype)) + goto nonrepl; + } + + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && !DECL_ARTIFICIAL (field) + && !DECL_UNNAMED_BIT_FIELD (field)) + { + tree type = TREE_TYPE (field); + if (type == error_mark_node) + goto nonrepl; + if (!replaceable_type_p (type)) + goto nonrepl; + } + + CLASSTYPE_REPLACEABLE_BIT (t) = 1; + CLASSTYPE_REPLACEABLE_COMPUTED (t) = 1; + return true; +} + /* Returns 1 iff type T is a POD type, as defined in [basic.types]. */ bool diff --git a/gcc/diagnostic-digraphs.cc b/gcc/diagnostic-digraphs.cc new file mode 100644 index 0000000..85c0c51 --- /dev/null +++ b/gcc/diagnostic-digraphs.cc @@ -0,0 +1,484 @@ +/* Directed graphs associated with a diagnostic. + 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 "graphviz.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-format-sarif.h" + +#include "selftest.h" + +using digraph = diagnostics::digraphs::digraph; +using digraph_node = diagnostics::digraphs::node; +using digraph_edge = diagnostics::digraphs::edge; + +namespace { + +class conversion_to_dot +{ +public: + std::unique_ptr<dot::graph> + make_dot_graph_from_diagnostic_graph (const digraph &); + + std::unique_ptr<dot::stmt> + make_dot_node_from_digraph_node (const digraph_node &); + + std::unique_ptr<dot::edge_stmt> + make_dot_edge_from_digraph_edge (const digraph_edge &); + + dot::id + get_dot_id_for_node (const digraph_node &); + + bool + has_edges_p (const digraph_node &); + +private: + std::set<const digraph_node *> m_nodes_with_edges; + std::map<const digraph_node *, dot::stmt *> m_node_map; +}; + +} // anonymous namespace + +// class conversion_to_dot + +std::unique_ptr<dot::graph> +conversion_to_dot:: +make_dot_graph_from_diagnostic_graph (const diagnostics::digraphs::digraph &input_graph) +{ + auto output_graph = std::make_unique<dot::graph> (); + + if (const char *description = input_graph.get_description ()) + output_graph->m_stmt_list.add_attr (dot::id ("label"), + dot::id (description)); + + const int num_nodes = input_graph.get_num_nodes (); + const int num_edges = input_graph.get_num_edges (); + + /* Determine which nodes have in-edges and out-edges. */ + for (int i = 0; i < num_edges; ++i) + { + const digraph_edge &input_edge = input_graph.get_edge (i); + m_nodes_with_edges.insert (&input_edge.get_src_node ()); + m_nodes_with_edges.insert (&input_edge.get_dst_node ()); + } + + for (int i = 0; i < num_nodes; ++i) + { + const digraph_node &input_node = input_graph.get_node (i); + auto dot_node_stmt = make_dot_node_from_digraph_node (input_node); + output_graph->m_stmt_list.add_stmt (std::move (dot_node_stmt)); + } + + for (int i = 0; i < num_edges; ++i) + { + const digraph_edge &input_edge = input_graph.get_edge (i); + auto dot_edge_stmt = make_dot_edge_from_digraph_edge (input_edge); + output_graph->m_stmt_list.add_stmt (std::move (dot_edge_stmt)); + } + + return output_graph; +} + +std::unique_ptr<dot::stmt> +conversion_to_dot:: +make_dot_node_from_digraph_node (const diagnostics::digraphs::node &input_node) +{ + dot::id dot_id (get_dot_id_for_node (input_node)); + + /* For now, we can only do either edges or children, not both + ...but see https://graphviz.org/docs/attrs/compound/ */ + + if (has_edges_p (input_node)) + { + auto output_node + = std::make_unique<dot::node_stmt> (std::move (dot_id)); + m_node_map[&input_node] = output_node.get (); + if (const char *label = input_node.get_label ()) + output_node->set_label (dot::id (label)); + return output_node; + } + else + { + auto output_node = std::make_unique<dot::subgraph> (std::move (dot_id)); + m_node_map[&input_node] = output_node.get (); + if (const char *label = input_node.get_label ()) + output_node->add_attr (dot::id ("label"), dot::id (label)); + const int num_children = input_node.get_num_children (); + for (int i = 0; i < num_children; ++i) + { + const digraph_node &input_child = input_node.get_child (i); + auto dot_child_stmt = make_dot_node_from_digraph_node (input_child); + output_node->m_stmt_list.add_stmt (std::move (dot_child_stmt)); + } + return output_node; + } +} + +std::unique_ptr<dot::edge_stmt> +conversion_to_dot:: +make_dot_edge_from_digraph_edge (const digraph_edge &input_edge) +{ + const digraph_node &src_dnode = input_edge.get_src_node (); + const digraph_node &dst_dnode = input_edge.get_dst_node (); + auto output_edge + = std::make_unique<dot::edge_stmt> + (get_dot_id_for_node (src_dnode), + get_dot_id_for_node (dst_dnode)); + if (const char *label = input_edge.get_label ()) + output_edge->set_label (dot::id (label)); + return output_edge; +} + +dot::id +conversion_to_dot::get_dot_id_for_node (const digraph_node &input_node) +{ + if (has_edges_p (input_node)) + return input_node.get_id (); + else + return std::string ("cluster_") + input_node.get_id (); +} + +bool +conversion_to_dot::has_edges_p (const digraph_node &input_node) +{ + return m_nodes_with_edges.find (&input_node) != m_nodes_with_edges.end (); +} + +// class object + +const char * +diagnostics::digraphs::object:: +get_attr (const char *key_prefix, const char *key) 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::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) +{ + set_json_attr (key_prefix, key, std::make_unique<json::string> (value)); +} + +void +diagnostics::digraphs::object:: +set_json_attr (const char *key_prefix, const char *key, std::unique_ptr<json::value> value) +{ + 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)); +} + +// class digraph + +DEBUG_FUNCTION void +diagnostics::digraphs::digraph::dump () const +{ + make_json_sarif_graph ()->dump (); +} + +std::unique_ptr<json::object> +diagnostics::digraphs::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 +{ + conversion_to_dot to_dot; + return to_dot.make_dot_graph_from_diagnostic_graph (*this); +} + +std::unique_ptr<diagnostics::digraphs::digraph> +diagnostics::digraphs::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; + + for (auto &iter : m_nodes) + result->add_node (iter->clone (*result, node_mapping)); + for (auto &iter : m_edges) + result->add_edge (iter->clone (*result, node_mapping)); + + return result; +} + +void +diagnostics::digraphs::digraph::add_edge (const char *id, + node &src_node, + node &dst_node, + const char *label) +{ + auto e = std::make_unique<digraph_edge> (*this, + id, + src_node, + dst_node); + if (label) + e->set_label (label); + add_edge (std::move (e)); +} + +/* Utility function for edge ids: either use EDGE_ID, or + generate a unique one for when we don't care about the name. + + Edges in SARIF "SHALL" have an id that's unique within the graph + (SARIF 2.1.0 §3.41.2). This is so that graph traversals can refer + 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) +{ + /* If we have an id, use it. */ + if (edge_id) + return edge_id; + + /* Otherwise, generate a unique one of the form "edgeN". */ + while (true) + { + auto candidate (std::string ("edge") + + std::to_string (m_next_edge_id_index++)); + auto iter = m_id_to_edge_map.find (candidate); + if (iter != m_id_to_edge_map.end ()) + { + // Try again with the next index... + continue; + } + return candidate; + } +} + +// class node + +DEBUG_FUNCTION void +diagnostics::digraphs::node::dump () const +{ + to_json_sarif_node ()->dump (); +} + +std::unique_ptr<json::object> +diagnostics::digraphs::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 +{ + auto result + = std::make_unique<diagnostics::digraphs::node> (new_graph, + get_id ()); + node_mapping.insert ({const_cast <node *> (this), result.get ()}); + + result->set_logical_loc (m_logical_loc); + + if (get_property_bag ()) + result->set_property_bag (get_property_bag ()->clone_as_object ()); + + for (auto &iter : m_children) + result->add_child (iter->clone (new_graph, node_mapping)); + + return result; +} + +// class edge + +std::unique_ptr<digraph_edge> +digraph_edge::clone (digraph &new_graph, + const std::map<node *, node *> &node_mapping) const +{ + auto iter_new_src = node_mapping.find (&m_src_node); + gcc_assert (iter_new_src != node_mapping.end ()); + auto iter_new_dst = node_mapping.find (&m_dst_node); + gcc_assert (iter_new_dst != node_mapping.end ()); + auto result + = std::make_unique<digraph_edge> (new_graph, + m_id.c_str (), + *iter_new_src->second, + *iter_new_dst->second); + if (get_property_bag ()) + result->set_property_bag (get_property_bag ()->clone_as_object ()); + + return result; +} + +DEBUG_FUNCTION void +diagnostics::digraphs::edge::dump () const +{ + to_json_sarif_edge ()->dump (); +} + +std::unique_ptr<json::object> +diagnostics::digraphs::edge::to_json_sarif_edge () const +{ + return make_sarif_edge (*this, nullptr); +} + +// class lazy_digraph + +const diagnostics::digraphs::digraph & +diagnostics::digraphs::lazy_digraph::get_or_create_digraph () const +{ + if (!m_digraph) + m_digraph = create_digraph (); + gcc_assert (m_digraph); + return *m_digraph; +} + +// class lazy_digraphs + +const std::vector<std::unique_ptr<diagnostics::digraphs::digraph>> & +diagnostics::digraphs::lazy_digraphs::get_or_create_digraphs () const +{ + if (!m_digraphs) + m_digraphs = create_digraphs (); + gcc_assert (m_digraphs); + return *m_digraphs; +} + +#if CHECKING_P + +namespace selftest { + +static void +test_empty_graph () +{ + digraph g; + + { + auto sarif = g.make_json_sarif_graph (); + + pretty_printer pp; + sarif->print (&pp, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("{\"nodes\": [],\n" + " \"edges\": []}")); + } + + { + auto dg = g.make_dot_graph (); + + pretty_printer pp; + dot::writer w (pp); + dg->print (w); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("digraph {\n" + "}\n")); + } +} + +static void +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"); + + 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"); + 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_label ("I am an edge label"); + g->add_edge (std::move (e)); + + g->add_node (std::move (a)); + + b->add_child (std::move (c)); + g->add_node (std::move (b)); +#undef KEY_PREFIX + + { + auto sarif = g->make_json_sarif_graph (); + + pretty_printer pp; + sarif->print (&pp, true); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("{\"properties\": {\"/placeholder/date\": \"1066\"},\n" + " \"nodes\": [{\"id\": \"a\"},\n" + " {\"id\": \"b\",\n" + " \"properties\": {\"/placeholder/color\": \"red\"},\n" + " \"children\": [{\"id\": \"c\"}]}],\n" + " \"edges\": [{\"id\": \"edge0\",\n" + " \"properties\": {\"/placeholder/status\": \"copacetic\"},\n" + " \"sourceNodeId\": \"a\",\n" + " \"targetNodeId\": \"c\"}]}")); + } + + { + auto dg = g->make_dot_graph (); + + pretty_printer pp; + dot::writer w (pp); + dg->print (w); + ASSERT_STREQ + (pp_formatted_text (&pp), + ("digraph {\n" + " label=\"test graph\";\n" + " a;\n" + " \n" + " subgraph cluster_b {\n" + " c [label=\"I am a node label\"];\n" + "\n" + " };\n" + " a -> c [label=\"I am an edge label\"];\n" + "}\n")); + } +} + +/* Run all of the selftests within this file. */ + +void +diagnostic_digraphs_cc_tests () +{ + test_empty_graph (); + test_simple_graph (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/diagnostic-digraphs.h b/gcc/diagnostic-digraphs.h new file mode 100644 index 0000000..94cb76e --- /dev/null +++ b/gcc/diagnostic-digraphs.h @@ -0,0 +1,411 @@ +/* Directed graphs associated with a diagnostic. + 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_DIAGNOSTIC_DIGRAPHS_H +#define GCC_DIAGNOSTIC_DIGRAPHS_H + +#include "json.h" +#include "logical-location.h" + +class graphviz_out; + +class sarif_graph; +class sarif_node; +class sarif_edge; + +namespace dot { class graph; } + +namespace diagnostics { +namespace digraphs { + +/* A family of classes: digraph, node, and edge, closely related to + SARIF's graph, node, and edge types (SARIF v2.1.0 sections 3.39-3.41). + + Nodes can have child nodes, allowing for arbitrarily deep nesting. + Edges can be between any pair of nodes (potentially at different + nesting levels). + + Digraphs, nodes, and edges also optionally have a JSON property bag, + allowing round-tripping of arbitrary key/value pairs through SARIF. */ + +class digraph; +class node; +class edge; + +/* A base class for digraph, node, and edge to allow them to have + an optional JSON property bag. */ + +class object +{ +public: + const char * + get_attr (const char *key_prefix, + const char *key) const; + + void + set_attr (const char *key_prefix, + const char *key, + const char *value); + + void + set_json_attr (const char *key_prefix, + const char *key, + std::unique_ptr<json::value> value); + + json::object * + get_property_bag () const { return m_property_bag.get (); } + + void + set_property_bag (std::unique_ptr<json::object> property_bag) + { + m_property_bag = std::move (property_bag); + } + +private: + std::unique_ptr<json::object> m_property_bag; +}; + +// A directed graph, corresponding to SARIF v2.1.0 section 3.39. + +class digraph : public object +{ + public: + friend class node; + friend class edge; + + digraph () : m_next_edge_id_index (0) {} + virtual ~digraph () {} + + const char * + get_description () const + { + if (!m_description) + return nullptr; + return m_description->c_str (); + } + + void + set_description (const char *desc) + { + if (desc) + m_description = std::make_unique<std::string> (desc); + else + m_description = nullptr; + } + + node * + get_node_by_id (const char *id) const + { + auto iter = m_id_to_node_map.find (id); + if (iter == m_id_to_node_map.end ()) + return nullptr; + return iter->second; + } + + edge * + get_edge_by_id (const char *id) const + { + auto iter = m_id_to_edge_map.find (id); + if (iter == m_id_to_edge_map.end ()) + return nullptr; + return iter->second; + } + + size_t + get_num_nodes () const + { + return m_nodes.size (); + } + + node & + get_node (size_t idx) const + { + return *m_nodes[idx].get (); + } + + size_t + get_num_edges () const + { + return m_edges.size (); + } + + edge & + get_edge (size_t idx) const + { + return *m_edges[idx].get (); + } + + void + dump () const; + + std::unique_ptr<json::object> + make_json_sarif_graph () const; + + std::unique_ptr<dot::graph> + make_dot_graph () const; + + void + add_node (std::unique_ptr<node> n) + { + gcc_assert (n); + m_nodes.push_back (std::move (n)); + } + + void + add_edge (std::unique_ptr<edge> e) + { + gcc_assert (e); + m_edges.push_back (std::move (e)); + } + + void + add_edge (const char *id, + node &src_node, + node &dst_node, + const char *label = nullptr); + + std::unique_ptr<digraph> clone () const; + + private: + void + add_node_id (std::string node_id, node &new_node) + { + m_id_to_node_map.insert ({std::move (node_id), &new_node}); + } + void + add_edge_id (std::string edge_id, edge &new_edge) + { + m_id_to_edge_map.insert ({std::move (edge_id), &new_edge}); + } + + std::string + make_edge_id (const char *edge_id); + + std::unique_ptr<std::string> m_description; + std::map<std::string, node *> m_id_to_node_map; + std::map<std::string, edge *> m_id_to_edge_map; + std::vector<std::unique_ptr<node>> m_nodes; + std::vector<std::unique_ptr<edge>> m_edges; + size_t m_next_edge_id_index; +}; + +// A node in a directed graph, corresponding to SARIF v2.1.0 section 3.40. + +class node : public object +{ + public: + virtual ~node () {} + + node (digraph &g, std::string id) + : m_id (id), + m_physical_loc (UNKNOWN_LOCATION) + { + g.add_node_id (std::move (id), *this); + } + node (const node &) = delete; + + std::string + get_id () const { return m_id; } + + const char * + get_label () const + { + if (!m_label) + return nullptr; + return m_label->c_str (); + } + + void + set_label (const char *label) + { + if (label) + m_label = std::make_unique<std::string> (label); + else + m_label = nullptr; + } + + size_t + get_num_children () const { return m_children.size (); } + + node & + get_child (size_t idx) const { return *m_children[idx].get (); } + + void + add_child (std::unique_ptr<node> child) + { + gcc_assert (child); + m_children.push_back (std::move (child)); + } + + location_t + get_physical_loc () const + { + return m_physical_loc; + } + + void + set_physical_loc (location_t physical_loc) + { + m_physical_loc = physical_loc; + } + + logical_location + get_logical_loc () const + { + return m_logical_loc; + } + + void + set_logical_loc (logical_location logical_loc) + { + m_logical_loc = logical_loc; + } + + void print (graphviz_out &gv) const; + + void + dump () const; + + std::unique_ptr<json::object> + to_json_sarif_node () const; + + std::unique_ptr<node> + clone (digraph &new_graph, + std::map<node *, node *> &node_mapping) const; + + private: + std::string m_id; + std::unique_ptr<std::string> m_label; + std::vector<std::unique_ptr<node>> m_children; + location_t m_physical_loc; + logical_location m_logical_loc; +}; + +// An edge in a directed graph, corresponding to SARIF v2.1.0 section 3.41. + +class edge : public object +{ + public: + virtual ~edge () {} + + /* SARIF requires us to provide unique edge IDs within a graph, + but otherwise we don't need them. + Pass in nullptr for the id to get the graph to generate a unique + edge id for us. */ + edge (digraph &g, + const char *id, + node &src_node, + node &dst_node) + : m_id (g.make_edge_id (id)), + m_src_node (src_node), + m_dst_node (dst_node) + { + g.add_edge_id (m_id, *this); + } + + std::string + get_id () const { return m_id; } + + const char * + get_label () const + { + if (!m_label) + return nullptr; + return m_label->c_str (); + } + + void + set_label (const char *label) + { + if (label) + m_label = std::make_unique<std::string> (label); + else + m_label = nullptr; + } + + node & + get_src_node () const { return m_src_node; } + + node & + get_dst_node () const { return m_dst_node; } + + void + dump () const; + + std::unique_ptr<json::object> + to_json_sarif_edge () const; + + std::unique_ptr<edge> + clone (digraph &new_graph, + const std::map<diagnostics::digraphs::node *, diagnostics::digraphs::node *> &node_mapping) const; + +private: + std::string m_id; + std::unique_ptr<std::string> m_label; + node &m_src_node; + node &m_dst_node; +}; + +/* Abstract base class for lazily creating + a digraph on demand. + + This allows us to avoid the work of creating the digraph for + the common case where we just have a text sink. */ + +class lazy_digraph +{ +public: + virtual ~lazy_digraph () {} + + const digraph & + get_or_create_digraph () const; + +private: + virtual std::unique_ptr<digraph> + create_digraph () const = 0; + + mutable std::unique_ptr<digraph> m_digraph; +}; + +/* Abstract base class for lazily creating a collection of + digraphs on demand. + + This allows us to avoid the work of creating the digraphs for + the common case where we just have a text sink. */ + +class lazy_digraphs +{ +public: + virtual ~lazy_digraphs () {} + + const std::vector<std::unique_ptr<digraph>> & + get_or_create_digraphs () const; + +private: + virtual std::unique_ptr<std::vector<std::unique_ptr<digraph>>> + create_digraphs () const = 0; + + mutable std::unique_ptr<std::vector<std::unique_ptr<digraph>>> m_digraphs; +}; + +} // namespace digraphs +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTIC_DIGRAPHS_H */ diff --git a/gcc/diagnostic-format-html.cc b/gcc/diagnostic-format-html.cc index 473880f..becac39 100644 --- a/gcc/diagnostic-format-html.cc +++ b/gcc/diagnostic-format-html.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-format.h" #include "diagnostic-format-html.h" #include "diagnostic-format-text.h" +#include "diagnostic-format-sarif.h" #include "diagnostic-output-file.h" #include "diagnostic-buffer.h" #include "diagnostic-path.h" @@ -41,7 +42,8 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "xml.h" #include "xml-printer.h" -#include "diagnostic-state.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "graphviz.h" #include "json.h" #include "selftest-xml.h" @@ -52,8 +54,8 @@ html_generation_options::html_generation_options () : m_css (true), m_javascript (true), m_show_state_diagrams (false), - m_show_state_diagram_xml (false), - m_show_state_diagram_dot_src (false) + m_show_state_diagrams_sarif (false), + m_show_state_diagrams_dot_src (false) { } @@ -119,6 +121,8 @@ public: diagnostic_t orig_diag_kind, diagnostic_html_format_buffer *buffer); void emit_diagram (const diagnostic_diagram &diagram); + void emit_global_graph (const diagnostics::digraphs::lazy_digraph &); + void end_group (); std::unique_ptr<xml::element> take_current_diagnostic () @@ -172,6 +176,10 @@ private: void pop_nesting_level (); + void + add_graph (const diagnostics::digraphs::digraph &dg, + xml::element &parent_element); + diagnostic_context &m_context; pretty_printer *m_printer; const line_maps *m_line_maps; @@ -181,6 +189,7 @@ private: std::unique_ptr<xml::document> m_document; xml::element *m_head_element; xml::element *m_title_element; + xml::element *m_body_element; xml::element *m_diagnostics_element; std::unique_ptr<xml::element> m_cur_diagnostic_element; std::vector<xml::element *> m_cur_nesting_levels; @@ -401,6 +410,7 @@ html_builder::html_builder (diagnostic_context &context, m_logical_loc_mgr (nullptr), m_head_element (nullptr), m_title_element (nullptr), + m_body_element (nullptr), m_diagnostics_element (nullptr), m_next_diag_id (0), m_last_location (UNKNOWN_LOCATION), @@ -447,6 +457,7 @@ html_builder::html_builder (diagnostic_context &context, { xml::auto_print_element body (xp, "body"); + m_body_element = xp.get_insertion_point (); { auto diagnostics_element = make_div ("gcc-diagnostic-list"); m_diagnostics_element = diagnostics_element.get (); @@ -599,39 +610,45 @@ html_builder::maybe_make_state_diagram (const diagnostic_event &event) if (!m_html_gen_opts.m_show_state_diagrams) return nullptr; - /* Get XML state document; if we're going to print it later, also request + if (!m_logical_loc_mgr) + return nullptr; + + /* Get state graph; if we're going to print it later, also request the debug version. */ - auto xml_state - = event.maybe_make_xml_state (m_html_gen_opts.m_show_state_diagram_xml); - if (!xml_state) + auto state_graph + = event.maybe_make_diagnostic_state_graph + (m_html_gen_opts.m_show_state_diagrams_sarif); + if (!state_graph) return nullptr; // Convert it to .dot AST - auto graph = make_dot_graph_from_xml_state (*xml_state); - gcc_assert (graph); + auto dot_graph + = diagnostics::state_graphs::make_dot_graph (*state_graph, + *m_logical_loc_mgr); + gcc_assert (dot_graph); auto wrapper = std::make_unique<xml::element> ("div", false); xml::printer xp (*wrapper); - if (m_html_gen_opts.m_show_state_diagram_xml) + if (m_html_gen_opts.m_show_state_diagrams_sarif) { - // For debugging, show the XML src inline: + // For debugging, show the SARIF src inline: pretty_printer pp; - xml_state->write_as_xml (&pp, 0, true); + state_graph->make_json_sarif_graph ()->print (&pp, true); print_pre_source (xp, pp_formatted_text (&pp)); } - if (m_html_gen_opts.m_show_state_diagram_dot_src) + if (m_html_gen_opts.m_show_state_diagrams_dot_src) { // For debugging, show the dot src inline: pretty_printer pp; dot::writer w (pp); - graph->print (w); + dot_graph->print (w); print_pre_source (xp, pp_formatted_text (&pp)); } // Turn the .dot into SVG and splice into place - auto svg = dot::make_svg_from_graph (*graph); + auto svg = dot::make_svg_from_graph (*dot_graph); if (svg) xp.append (std::move (svg)); @@ -1125,6 +1142,15 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic, gcc_assert (xp.get_num_open_tags () == depth_within_alert_div); + // Try to display any per-diagnostic graphs + if (diagnostic.metadata) + if (auto ldg = diagnostic.metadata->get_lazy_digraphs ()) + { + auto &digraphs = ldg->get_or_create_digraphs (); + for (auto &dg : digraphs) + add_graph (*dg, *xp.get_insertion_point ()); + } + if (auto patch_element = make_element_for_patch (diagnostic)) { xp.push_tag ("div"); @@ -1221,6 +1247,35 @@ html_builder::emit_diagram (const diagnostic_diagram &/*diagram*/) // TODO: currently a no-op } +void +html_builder::add_graph (const diagnostics::digraphs::digraph &dg, + xml::element &parent_element) +{ + 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)); + } +} + +void +html_builder::emit_global_graph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + auto &dg = ldg.get_or_create_digraph (); + gcc_assert (m_body_element); + add_graph (dg, *m_body_element); +} + /* Implementation of "end_group_cb" for HTML output. */ void @@ -1334,6 +1389,12 @@ public: m_builder.set_printer (*get_printer ()); } + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) final override + { + m_builder.emit_global_graph (ldg); + } + const xml::document &get_document () const { return m_builder.get_document (); diff --git a/gcc/diagnostic-format-html.h b/gcc/diagnostic-format-html.h index 09a97e0..537ba54 100644 --- a/gcc/diagnostic-format-html.h +++ b/gcc/diagnostic-format-html.h @@ -36,11 +36,11 @@ struct html_generation_options // If true, attempt to show state diagrams at events bool m_show_state_diagrams; - // If true, show the XML form of the state with such diagrams - bool m_show_state_diagram_xml; + // If true, show the SARIF form of the state with such diagrams + bool m_show_state_diagrams_sarif; // If true, show the .dot source used for the diagram - bool m_show_state_diagram_dot_src; + bool m_show_state_diagrams_dot_src; }; extern diagnostic_output_file diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index 14cdbc2..d20ca86 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -28,6 +28,8 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "diagnostic.h" #include "diagnostic-metadata.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "diagnostic-path.h" #include "diagnostic-format.h" #include "diagnostic-buffer.h" @@ -776,6 +778,9 @@ public: void emit_diagram (const diagnostic_diagram &diagram); void end_group (); + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &); + std::unique_ptr<sarif_result> take_current_result () { return std::move (m_cur_group_result); @@ -789,7 +794,7 @@ public: const diagnostic_info &diagnostic, enum diagnostic_artifact_role role); std::unique_ptr<sarif_location> - make_location_object (sarif_location_manager &loc_mgr, + make_location_object (sarif_location_manager *loc_mgr, const rich_location &rich_loc, logical_location logical_loc, enum diagnostic_artifact_role role); @@ -974,6 +979,8 @@ private: std::unique_ptr<sarif_array_of_unique<sarif_logical_location>> m_cached_logical_locs; + std::unique_ptr<sarif_array_of_unique<sarif_graph>> m_run_graphs; + int m_tabstop; std::unique_ptr<sarif_serialization_format> m_serialization_format; @@ -1305,7 +1312,7 @@ sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic, sometimes these will related to current_function_decl, but often they won't. */ auto location_obj - = builder.make_location_object (*this, *diagnostic.richloc, + = builder.make_location_object (this, *diagnostic.richloc, logical_location (), diagnostic_artifact_role::result_file); auto message_obj @@ -1663,6 +1670,8 @@ sarif_builder::sarif_builder (diagnostic_context &context, m_rules_arr (new json::array ()), m_cached_logical_locs (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()), + m_run_graphs + (std::make_unique<sarif_array_of_unique<sarif_graph>> ()), m_tabstop (context.m_tabstop), m_serialization_format (std::move (serialization_format)), m_sarif_gen_opts (sarif_gen_opts), @@ -1895,6 +1904,17 @@ sarif_builder::end_group () } } +void +sarif_builder:: +report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + auto &dg = ldg.get_or_create_digraph (); + + /* Presumably the location manager must be nullptr; see + https://github.com/oasis-tcs/sarif-spec/issues/712 */ + m_run_graphs->append (make_sarif_graph (dg, this, nullptr)); +} + /* Create a top-level object, and add it to all the results (and other entities) we've seen so far, moving ownership to the object. */ @@ -2049,6 +2069,19 @@ sarif_builder::make_result_object (const diagnostic_info &diagnostic, result_obj->set<json::array> ("codeFlows", std::move (code_flows_arr)); } + // "graphs" property (SARIF v2.1.0 section 3.27.19). */ + if (diagnostic.metadata) + if (auto ldg = diagnostic.metadata->get_lazy_digraphs ()) + { + auto &digraphs = ldg->get_or_create_digraphs (); + auto graphs_arr = std::make_unique<json::array> (); + for (auto &iter : digraphs) + graphs_arr->append (make_sarif_graph (*iter, this, + result_obj.get ())); + if (graphs_arr->size () > 0) + result_obj->set<json::array> ("graphs", std::move (graphs_arr)); + } + /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is set up later, if any nested diagnostics occur within this diagnostic group. */ @@ -2176,7 +2209,7 @@ sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr, logical_loc = client_data_hooks->get_current_logical_location (); auto location_obj - = make_location_object (loc_mgr, *diagnostic.richloc, logical_loc, role); + = make_location_object (&loc_mgr, *diagnostic.richloc, logical_loc, role); /* Don't add entirely empty location objects to the array. */ if (!location_obj->is_empty ()) locations_arr->append<sarif_location> (std::move (location_obj)); @@ -2210,10 +2243,12 @@ set_any_logical_locs_arr (sarif_location &location_obj, /* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC and LOGICAL_LOC. Use LOC_MGR for any locations that need "id" values, and for - any worklist items. */ + any worklist items. + Note that we might not always have a LOC_MGR; see + https://github.com/oasis-tcs/sarif-spec/issues/712 */ std::unique_ptr<sarif_location> -sarif_builder::make_location_object (sarif_location_manager &loc_mgr, +sarif_builder::make_location_object (sarif_location_manager *loc_mgr, const rich_location &rich_loc, logical_location logical_loc, enum diagnostic_artifact_role role) @@ -2309,8 +2344,8 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr, /* Add related locations for any secondary locations in RICH_LOC that don't have labels (and thus aren't added to "annotations"). */ - if (i > 0 && !handled) - loc_mgr.add_relationship_to_worklist + if (loc_mgr && i > 0 && !handled) + loc_mgr->add_relationship_to_worklist (*location_obj.get (), sarif_location_manager::worklist_item::kind::unlabelled_secondary_location, range->m_loc); @@ -2321,7 +2356,8 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr, std::move (annotations_arr)); } - add_any_include_chain (loc_mgr, *location_obj.get (), loc); + if (loc_mgr) + add_any_include_chain (*loc_mgr, *location_obj.get (), loc); /* A flag for hinting that the diagnostic involves issues at the level of character encodings (such as homoglyphs, or misleading @@ -2799,6 +2835,142 @@ sarif_property_bag::set_logical_location (const char *property_name, builder.make_minimal_sarif_logical_location (logical_loc)); } +static void +copy_any_property_bag (const diagnostics::digraphs::object &input_obj, + sarif_object &output_obj) +{ + if (input_obj.get_property_bag ()) + { + const json::object &old_bag = *input_obj.get_property_bag (); + sarif_property_bag &new_bag = output_obj.get_or_create_properties (); + for (size_t i = 0; i < old_bag.get_num_keys (); ++i) + { + const char *key = old_bag.get_key (i); + json::value *val = old_bag.get (key); + new_bag.set (key, val->clone ()); + } + } +} + +std::unique_ptr<sarif_graph> +make_sarif_graph (const diagnostics::digraphs::digraph &g, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr) +{ + auto result = std::make_unique<sarif_graph> (); + + // 3.39.2 description property + if (const char *desc = g.get_description ()) + if (builder) + result->set<sarif_message> ("description", + builder->make_message_object (desc)); + + copy_any_property_bag (g, *result); + + // 3.39.3 nodes property + auto nodes_arr = std::make_unique<json::array> (); + const int num_nodes = g.get_num_nodes (); + for (int i = 0; i < num_nodes; ++i) + nodes_arr->append (make_sarif_node (g.get_node (i), + builder, + sarif_location_mgr)); + result->set ("nodes", std::move (nodes_arr)); + + // 3.39.4 edges property + auto edges_arr = std::make_unique<json::array> (); + const int num_edges = g.get_num_edges (); + for (int i = 0; i < num_edges; ++i) + edges_arr->append (make_sarif_edge (g.get_edge (i), builder)); + result->set ("edges", std::move (edges_arr)); + + return result; +} + +std::unique_ptr<sarif_node> +make_sarif_node (const diagnostics::digraphs::node &n, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr) +{ + auto result = std::make_unique<sarif_node> (); + + // 3.40.2 id property + result->set_string ("id", n.get_id ().c_str ()); + + copy_any_property_bag (n, *result); + + // 3.40.3 label property + if (const char *label = n.get_label ()) + if (builder) + result->set<sarif_message> ("label", + builder->make_message_object (label)); + + // 3.40.4 location property + if (n.get_logical_loc () + || n.get_physical_loc () != UNKNOWN_LOCATION) + if (builder) + { + rich_location rich_loc + (line_table, n.get_physical_loc ()); + auto loc_obj + = builder->make_location_object + (sarif_location_mgr, + rich_loc, + n.get_logical_loc (), + diagnostic_artifact_role::scanned_file); + result->set<sarif_location> ("location", + std::move (loc_obj)); + } + + // 3.40.5 children property + if (const int num_children = n.get_num_children ()) + { + auto children_arr = std::make_unique<json::array> (); + for (int i = 0; i < num_children; ++i) + children_arr->append (make_sarif_node (n.get_child (i), + builder, + sarif_location_mgr)); + result->set ("children", std::move (children_arr)); + } + + return result; +} + +std::unique_ptr<sarif_edge> +make_sarif_edge (const diagnostics::digraphs::edge &e, + sarif_builder *builder) +{ + auto result = std::make_unique<sarif_edge> (); + + // 3.41.2 id property + result->set_string ("id", e.get_id ().c_str ()); + + copy_any_property_bag (e, *result); + + // 3.41.3 label property + if (const char *label = e.get_label ()) + if (builder) + result->set<sarif_message> ("label", + builder->make_message_object (label)); + + // 3.41.4 sourceNodeId property + result->set_string ("sourceNodeId", e.get_src_node ().get_id ().c_str ()); + + // 3.41.5 targetNodeId property + result->set_string ("targetNodeId", e.get_dst_node ().get_id ().c_str ()); + + return result; +} + +void +sarif_property_bag::set_graph (const char *property_name, + sarif_builder &builder, + sarif_location_manager *sarif_location_mgr, + const diagnostics::digraphs::digraph &g) +{ + set<sarif_graph> (property_name, + make_sarif_graph (g, &builder, sarif_location_mgr)); +} + /* Ensure that m_cached_logical_locs has a "logicalLocation" object (SARIF v2.1.0 section 3.33) for K, and return its index within the array. */ @@ -2960,17 +3132,21 @@ populate_thread_flow_location_object (sarif_result &result, via a property bag. */ ev.maybe_add_sarif_properties (*this, tfl_obj); - if (get_opts ().m_xml_state) - if (auto xml_state = ev.maybe_make_xml_state (true)) + if (get_opts ().m_state_graph) + if (auto state_graph = ev.maybe_make_diagnostic_state_graph (true)) { sarif_property_bag &props = tfl_obj.get_or_create_properties (); - pretty_printer pp; - xml_state->write_as_xml (&pp, 0, true); - #define PROPERTY_PREFIX "gcc/diagnostic_event/" - props.set_string (PROPERTY_PREFIX "xml_state", - pp_formatted_text (&pp)); + props.set_graph (PROPERTY_PREFIX "state_graph", + *this, + /* Use RESULT for any related locations in the graph's + nodes. + It's not clear if this is correct; see: + https://github.com/oasis-tcs/sarif-spec/issues/712 + */ + &result, + *state_graph); #undef PROPERTY_PREFIX } @@ -3234,6 +3410,11 @@ make_run_object (std::unique_ptr<sarif_invocation> invocation_obj, std::move (m_cached_logical_locs)); } + // "graphs" property (SARIF v2.1.0 3.14.20) + if (m_run_graphs->size () > 0) + run_obj->set<json::array> ("graphs", + std::move (m_run_graphs)); + return run_obj; } @@ -3739,6 +3920,12 @@ public: /* No-op. */ } + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &lazy_digraph) final override + { + m_builder.report_global_digraph (lazy_digraph); + } + sarif_builder &get_builder () { return m_builder; } size_t num_results () const { return m_builder.num_results (); } @@ -4117,7 +4304,7 @@ make_sarif_sink (diagnostic_context &context, sarif_generation_options::sarif_generation_options () : m_version (sarif_version::v2_1_0), - m_xml_state (false) + m_state_graph (false) { } @@ -4299,7 +4486,7 @@ test_make_location_object (const sarif_generation_options &sarif_gen_opts, std::unique_ptr<sarif_location> location_obj = builder.make_location_object - (result, richloc, logical_location (), + (&result, richloc, logical_location (), diagnostic_artifact_role::analysis_target); ASSERT_NE (location_obj, nullptr); diff --git a/gcc/diagnostic-format-sarif.h b/gcc/diagnostic-format-sarif.h index c3ae330..60657c6 100644 --- a/gcc/diagnostic-format-sarif.h +++ b/gcc/diagnostic-format-sarif.h @@ -101,7 +101,7 @@ struct sarif_generation_options sarif_generation_options (); enum sarif_version m_version; - bool m_xml_state; + bool m_state_graph; }; extern std::unique_ptr<diagnostic_output_format> @@ -112,6 +112,14 @@ make_sarif_sink (diagnostic_context &context, diagnostic_output_file output_file); class sarif_builder; +class sarif_location_manager; + +namespace diagnostics { +namespace digraphs { + class digraph; + class node; + class edge; +}} /* Concrete subclass of json::object for SARIF property bags (SARIF v2.1.0 section 3.8). */ @@ -122,6 +130,10 @@ public: void set_logical_location (const char *property_name, sarif_builder &, logical_location logical_loc); + void set_graph (const char *property_name, + sarif_builder &, + sarif_location_manager *sarif_location_mgr, + const diagnostics::digraphs::digraph &g); }; /* Concrete subclass of json::object for SARIF objects that can @@ -136,4 +148,39 @@ public: sarif_property_bag &get_or_create_properties (); }; +/* Subclass of sarif_object for SARIF "graph" objects + (SARIF v2.1.0 section 3.39). */ + +class sarif_graph : public sarif_object +{ +}; + +/* Subclass of sarif_object for SARIF "node" objects + (SARIF v2.1.0 section 3.40). */ + +class sarif_node : public sarif_object +{ +}; + +/* Subclass of sarif_object for SARIF "edge" objects + (SARIF v2.1.0 section 3.41). */ + +class sarif_edge : public sarif_object +{ +}; + +extern std::unique_ptr<sarif_graph> +make_sarif_graph (const diagnostics::digraphs::digraph &g, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr); + +extern std::unique_ptr<sarif_node> +make_sarif_node (const diagnostics::digraphs::node &n, + sarif_builder *builder, + sarif_location_manager *sarif_location_mgr); + +extern std::unique_ptr<sarif_edge> +make_sarif_edge (const diagnostics::digraphs::edge &e, + sarif_builder *builder); + #endif /* ! GCC_DIAGNOSTIC_FORMAT_SARIF_H */ diff --git a/gcc/diagnostic-format-text.h b/gcc/diagnostic-format-text.h index 64f8e13..64a4452 100644 --- a/gcc/diagnostic-format-text.h +++ b/gcc/diagnostic-format-text.h @@ -70,6 +70,12 @@ public: void update_printer () override; + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &) final override + { + // no-op for text + } + /* Helpers for writing lang-specific starters/finalizers for text output. */ char *build_prefix (const diagnostic_info &) const; void report_current_module (location_t where); diff --git a/gcc/diagnostic-format.h b/gcc/diagnostic-format.h index 07db2cd..a4c776c 100644 --- a/gcc/diagnostic-format.h +++ b/gcc/diagnostic-format.h @@ -72,6 +72,9 @@ public: Subclasses should update their m_printer accordingly. */ virtual void update_printer () = 0; + virtual void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &) = 0; + diagnostic_context &get_context () const { return m_context; } pretty_printer *get_printer () const { return m_printer.get (); } diff --git a/gcc/diagnostic-metadata.h b/gcc/diagnostic-metadata.h index baeeb6e..f6ea7dc 100644 --- a/gcc/diagnostic-metadata.h +++ b/gcc/diagnostic-metadata.h @@ -23,11 +23,20 @@ along with GCC; see the file COPYING3. If not see class sarif_object; +namespace diagnostics { + namespace digraphs { + class lazy_digraphs; + } // namespace digraphs +} // namespace diagnostics + /* A bundle of additional metadata that can be associated with a diagnostic. This supports an optional CWE identifier, and zero or more - "rules". */ + "rules". + + Additionally, this provides a place to associate a diagnostic + with zero or more directed graphs. */ class diagnostic_metadata { @@ -64,7 +73,7 @@ class diagnostic_metadata const char *m_url; }; - diagnostic_metadata () : m_cwe (0) {} + diagnostic_metadata () : m_cwe (0), m_lazy_digraphs (nullptr) {} virtual ~diagnostic_metadata () {} /* Hook for SARIF output to allow for adding diagnostic-specific @@ -87,9 +96,25 @@ class diagnostic_metadata unsigned get_num_rules () const { return m_rules.length (); } const rule &get_rule (unsigned idx) const { return *(m_rules[idx]); } + void + set_lazy_digraphs (const diagnostics::digraphs::lazy_digraphs *lazy_digraphs) + { + m_lazy_digraphs = lazy_digraphs; + } + + const diagnostics::digraphs::lazy_digraphs * + get_lazy_digraphs () const + { + return m_lazy_digraphs; + } + private: int m_cwe; auto_vec<const rule *> m_rules; + + /* An optional way to create directed graphs associated with the + diagnostic, for the sinks that support this (e.g. SARIF). */ + const diagnostics::digraphs::lazy_digraphs *m_lazy_digraphs; }; #endif /* ! GCC_DIAGNOSTIC_METADATA_H */ diff --git a/gcc/diagnostic-output-spec.cc b/gcc/diagnostic-output-spec.cc index 25ef86f..4fa3176 100644 --- a/gcc/diagnostic-output-spec.cc +++ b/gcc/diagnostic-output-spec.cc @@ -183,7 +183,7 @@ public: private: static sarif_generation_options make_sarif_gen_opts (enum sarif_version version, - bool xml_state); + bool state_graph); static std::unique_ptr<sarif_serialization_format> make_sarif_serialization_object (enum sarif_serialization_kind); @@ -431,7 +431,7 @@ sarif_scheme_handler::make_sink (const context &ctxt, enum sarif_serialization_kind serialization_kind = sarif_serialization_kind::json; enum sarif_version version = sarif_version::v2_1_0; - bool xml_state = false; + bool state_graph = false; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -469,10 +469,10 @@ sarif_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "xml-state") + if (key == "state-graphs") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - xml_state)) + state_graph)) return nullptr; continue; } @@ -481,8 +481,8 @@ sarif_scheme_handler::make_sink (const context &ctxt, 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"); - known_keys.safe_push ("xml-state"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; @@ -513,7 +513,7 @@ sarif_scheme_handler::make_sink (const context &ctxt, if (!output_file) return nullptr; - auto sarif_gen_opts = make_sarif_gen_opts (version, xml_state); + auto sarif_gen_opts = make_sarif_gen_opts (version, state_graph); auto serialization_obj = make_sarif_serialization_object (serialization_kind); @@ -527,11 +527,11 @@ sarif_scheme_handler::make_sink (const context &ctxt, sarif_generation_options sarif_scheme_handler::make_sarif_gen_opts (enum sarif_version version, - bool xml_state) + bool state_graph) { sarif_generation_options sarif_gen_opts; sarif_gen_opts.m_version = version; - sarif_gen_opts.m_xml_state = xml_state; + sarif_gen_opts.m_state_graph = state_graph; return sarif_gen_opts; } @@ -561,8 +561,8 @@ html_scheme_handler::make_sink (const context &ctxt, label_text filename; bool javascript = true; bool show_state_diagrams = false; - bool show_state_diagram_xml = false; - bool show_state_diagram_dot_src = false; + bool show_state_diagrams_sarif = false; + bool show_state_diagrams_dot_src = false; for (auto& iter : parsed_arg.m_kvs) { const std::string &key = iter.first; @@ -593,17 +593,17 @@ html_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "show-state-diagram-dot-src") + if (key == "show-state-diagrams-dot-src") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagram_dot_src)) + show_state_diagrams_dot_src)) return nullptr; continue; } - if (key == "show-state-diagram-xml") + if (key == "show-state-diagrams-sarif") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, - show_state_diagram_xml)) + show_state_diagrams_sarif)) return nullptr; continue; } @@ -615,7 +615,7 @@ html_scheme_handler::make_sink (const context &ctxt, 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-xml"); + known_keys.safe_push ("show-state-diagram-sarif"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; @@ -649,8 +649,8 @@ html_scheme_handler::make_sink (const context &ctxt, html_gen_opts.m_css = css; html_gen_opts.m_javascript = javascript; html_gen_opts.m_show_state_diagrams = show_state_diagrams; - html_gen_opts.m_show_state_diagram_xml = show_state_diagram_xml; - html_gen_opts.m_show_state_diagram_dot_src = show_state_diagram_dot_src; + html_gen_opts.m_show_state_diagrams_sarif = show_state_diagrams_sarif; + html_gen_opts.m_show_state_diagrams_dot_src = show_state_diagrams_dot_src; auto sink = make_html_sink (dc, *ctxt.get_affected_location_mgr (), diff --git a/gcc/diagnostic-path.cc b/gcc/diagnostic-path.cc index 3e169da..1ae87b7 100644 --- a/gcc/diagnostic-path.cc +++ b/gcc/diagnostic-path.cc @@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "diagnostic.h" #include "diagnostic-path.h" -#include "xml.h" +#include "diagnostic-state-graphs.h" /* Disable warnings about missing quoting in GCC diagnostics for the print calls below. */ @@ -156,12 +156,12 @@ diagnostic_event::get_desc (pretty_printer &ref_pp) const return label_text::take (xstrdup (pp_formatted_text (pp.get ()))); } -// Base implementation of diagnostic_event::maybe_make_xml_state +// Base implementation of diagnostic_event::maybe_make_diagnostic_state_graph -std::unique_ptr<xml::document> -diagnostic_event::maybe_make_xml_state (bool) const +std::unique_ptr<diagnostics::digraphs::digraph> +diagnostic_event::maybe_make_diagnostic_state_graph (bool) const { - // Don't attempt to make a state document: + // Don't attempt to make a state graph: return nullptr; } diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h index e68f768..ca2628e 100644 --- a/gcc/diagnostic-path.h +++ b/gcc/diagnostic-path.h @@ -30,6 +30,12 @@ namespace xml { class document; } class sarif_builder; class sarif_object; +namespace diagnostics { +namespace digraphs { + class digraph; +} // namespace digraphs +} //namespace diagnostics + /* A diagnostic_path is an optional additional piece of metadata associated with a diagnostic (via its rich_location). @@ -173,9 +179,9 @@ class diagnostic_event } /* Hook for capturing state at this event, potentially for visualizing - in HTML output. */ - virtual std::unique_ptr<xml::document> - maybe_make_xml_state (bool debug) const; + in HTML output, or for adding to SARIF. */ + virtual std::unique_ptr<diagnostics::digraphs::digraph> + maybe_make_diagnostic_state_graph (bool debug) const; label_text get_desc (pretty_printer &ref_pp) const; }; diff --git a/gcc/diagnostic-state-graphs.cc b/gcc/diagnostic-state-graphs.cc new file mode 100644 index 0000000..62eb6f5 --- /dev/null +++ b/gcc/diagnostic-state-graphs.cc @@ -0,0 +1,158 @@ +/* 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 "diagnostic-state-graphs.h" +#include "graphviz.h" +#include "xml.h" +#include "xml-printer.h" +#include "intl.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 selftest { + +static void +test_node_attrs () +{ + diagnostics::digraphs::digraph g; + diagnostics::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 +diagnostic_state_graphs_cc_tests () +{ + test_node_attrs (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ diff --git a/gcc/diagnostic-state-graphs.h b/gcc/diagnostic-state-graphs.h new file mode 100644 index 0000000..def6d05 --- /dev/null +++ b/gcc/diagnostic-state-graphs.h @@ -0,0 +1,156 @@ +/* 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/>. */ + +#ifndef GCC_DIAGNOSTIC_STATE_GRAPHS_H +#define GCC_DIAGNOSTIC_STATE_GRAPHS_H + +#include "diagnostic-digraphs.h" +#include "logical-location.h" + +/* diagnostics::digraphs provides support for directed graphs. + + diagnostics::state_graphs provides a way to extend these graphs + for representing "state graphs" i.e. a representation of the state + of memory inside a program, for use e.g. by -fanalyzer. + + Specifically, nodes represent memory regions, and we use property bags + 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_location + 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_location_manager &logical_loc_mgr); + +} // namespace state_graphs +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTIC_STATE_GRAPHS_H */ diff --git a/gcc/diagnostic-state-to-dot.cc b/gcc/diagnostic-state-to-dot.cc index ddae83b..8195c11 100644 --- a/gcc/diagnostic-state-to-dot.cc +++ b/gcc/diagnostic-state-to-dot.cc @@ -1,4 +1,4 @@ -/* Creating GraphViz .dot files from XML state documents. +/* Creating GraphViz .dot files from diagnostic state graphs. Copyright (C) 2025 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -27,81 +27,59 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" +#include "diagnostic-state-graphs.h" +#include "graphviz.h" #include "xml.h" #include "xml-printer.h" -#include "graphviz.h" +#include "intl.h" + +using namespace diagnostics::state_graphs; static int -get_depth (const xml::element &e) +get_depth (const diagnostics::digraphs::node &n) { int deepest_child = 0; - for (auto &iter : e.m_children) - if (xml::element *child_element = iter->dyn_cast_element ()) + for (size_t i = 0; i < n.get_num_children (); ++i) deepest_child = std::max (deepest_child, - get_depth (*child_element)); + get_depth (n.get_child (i))); return deepest_child + 1; } -enum class dynalloc_state -{ - unknown, - nonnull, - unchecked, - freed -}; - static const char * -get_color_for_dynalloc_state (enum dynalloc_state dynalloc_st) +get_color_for_dynalloc_state (enum node_dynalloc_state dynalloc_st) { switch (dynalloc_st) { default: gcc_unreachable (); break; - case dynalloc_state::unknown: - case dynalloc_state::nonnull: + case node_dynalloc_state::unknown: + case node_dynalloc_state::nonnull: return nullptr; - case dynalloc_state::unchecked: + case node_dynalloc_state::unchecked: return "#ec7a08"; // pf-orange-400 - case dynalloc_state::freed: + case node_dynalloc_state::freed: return "#cc0000"; // pf-red-100 } } static void set_color_for_dynalloc_state (dot::attr_list &attrs, - enum dynalloc_state dynalloc_state) + enum node_dynalloc_state state) { - if (const char *color = get_color_for_dynalloc_state (dynalloc_state)) + if (const char *color = get_color_for_dynalloc_state (state)) attrs.add (dot::id ("color"), dot::id (color)); } -static enum dynalloc_state -get_dynalloc_state (const xml::element &input_element) -{ - const char *dyn_alloc_state = input_element.get_attr ("dynamic-alloc-state"); - if (!dyn_alloc_state) - return dynalloc_state::unknown; - - if (dyn_alloc_state == std::string ("unchecked")) - return dynalloc_state::unchecked; - - if (dyn_alloc_state == std::string ("nonnull")) - return dynalloc_state::nonnull; - - if (dyn_alloc_state == std::string ("freed")) - return dynalloc_state::freed; - - return dynalloc_state::unknown; -} - class state_diagram : public dot::graph { public: - state_diagram (const xml::document &input_state_doc) - : m_next_id (0), + state_diagram (const diagnostics::digraphs::digraph &input_state_graph, + const logical_location_manager &logical_loc_mgr) + : m_logical_loc_mgr (logical_loc_mgr), + // m_next_id (0), m_show_tags (false) { // "node [shape=plaintext]\n" @@ -112,41 +90,46 @@ public: add_stmt (std::move (attr_stmt)); } - /* Recurse down the XML state diagram, creating subgraphs + /* Determine which nodes are involved in edges. */ + for (size_t i = 0; i < input_state_graph.get_num_edges (); ++i) + { + auto &edge = input_state_graph.get_edge (i); + m_src_nodes.insert (&edge.get_src_node ()); + m_dst_nodes.insert (&edge.get_dst_node ()); + } + + /* Recurse down the nodes in the state graph, creating subgraphs and then eventually creating nodes, and recursively - creating XML tables, adding ports for the endpoints of edges, - and recording edges we'll want to create (into m_pending_edges). */ - xml::element *input_elmt_state_diagram - = input_state_doc.find_child_element ("state-diagram"); - gcc_assert (input_elmt_state_diagram); - xml::element *input_elmt_mem_regions - = input_elmt_state_diagram->find_child_element ("memory-regions"); - if (!input_elmt_mem_regions) - return; + creating XML tables, and adding ports for the endpoints of edges + where needed. */ + auto root_cluster = std::make_unique<dot::subgraph> (dot::id ("cluster_memory_regions")); - for (auto &iter : input_elmt_mem_regions->m_children) - on_input_xml_node (*root_cluster, *iter); + 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))); add_stmt (std::move (root_cluster)); - /* We should now have ports for edge endpoints for all region ids. - Use them now to create edges. */ - for (auto &pe : m_pending_edges) + /* Now create dot edges for edges in input_stage_graph. */ + for (size_t i = 0; i < input_state_graph.get_num_edges (); ++i) { - auto search = m_region_id_to_dst_node_id.find (pe.m_dst_region_id); - if (search != m_region_id_to_dst_node_id.end ()) - { - auto &dst_node_id = search->second; - auto e = std::make_unique<dot::edge_stmt> (pe.m_src_node_id, - dst_node_id); - - auto dynalloc_state = m_region_id_to_dynalloc_state.find (pe.m_dst_region_id); - if (dynalloc_state != m_region_id_to_dynalloc_state.end ()) - set_color_for_dynalloc_state (e->m_attrs, - dynalloc_state->second); - - add_stmt (std::move (e)); - } + auto &edge = input_state_graph.get_edge (i); + auto &src_node = edge.get_src_node (); + auto &dst_node = edge.get_dst_node (); + + auto src_port_id = m_src_node_to_port_id.find (&src_node); + if (src_port_id == m_src_node_to_port_id.end ()) + continue; + auto dst_port_id = m_dst_node_to_port_id.find (&dst_node); + if (dst_port_id == m_dst_node_to_port_id.end ()) + continue; + + 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 ()); + + add_stmt (std::move (e)); } } @@ -165,35 +148,58 @@ private: } dot::id - make_id (bool cluster = false) + make_id (state_node_ref state_node, bool cluster) { + std::string input_node_id = state_node.m_node.get_id (); if (cluster) - return std::string ("cluster_") + std::to_string (m_next_id++); + return std::string ("cluster_") + input_node_id; else - return std::string ("id_") + std::to_string (m_next_id++); + return input_node_id; } bool - starts_node_p (const xml::element &e) + starts_node_p (state_node_ref state_node) { - if (e.m_kind == "stack" - || e.m_kind == "heap-buffer" - || e.m_kind == "variable") // e.g. within globals - return true; - return false; + switch (state_node.get_node_kind ()) + { + default: + return false; + + case node_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: + return true; + } } - void - on_input_xml_node (dot::subgraph &parent_subgraph, - xml::node &input_node) + const char * + get_label_for_node (state_node_ref state_node) { - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; + switch (state_node.get_node_kind ()) + { + default: + return nullptr; + + case node_kind::globals: + return _("Globals"); + case node_kind::code: + return _("Code"); + case node_kind::stack: + return _("Stack"); + case node_kind::heap_: + return _("Heap"); + } + } - dot::id sg_id = make_id (true); + void + on_input_state_node (dot::subgraph &parent_subgraph, + state_node_ref state_node) + { + dot::id sg_id = make_id (state_node, true); - if (starts_node_p (*input_element)) + if (starts_node_p (state_node)) { // Create node with table xml::element table ("table", false); @@ -202,14 +208,14 @@ private: xp.set_attr ("cellborder", "1"); xp.set_attr ("cellspacing", "0"); - const int max_depth = get_depth (*input_element); + const int max_depth = get_depth (state_node.m_node); const int num_columns = max_depth + 2; - dot::id id_of_node = make_id (); - on_xml_node (id_of_node, xp, *input_element, - max_depth, 0, num_columns); + dot::id id_of_dot_node = make_id (state_node, false); + on_node_in_table (id_of_dot_node, xp, state_node, + max_depth, 0, num_columns); - auto node = std::make_unique<dot::node_stmt> (std::move (id_of_node)); + auto node = std::make_unique<dot::node_stmt> (std::move (id_of_dot_node)); node->m_attrs.add (dot::id ("shape"), dot::id ("plaintext")); @@ -224,12 +230,13 @@ private: { auto child_subgraph = std::make_unique<dot::subgraph> (std::move (sg_id)); - if (const char *label = input_element->get_attr ("label")) + if (const char *label = get_label_for_node (state_node)) child_subgraph->add_attr (dot::id ("label"), dot::id (label)); // recurse: - for (auto &iter : input_element->m_children) - on_input_xml_node (*child_subgraph, *iter); + for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + on_input_state_node (*child_subgraph, + state_node.m_node.get_child (i)); parent_subgraph.m_stmt_list.add_stmt (std::move (child_subgraph)); } } @@ -237,13 +244,13 @@ private: enum class style { h1, h2 }; void - add_title_tr (const dot::id &id_of_node, + add_title_tr (const dot::id &id_of_dot_node, xml::printer &xp, int num_columns, - const xml::element &input_element, + state_node_ref state_node, std::string heading, enum style styl, - enum dynalloc_state dynalloc_state) + enum node_dynalloc_state dynalloc_state) { xp.push_tag ("tr", true); xp.push_tag ("td", false); @@ -282,183 +289,194 @@ private: xp.add_text (std::move (heading)); xp.pop_tag ("font"); - maybe_add_dst_port (id_of_node, xp, input_element); + maybe_add_dst_port (id_of_dot_node, xp, state_node); xp.pop_tag ("td"); xp.pop_tag ("tr"); } - /* Recursively add <TR> to XP for INPUT_NODE and its descendents. */ + /* Recursively add <TR> to XP for STATE_NODE and its descendents. */ void - on_xml_node (const dot::id &id_of_node, - xml::printer &xp, - xml::node &input_node, - int max_depth, - int depth, - int num_columns) + on_node_in_table (const dot::id &id_of_dot_node, + xml::printer &xp, + state_node_ref state_node, + int max_depth, + int depth, + int num_columns) { bool recurse = true; + auto input_node_kind = state_node.get_node_kind (); - xml::element *input_element = input_node.dyn_cast_element (); - if (!input_element) - return; - - if (input_element->m_kind == "concrete-bindings") - return; - if (input_element->m_kind == "padding") - return; - - if (input_element->m_kind == "stack") - { - add_title_tr (id_of_node, xp, num_columns, *input_element, "Stack", - style::h1, dynalloc_state::unknown); - } - else if (input_element->m_kind == "stack-frame") - { - if (const char *function = input_element->get_attr ("function")) - add_title_tr (id_of_node, xp, num_columns, *input_element, - std::string ("Frame: ") + function, - style::h2, dynalloc_state::unknown); - } - else if (input_element->m_kind == "heap-buffer") + switch (input_node_kind) { - const char *extents = input_element->get_attr ("dynamic-extents"); - enum dynalloc_state dynalloc_st = get_dynalloc_state (*input_element); - if (auto region_id = input_element->get_attr ("region_id")) - m_region_id_to_dynalloc_state[region_id] = dynalloc_st; - const char *type = input_element->get_attr ("type"); - pretty_printer pp; - switch (dynalloc_st) - { - default: - gcc_unreachable (); - - case dynalloc_state::unknown: - case dynalloc_state::nonnull: - if (type) - { + case node_kind::padding: + case node_kind::other: + return; + + case node_kind::stack: + add_title_tr (id_of_dot_node, xp, num_columns, state_node, "Stack", + style::h1, + node_dynalloc_state::unknown); + break; + case node_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); + break; + case node_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 (); + pretty_printer pp; + switch (dynalloc_st) + { + default: + gcc_unreachable (); + + case node_dynalloc_state::unknown: + case node_dynalloc_state::nonnull: + if (type) + { if (extents) pp_printf (&pp, "%s (%s byte allocation)", type, extents); else pp_printf (&pp, "%s", type); - } - else + } + else + { + if (extents) + pp_printf (&pp, "%s byte allocation", + extents); + } + break; + + case node_dynalloc_state::unchecked: + if (type) + { + if (extents) + pp_printf (&pp, "%s (unchecked %s byte allocation)", + type, extents); + } + else + { + if (extents) + pp_printf (&pp, "Unchecked %s byte allocation", + extents); + } + break; + + case node_dynalloc_state::freed: + // TODO: show deallocator + // TODO: show deallocation event + pp_printf (&pp, "Freed buffer"); + break; + } + maybe_add_dst_port (id_of_dot_node, xp, state_node); + add_title_tr (id_of_dot_node, xp, num_columns, state_node, + pp_formatted_text (&pp), + style::h2, + dynalloc_st); + } + break; + + default: + { + xp.push_tag ("tr", true); + + maybe_add_dst_port (id_of_dot_node, xp, state_node); + + if (depth > 0) + { + /* Indent, by create a <td> spanning "depth" columns. */ + xp.push_tag ("td", false); + xp.set_attr ("colspan", std::to_string (depth)); + xp.add_text (" "); // graphviz doesn't like <td/> + xp.pop_tag ("td"); + } + + switch (input_node_kind) + { + default: + break; + case node_kind::variable: { - if (extents) - pp_printf (&pp, "%s byte allocation", - extents); + const char *name = state_node.get_name (); + gcc_assert (name); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text (name); + pop_src_text (xp); + xp.pop_tag ("td"); } - break; - - case dynalloc_state::unchecked: - if (type) + break; + case node_kind::element: { - if (extents) - pp_printf (&pp, "%s (unchecked %s byte allocation)", - type, extents); + const char *index = state_node.get_index (); + gcc_assert (index); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text ("["); + xp.add_text (index); + xp.add_text ("]"); + pop_src_text (xp); + xp.pop_tag ("td"); } - else + break; + case node_kind::field: { - if (extents) - pp_printf (&pp, "Unchecked %s byte allocation", - extents); + const char *name = state_node.get_name (); + gcc_assert (name); + xp.push_tag ("td", false); + maybe_add_dst_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text ("."); + xp.add_text (name); + pop_src_text (xp); + xp.pop_tag ("td"); } - break; - - case dynalloc_state::freed: - // TODO: show deallocator - // TODO: show deallocation event - pp_printf (&pp, "Freed buffer"); - break; - } - add_title_tr (id_of_node, xp, num_columns, *input_element, - pp_formatted_text (&pp), - style::h2, - dynalloc_st); - } - else - { - xp.push_tag ("tr", true); - if (depth > 0) - { - /* Indent, by create a <td> spanning "depth" columns. */ - xp.push_tag ("td", false); - xp.set_attr ("colspan", std::to_string (depth)); - xp.add_text (" "); // graphviz doesn't like <td/> - xp.pop_tag ("td"); - } - if (m_show_tags) - { - // Debug: show XML tag - xp.push_tag ("td", false); - xp.add_text ("<"); - xp.add_text (input_element->m_kind); - xp.add_text (">"); - xp.pop_tag ("td"); - } - if (input_element->m_kind == "variable") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "element") - { - const char *index = input_element->get_attr ("index"); - gcc_assert (index); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("["); - xp.add_text (index); - xp.add_text ("]"); - pop_src_text (xp); - xp.pop_tag ("td"); - } - else if (input_element->m_kind == "field") - { - const char *name = input_element->get_attr ("name"); - gcc_assert (name); - xp.push_tag ("td", false); - maybe_add_dst_port (id_of_node, xp, *input_element); - push_src_text (xp); - xp.add_text ("."); - xp.add_text (name); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (const char *type = input_element->get_attr ("type")) - { - xp.push_tag ("td", false); - if (max_depth > depth) - xp.set_attr ("colspan", std::to_string (max_depth - depth)); - xp.set_attr ("align", "right"); - push_src_text (xp); - xp.add_text (type); - pop_src_text (xp); - xp.pop_tag ("td"); - } - if (auto value = input_element->find_child_element ("value-of-region")) - { - xp.push_tag ("td", false); - for (auto &iter : value->m_children) - if (auto child_element = iter->dyn_cast_element ()) - print_value (id_of_node, xp, *child_element); - xp.pop_tag ("td"); - recurse = false; - } - xp.pop_tag ("tr"); + break; + } + + if (const char *type = state_node.get_type ()) + { + xp.push_tag ("td", false); + xp.set_attr ("align", "right"); + push_src_text (xp); + xp.add_text (type); + pop_src_text (xp); + xp.pop_tag ("td"); + } + + if (const char *value = state_node.get_value ()) + { + xp.push_tag ("td", false); + xp.set_attr ("align", "left"); + maybe_add_src_port (id_of_dot_node, xp, state_node); + push_src_text (xp); + xp.add_text (value); + pop_src_text (xp); + xp.pop_tag ("td"); + recurse = false; + } + xp.pop_tag ("tr"); + } + break; } if (recurse) - for (auto &iter : input_element->m_children) - on_xml_node (id_of_node, xp, *iter, max_depth, depth + 1, num_columns); + for (size_t i = 0; i < state_node.m_node.get_num_children (); ++i) + on_node_in_table (id_of_dot_node, xp, + state_node.m_node.get_child (i), + max_depth, depth + 1, num_columns); } void @@ -474,64 +492,63 @@ private: xp.pop_tag ("font"); } + /* If STATE_NODE is in m_src_nodes, add a port to XP for possible + incoming edges to use. */ + void - print_value (const dot::id &id_of_node, - xml::printer &xp, - xml::element &input_element) + maybe_add_src_port (const dot::id &id_of_dot_node, + xml::printer &xp, + state_node_ref state_node) { - if (input_element.m_kind == "pointer-to-region") - if (const char *dst_region_id = input_element.get_attr ("region_id")) - { - dot::id src_port_id = make_id (); - xp.set_attr ("port", src_port_id.m_str); - m_pending_edges.push_back - ({dot::node_id (id_of_node, - dot::port (src_port_id, - dot::compass_pt::e)), - dst_region_id}); - } - - if (input_element.m_kind == "uninitialized") - { - xp.add_text ("(uninitialized)"); - return; - } + auto iter = m_src_nodes.find (&state_node.m_node); + if (iter == m_src_nodes.end ()) + return; - if (auto dump_text = input_element.get_attr ("dump-text")) - xp.add_text (dump_text); + dot::id src_id = make_id (state_node, false); + 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}); + xp.set_attr ("port", src_id.m_str); } - /* If INPUT_ELEMENT has a "region_id", add a port to XP for possible + /* If STATE_NODE is in m_dst_nodes, add a port to XP for possible incoming edges to use. */ void - maybe_add_dst_port (const dot::id &id_of_node, + maybe_add_dst_port (const dot::id &id_of_dot_node, xml::printer &xp, - const xml::element &input_element) + state_node_ref state_node) { - if (const char *region_id = input_element.get_attr ("region_id")) - { - dot::id dst_id = make_id (); - dot::node_id node_id (id_of_node, - dot::port (dst_id/*, - dot::compass_pt::w*/)); - xp.set_attr ("port", dst_id.m_str); - m_region_id_to_dst_node_id.emplace (std::string (region_id), - std::move (node_id)); - } - } + auto iter = m_dst_nodes.find (&state_node.m_node); + if (iter == m_dst_nodes.end ()) + return; + dot::id dst_id = make_id (state_node, false); + 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}); + xp.set_attr ("port", dst_id.m_str); + } private: - int m_next_id; - std::vector<pending_edge> m_pending_edges; - std::map<std::string, dot::node_id> m_region_id_to_dst_node_id; - std::map<std::string, enum dynalloc_state> m_region_id_to_dynalloc_state; + const logical_location_manager &m_logical_loc_mgr; + + /* All nodes involved in edges (and thus will need a port). */ + std::set<diagnostics::digraphs::node *> m_src_nodes; + std::set<diagnostics::digraphs::node *> m_dst_nodes; + + std::map<diagnostics::digraphs::node *, dot::node_id> m_src_node_to_port_id; + std::map<diagnostics::digraphs::node *, dot::node_id> m_dst_node_to_port_id; + bool m_show_tags; }; std::unique_ptr<dot::graph> -make_dot_graph_from_xml_state (const xml::document &xml_state) +diagnostics::state_graphs:: +make_dot_graph (const diagnostics::digraphs::digraph &state_graph, + const logical_location_manager &logical_loc_mgr) { - return std::make_unique<state_diagram> (xml_state); + return std::make_unique<state_diagram> (state_graph, logical_loc_mgr); } diff --git a/gcc/diagnostic-state.h b/gcc/diagnostic-state.h deleted file mode 100644 index 68c3e00..0000000 --- a/gcc/diagnostic-state.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Capturing changing state in diagnostic paths. - 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_DIAGNOSTIC_STATE_H -#define GCC_DIAGNOSTIC_STATE_H - -/* We want to be able to express changing program states in diagnostic paths, - so that we can emit this in HTML and SARIF output, and to keep this - separate from implementation details of -fanalyzer. - - For now, we use xml::document as the type in the diagnostic subsystem - for (optionally) tracking the state at a diagnostic_event. */ - -namespace xml { class document; } -namespace dot { class graph; } - -extern std::unique_ptr<dot::graph> -make_dot_graph_from_xml_state (const xml::document &xml_state); - -#endif /* GCC_DIAGNOSTIC_STATE_H */ diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 088390b..7e5ac87 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -1624,6 +1624,14 @@ diagnostic_context::report_verbatim (text_info &text) } } +void +diagnostic_context:: +report_global_digraph (const diagnostics::digraphs::lazy_digraph &ldg) +{ + for (auto sink : m_output_sinks) + sink->report_global_digraph (ldg); +} + /* Get the number of digits in the decimal representation of VALUE. */ int diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 00f6e35..04eb2b1 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -26,6 +26,12 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print.h" #include "diagnostic-core.h" +namespace diagnostics { + namespace digraphs { + class lazy_digraph; + } // namespace digraphs +} // namespace diagnostics + namespace text_art { class theme; @@ -616,6 +622,11 @@ public: bool report_diagnostic (diagnostic_info *); void report_verbatim (text_info &); + /* Report a directed graph associated with the run as a whole + to any sinks that support directed graphs. */ + void + report_global_digraph (const diagnostics::digraphs::lazy_digraph &); + diagnostic_t classify_diagnostic (diagnostic_option_id option_id, diagnostic_t new_kind, diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index 4dd14c8..4efba4d 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -624,17 +624,17 @@ which writes out the diagnostics in HTML form, and generates SVG state diagrams visualizing the state of memory at each event (inspired by the "ddd" debugger). These can be seen by pressing 'j' and 'k' to single-step forward and backward through events. Note that these SVG diagrams are -created from an intermediate XML representation generated from -@code{program_state} objects. The XML representation can be easier to +created from an intermediate SARIF directed graph representation generated from +@code{program_state} objects. The SARIF representation can be easier to read - for example, rather than storing the contents of memory via byte offsets, it uses fields for structs and element indexes for arrays, recursively. However it is a different representation, and thus bugs could be hidden by this transformation. Generating the SVG diagrams requires an invocation of "dot" per event, so it noticeably slows down diagnostic -emission, hence the opt-in command-line flag. The XML and ``dot'' +emission, hence the opt-in command-line flag. The SARIF and ``dot'' representations can be seen by @code{__analyzer_dump_xml} and @code{__analyzer_dump_dot} below (writing them to stderr), or by adding -@code{show-state-diagrams-xml=yes} and +@code{show-state-diagrams-sarif=yes} and @code{show-state-diagrams-dot-src=yes} to the html sink, which shows them within the generated HTML next to the generated SVG. @@ -792,9 +792,9 @@ will emit a warning describing the state of the 2nd argument a name matching the 1st argument (which must be a string literal). This is for use when debugging, and may be of use in DejaGnu tests. -@item __analyzer_dump_xml +@item __analyzer_dump_sarif @smallexample -__analyzer_dump_xml (); +__analyzer_dump_sarif (); @end smallexample will dump the copious information about the analyzer's state each time it diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 74f5ee2..9a1aa37 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -353,7 +353,7 @@ Objective-C and Objective-C++ Dialects}. -Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat -Wc11-c23-compat -Wc23-c2y-compat -Wc++-compat -Wc++11-compat -Wc++14-compat -Wc++17-compat --Wc++20-compat +-Wc++20-compat -Wc++26-compat -Wno-c++11-extensions -Wno-c++14-extensions -Wno-c++17-extensions -Wno-c++20-extensions -Wno-c++23-extensions -Wcalloc-transposed-args @@ -6143,10 +6143,10 @@ end-users, and subject to change or removal without notice: @table @gcctabopt -@item xml-state=@r{[}yes@r{|}no@r{]} +@item state-graphs=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{xml-state=yes}, then attempt to capture detailed state information -from @option{-fanalyzer} in the generated SARIF. +If @code{state-graphs=yes}, then attempt to capture detailed state +information from @option{-fanalyzer} in the generated SARIF. @end table @@ -6194,11 +6194,11 @@ 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-xml=@r{[}yes@r{|}no@r{]} +@item show-state-diagrams-sarif=@r{[}yes@r{|}no@r{]} This is a debugging feature and defaults to @code{no}. -If @code{show-state-diagrams-xml=yes} +If @code{show-state-diagrams-sarif=yes} then if @code{show-state-diagrams=yes}, the generated state diagrams will -also show an XML representation of the state. +also show a SARIF representation of the state. @end table @@ -9690,6 +9690,12 @@ and ISO C++ 2017. This warning is enabled by @option{-Wall}. Warn about C++ constructs whose meaning differs between ISO C++ 2017 and ISO C++ 2020. This warning is enabled by @option{-Wall}. +@opindex Wc++26-compat +@opindex Wno-c++26-compat +@item -Wc++26-compat @r{(C++ and Objective-C++ only)} +Warn about C++ constructs whose meaning differs between ISO C++ 2023 +and upcoming ISO C++ 2026. This warning is enabled by @option{-Wall}. + @opindex Wc++11-extensions @opindex Wno-c++11-extensions @item -Wno-c++11-extensions @r{(C++ and Objective-C++ only)} diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst index 10adcc5..0e078d0 100644 --- a/gcc/doc/libgdiagnostics/topics/compatibility.rst +++ b/gcc/doc/libgdiagnostics/topics/compatibility.rst @@ -178,6 +178,8 @@ acccessing values within a :type:`diagnostic_logical_location`: * :func:`diagnostic_logical_location_get_decorated_name` +.. _LIBGDIAGNOSTICS_ABI_2: + ``LIBGDIAGNOSTICS_ABI_2`` ------------------------- ``LIBGDIAGNOSTICS_ABI_2`` covers the addition of these functions for @@ -186,3 +188,34 @@ supporting command-line options and SARIF playback: * :func:`diagnostic_manager_add_sink_from_spec` * :func:`diagnostic_manager_set_analysis_target` + +.. _LIBGDIAGNOSTICS_ABI_3: + +``LIBGDIAGNOSTICS_ABI_3`` +------------------------- +``LIBGDIAGNOSTICS_ABI_3`` covers the addition of these functions for +working with directed graphs: + + * :func:`diagnostic_manager_new_graph` + + * :func:`diagnostic_manager_take_global_graph` + + * :func:`diagnostic_take_graph` + + * :func:`diagnostic_graph_release` + + * :func:`diagnostic_graph_set_description` + + * :func:`diagnostic_graph_add_node` + + * :func:`diagnostic_graph_add_edge` + + * :func:`diagnostic_graph_get_node_by_id` + + * :func:`diagnostic_graph_get_edge_by_id` + + * :func:`diagnostic_node_set_label` + + * :func:`diagnostic_node_set_location` + + * :func:`diagnostic_node_set_logical_location` diff --git a/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst b/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst index 0390704..c94d19e 100644 --- a/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst +++ b/gcc/doc/libgdiagnostics/topics/diagnostic-manager.rst @@ -63,17 +63,17 @@ Responsibilities include: diagnostic_manager *control_mgr) This function can be used to support option processing similar to GCC's - :option:`-fdiagnostics-add-output=`. This allows command-line tools to - support the same domain-specific language for specifying output sink - as GCC does. - - The function will attempt to parse :param:`spec` as if it were - an argument to GCC's :option:`-fdiagnostics-add-output=OUTPUT-SPEC`. - If successful, it will add an output sink to :param:`affected_mgr` and return zero. - Otherwise, it will emit an error diagnostic to :param:`control_mgr` and + `-fdiagnostics-add-output= <https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-add-output>`_. + This allows command-line tools to support the same domain-specific + language for specifying output sinks as GCC does. + + The function will attempt to parse ``spec`` as if it were + an argument to GCC's `-fdiagnostics-add-output= <https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-add-output>`_. + If successful, it will add an output sink to ``affected_mgr`` and return zero. + Otherwise, it will emit an error diagnostic to ``control_mgr`` and return non-zero. - :param:`affected_mgr` and :param:`control_mgr` can be the same manager, + ``affected_mgr`` and ``control_mgr`` can be the same manager, or be different managers. This function was added in :ref:`LIBGDIAGNOSTICS_ABI_2`; you can @@ -83,14 +83,14 @@ Responsibilities include: #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_manager_add_sink_from_spec - .. function:: void diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, \ const diagnostic_file *file) - This function sets the "main input file" of :param:`mgr` to be - :param:`file`. + This function sets the "main input file" of ``mgr`` to be + ``file``. This affects the :code:`<title>` of generated HTML and - the :code:`role` of the artifact in SARIF output (SARIF v2.1.0 section 3.24.6). + the :code:`role` of the :code:`artifact` in SARIF output + (`SARIF v2.1.0 section 3.24.6 <https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790867>`_). This function was added in :ref:`LIBGDIAGNOSTICS_ABI_2`; you can test for its presence using diff --git a/gcc/doc/libgdiagnostics/topics/graphs.rst b/gcc/doc/libgdiagnostics/topics/graphs.rst new file mode 100644 index 0000000..b976013 --- /dev/null +++ b/gcc/doc/libgdiagnostics/topics/graphs.rst @@ -0,0 +1,197 @@ +.. Copyright (C) 2025 Free Software Foundation, Inc. + Originally contributed by David Malcolm <dmalcolm@redhat.com> + + This 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 of the License, or + (at your option) any later version. + + This program 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 this program. If not, see + <https://www.gnu.org/licenses/>. + +.. default-domain:: c + +Graphs +====== + +.. type:: diagnostic_graph + +SARIF has support for capturing directed graphs (such as callgraphs +and control flow graphs), both at the level of the run as a whole, +and at the level of individual results. + +libgdiagnostics supports this with the following entrypoints, allowing +directed graphs to be + +* created (with :func:`diagnostic_manager_new_graph`) + +* reported "globally" (with :func:`diagnostic_manager_take_global_graph`) + +* reported as part of a :type:`diagnostic` (with :func:`diagnostic_take_graph`), or + +* discarded (with :func:`diagnostic_graph_release`). + +.. function:: diagnostic_graph * diagnostic_manager_new_graph (diagnostic_manager *manager) + + Create a new directed graph. + + The resulting graph is owned by the caller and must have one of + :func:`diagnostic_manager_take_global_graph`, + :func:`diagnostic_take_graph`, + or :func:`diagnostic_graph_release` called on it to avoid leaks. + + The parameter ``manager`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_manager_take_global_graph (diagnostic_manager *manager, \ + diagnostic_graph *graph) + + Report this graph "globally", taking ownership of it. + This won't appear in text sinks, but in SARIF sinks the graph will be + added to theRun.graphs (SARIF v2.1.0 3.14.20). + + Parameters ``manager`` and ``graph`` must both be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_take_graph (diagnostic *diag, \ + diagnostic_graph *graph) + + Add this graph to ``diag``, transferring ownership of it to ``diag``. + This won't appear in text sinks, but in SARIF sinks the graph will be + added to theResult.graphs (SARIF v2.1.0 3.27.19). + + Parameters ``diag`` and ``graph`` must both be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_graph_release (diagnostic_graph *graph) + + Release ``graph`` which must still be owned by the caller + i.e. it must *not* have had + :func:`diagnostic_manager_take_global_graph` or + :func:`diagnostic_take_graph` called on it. + + Parameters ``graph`` can be null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + + +.. function:: void diagnostic_graph_set_description (diagnostic_graph *graph, \ + const char *description) + + Set the description of ``graph`` for use in the value of the + SARIF ``description`` property (SARIF v2.1.0 section 3.39.2). + + The parameter ``graph`` must be non-null. + The parameter ``description`` can be null, for clearing any existing + description. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_node * diagnostic_graph_add_node (diagnostic_graph *graph, \ + const char *node_id, \ + diagnostic_node *parent_node) + + Create and add a new node within ``graph`` with the given `id``. + The id must be unique within nodes in ``graph``. + + The parameters ``graph`` and ``id`` must be non-null. + + ``parent_node`` can be NULL (for a top-level node in the graph), + or non-null for a child node, allowing for arbitrary nesting of + nodes. + + The new node is owned by ``graph``. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_edge * diagnostic_graph_add_edge (diagnostic_graph *graph, \ + const char *edge_id, \ + diagnostic_node *src_node, \ + diagnostic_node *dst_node, \ + const char *label) + + Create and add a new edge within ``graph``. + + The parameters ``graph``, ``src_node`` and ``dest_node`` + must be non-null. + + If non-null, then ``edge_id`` must be unique within ``graph``; + if ``edge_id`` is null then a unique id of the form "edge0", "edge1", + etc will be used automatically. + + If non-null, then ``label`` will be used for the value of the + SARIF ``label`` property (SARIF v2.1.0 section 3.41.3). + + The new edge is owned by ``graph``. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_node *diagnostic_graph_get_node_by_id (diagnostic_graph *graph, \ + const char *node_id) + + Get the node in ``graph`` with the given id, or null. + + The parameters ``graph`` and ``node_id`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: diagnostic_edge *diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, \ + const char *edge_id) + + Get the edge in ``graph`` with the given id, or null. + + The parameters ``graph`` and ``edge_id`` must be non-null. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + + +.. type:: diagnostic_node + +.. function:: void diagnostic_node_set_label (diagnostic_node *node, \ + const char *label) + + Set the label of ``node`` for use in the value of the + SARIF ``label`` property (SARIF v2.1.0 section 3.40.3). + + The parameter ``node`` must be non-null. + The parameter ``label`` can be null, for clearing any existing + label. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_node_set_location (diagnostic_node *node, \ + const diagnostic_physical_location *loc) + + Set the physical location of ``node``, if any. + + The parameter ``node`` must be non-null. + The parameter ``loc`` can be null, for clearing any existing + location. + + If set, the value will be used by SARIF sinks within the + ``location`` property (SARIF v2.1.0 section 3.40.4). + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. + +.. function:: void diagnostic_node_set_logical_location (diagnostic_node *node, \ + const diagnostic_logical_location *logical_loc) + + Set the logical location of ``node``, if any. + + The parameter ``node`` must be non-null. + The parameter ``logical_loc`` _can be null, for clearing any existing + location. + + If set, the value will be used by SARIF sinks within the + ``location`` property (SARIF v2.1.0 section 3.40.4). + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_3`. diff --git a/gcc/doc/libgdiagnostics/topics/index.rst b/gcc/doc/libgdiagnostics/topics/index.rst index 6eb3ed6..966f5ef 100644 --- a/gcc/doc/libgdiagnostics/topics/index.rst +++ b/gcc/doc/libgdiagnostics/topics/index.rst @@ -35,5 +35,6 @@ Topic reference execution-paths.rst text-output.rst sarif.rst + graphs.rst ux.rst compatibility.rst diff --git a/gcc/doc/libgdiagnostics/topics/logical-locations.rst b/gcc/doc/libgdiagnostics/topics/logical-locations.rst index 184b563..294d396 100644 --- a/gcc/doc/libgdiagnostics/topics/logical-locations.rst +++ b/gcc/doc/libgdiagnostics/topics/logical-locations.rst @@ -120,7 +120,7 @@ source location "equal" input values on the same :type:`diagnostic_manager` will return the same instance of :type:`diagnostic_logical_location`. "Equal" here includes different string buffers that compare as equal with - :func:``strcmp`. + :func:`strcmp`. .. function:: void diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, \ const diagnostic_logical_location *loc, \ @@ -147,8 +147,9 @@ Accessors The following functions can be used to access the data that was passed to a :type:`diagnostic_logical_location` when it was created. In each case, the -``loc`` parameter must be non-NULL. :type:`const char *` values will point -at copies of the original buffer. +``loc`` parameter must be non-NULL. The return values will point +at *copies* of the original buffer owned by the +:type:`diagnostic_logical_location`, or be null. .. function:: enum diagnostic_logical_location_kind_t diagnostic_logical_location_get_kind (const diagnostic_logical_location *loc) diff --git a/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst b/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst index 95b95ca..e64b668 100644 --- a/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst +++ b/gcc/doc/libgdiagnostics/tutorial/02-physical-locations.rst @@ -112,10 +112,10 @@ leading to output like this:: foo.c:17: error: can't find 'foo.h'" 17 | #include <foo.h> -where libgdiagnostics will attempt to load the source file and +where libgdiagnostics will attempt to load ``foo.c`` and quote the pertinent line. -If libgdiagnostics cannot open the file, it will merely print:: +If libgdiagnostics cannot open ``foo.c``, it will merely print:: foo.c:17: error: can't find 'foo.h' @@ -160,14 +160,15 @@ On compiling and running the program, we should get this output:: 17 | #include <foo.h> | ^~~~~ -where libgdiagnostics will attempt to load the source file and +where libgdiagnostics will attempt to load ``foo.c`` and underling the pertinent part of the given line. -If libgdiagnostics cannot open the file, it will merely print:: +If libgdiagnostics cannot open ``foo.c``, it will merely print:: foo.c:17:8: error: can't find 'foo.h' -A range can span multiple lines within the same file. +A range can span multiple lines within the same file, but cannot +span multiple files. As before, you can use :func:`diagnostic_manager_debug_dump_location` to dump the locations. For the above example:: diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 168c475..b4fadac 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,30 @@ +2025-07-11 Paul Thomas <pault@gcc.gnu.org> + + PR fortran/106135 + * decl.cc (build_sym): Emit an error if a symbol associated by + an IMPORT, ONLY or IMPORT, all statement is being redeclared. + (gfc_match_import): Parse and check the F2018 versions of the + IMPORT statement. For scopes other than and interface body, if + the symbol cannot be found in the host scope, generate it and + set it up such that gfc_fixup_sibling_symbols can transfer its + 'imported attribute' if it turnes out to be a not yet parsed + procedure. Test for violations of C897-8100. + * gfortran.h : Add 'import_only' to the gfc_symtree structure. + Add the enum, 'importstate', which is used for values the new + field 'import_state' in gfc_namespace. + * parse.cc (gfc_fixup_sibling_symbols): Transfer the attribute + 'imported' to the new symbol. + * resolve.cc (check_sym_import_status, check_import_status): + New functions to test symbols and expressions for violations of + F2018:C8102. + (resolve_call): Test the 'resolved_sym' against C8102 by a call + to 'check_sym_import_status'. + (gfc_resolve_expr): If the expression is OK and an IMPORT + statement has been registered in the current scope, test C102 + by calling 'check_import_status'. + (resolve_select_type): Test the declared derived type in TYPE + IS and CLASS IS statements. + 2025-07-08 Andre Vehreschild <vehre@gcc.gnu.org> PR fortran/120637 diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc index 69acd2d..111ebc5 100644 --- a/gcc/fortran/decl.cc +++ b/gcc/fortran/decl.cc @@ -1723,13 +1723,17 @@ build_sym (const char *name, int elem, gfc_charlen *cl, bool cl_deferred, symbol_attribute attr; gfc_symbol *sym; int upper; - gfc_symtree *st; + gfc_symtree *st, *host_st = NULL; /* Symbols in a submodule are host associated from the parent module or submodules. Therefore, they can be overridden by declarations in the submodule scope. Deal with this by attaching the existing symbol to a new symtree and recycling the old symtree with a new symbol... */ st = gfc_find_symtree (gfc_current_ns->sym_root, name); + if (((st && st->import_only) || (gfc_current_ns->import_state == IMPORT_ALL)) + && gfc_current_ns->parent) + host_st = gfc_find_symtree (gfc_current_ns->parent->sym_root, name); + if (st != NULL && gfc_state_stack->state == COMP_SUBMODULE && st->n.sym != NULL && st->n.sym->attr.host_assoc && st->n.sym->attr.used_in_submodule) @@ -1742,6 +1746,20 @@ build_sym (const char *name, int elem, gfc_charlen *cl, bool cl_deferred, sym->refs++; gfc_set_sym_referenced (sym); } + /* ...Check that F2018 IMPORT, ONLY and IMPORT, ALL statements, within the + current scope are not violated by local redeclarations. Note that there is + no need to guard for std >= F2018 because import_only and IMPORT_ALL are + only set for these standards. */ + else if (host_st && host_st->n.sym + && host_st->n.sym != gfc_current_ns->proc_name + && !(st && st->n.sym + && (st->n.sym->attr.dummy || st->n.sym->attr.result))) + { + gfc_error ("F2018: C8102 %s at %L is already imported by an %s " + "statement and must not be re-declared", name, var_locus, + (st && st->import_only) ? "IMPORT, ONLY" : "IMPORT, ALL"); + return false; + } /* ...Otherwise generate a new symtree and new symbol. */ else if (gfc_get_symbol (name, NULL, &sym, var_locus)) return false; @@ -5100,6 +5118,54 @@ error: } +/* Match the IMPORT statement. IMPORT was added to F2003 as + + R1209 import-stmt is IMPORT [[ :: ] import-name-list ] + + C1210 (R1209) The IMPORT statement is allowed only in an interface-body. + + C1211 (R1209) Each import-name shall be the name of an entity in the + host scoping unit. + + under the description of an interface block. Under F2008, IMPORT was + split out of the interface block description to 12.4.3.3 and C1210 + became + + C1210 (R1209) The IMPORT statement is allowed only in an interface-body + that is not a module procedure interface body. + + Finally, F2018, section 8.8, has changed the IMPORT statement to + + R867 import-stmt is IMPORT [[ :: ] import-name-list ] + or IMPORT, ONLY : import-name-list + or IMPORT, NONE + or IMPORT, ALL + + C896 (R867) An IMPORT statement shall not appear in the scoping unit of + a main-program, external-subprogram, module, or block-data. + + C897 (R867) Each import-name shall be the name of an entity in the host + scoping unit. + + C898 If any IMPORT statement in a scoping unit has an ONLY specifier, + all IMPORT statements in that scoping unit shall have an ONLY + specifier. + + C899 IMPORT, NONE shall not appear in the scoping unit of a submodule. + + C8100 If an IMPORT, NONE or IMPORT, ALL statement appears in a scoping + unit, no other IMPORT statement shall appear in that scoping unit. + + C8101 Within an interface body, an entity that is accessed by host + association shall be accessible by host or use association within + the host scoping unit, or explicitly declared prior to the interface + body. + + C8102 An entity whose name appears as an import-name or which is made + accessible by an IMPORT, ALL statement shall not appear in any + context described in 19.5.1.4 that would cause the host entity + of that name to be inaccessible. */ + match gfc_match_import (void) { @@ -5107,16 +5173,28 @@ gfc_match_import (void) match m; gfc_symbol *sym; gfc_symtree *st; + bool f2018_allowed = gfc_option.allow_std & ~GFC_STD_OPT_F08;; + importstate current_import_state = gfc_current_ns->import_state; - if (gfc_current_ns->proc_name == NULL - || gfc_current_ns->proc_name->attr.if_source != IFSRC_IFBODY) + if (!f2018_allowed + && (gfc_current_ns->proc_name == NULL + || gfc_current_ns->proc_name->attr.if_source != IFSRC_IFBODY)) { gfc_error ("IMPORT statement at %C only permitted in " "an INTERFACE body"); return MATCH_ERROR; } + else if (f2018_allowed + && (!gfc_current_ns->parent || gfc_current_ns->is_block_data)) + goto C897; + + if (f2018_allowed + && (current_import_state == IMPORT_ALL + || current_import_state == IMPORT_NONE)) + goto C8100; - if (gfc_current_ns->proc_name->attr.module_procedure) + if (gfc_current_ns->proc_name + && gfc_current_ns->proc_name->attr.module_procedure) { gfc_error ("F2008: C1210 IMPORT statement at %C is not permitted " "in a module procedure interface body"); @@ -5126,20 +5204,65 @@ gfc_match_import (void) if (!gfc_notify_std (GFC_STD_F2003, "IMPORT statement at %C")) return MATCH_ERROR; + gfc_current_ns->import_state = IMPORT_NOT_SET; + if (f2018_allowed) + { + if (gfc_match (" , none") == MATCH_YES) + { + if (current_import_state == IMPORT_ONLY) + goto C898; + if (gfc_current_state () == COMP_SUBMODULE) + goto C899; + gfc_current_ns->import_state = IMPORT_NONE; + } + else if (gfc_match (" , only :") == MATCH_YES) + { + if (current_import_state != IMPORT_NOT_SET + && current_import_state != IMPORT_ONLY) + goto C898; + gfc_current_ns->import_state = IMPORT_ONLY; + } + else if (gfc_match (" , all") == MATCH_YES) + { + if (current_import_state == IMPORT_ONLY) + goto C898; + gfc_current_ns->import_state = IMPORT_ALL; + } + + if (current_import_state != IMPORT_NOT_SET + && (gfc_current_ns->import_state == IMPORT_NONE + || gfc_current_ns->import_state == IMPORT_ALL)) + goto C8100; + } + + /* F2008 IMPORT<eos> is distinct from F2018 IMPORT, ALL. */ if (gfc_match_eos () == MATCH_YES) { - /* All host variables should be imported. */ - gfc_current_ns->has_import_set = 1; + /* This is the F2008 variant. */ + if (gfc_current_ns->import_state == IMPORT_NOT_SET) + { + if (current_import_state == IMPORT_ONLY) + goto C898; + gfc_current_ns->import_state = IMPORT_F2008; + } + + /* Host variables should be imported. */ + if (gfc_current_ns->import_state != IMPORT_NONE) + gfc_current_ns->has_import_set = 1; return MATCH_YES; } - if (gfc_match (" ::") == MATCH_YES) + if (gfc_match (" ::") == MATCH_YES + && gfc_current_ns->import_state != IMPORT_ONLY) { if (gfc_match_eos () == MATCH_YES) - { - gfc_error ("Expecting list of named entities at %C"); - return MATCH_ERROR; - } + goto expecting_list; + gfc_current_ns->import_state = IMPORT_F2008; + } + else if (gfc_current_ns->import_state == IMPORT_ONLY) + { + if (gfc_match_eos () == MATCH_YES) + goto expecting_list; } for(;;) @@ -5166,12 +5289,28 @@ gfc_match_import (void) if (sym == NULL) { - gfc_error ("Cannot IMPORT %qs from host scoping unit " - "at %C - does not exist.", name); - return MATCH_ERROR; + if (gfc_current_ns->proc_name->attr.if_source == IFSRC_IFBODY) + { + gfc_error ("Cannot IMPORT %qs from host scoping unit " + "at %C - does not exist.", name); + return MATCH_ERROR; + } + else + { + /* This might be a procedure that has not yet been parsed. If + so gfc_fixup_sibling_symbols will replace this symbol with + that of the procedure. */ + gfc_get_sym_tree (name, gfc_current_ns, &st, false, + &gfc_current_locus); + st->n.sym->refs++; + st->n.sym->attr.imported = 1; + st->import_only = 1; + goto next_item; + } } - if (gfc_find_symtree (gfc_current_ns->sym_root, name)) + st = gfc_find_symtree (gfc_current_ns->sym_root, name); + if (st && st->n.sym && st->n.sym->attr.imported) { gfc_warning (0, "%qs is already IMPORTed from host scoping unit " "at %C", name); @@ -5182,6 +5321,7 @@ gfc_match_import (void) st->n.sym = sym; sym->refs++; sym->attr.imported = 1; + st->import_only = 1; if (sym->attr.generic && (sym = gfc_find_dt_in_generic (sym))) { @@ -5193,6 +5333,7 @@ gfc_match_import (void) st->n.sym = sym; sym->refs++; sym->attr.imported = 1; + st->import_only = 1; } goto next_item; @@ -5216,6 +5357,34 @@ gfc_match_import (void) syntax: gfc_error ("Syntax error in IMPORT statement at %C"); return MATCH_ERROR; + +C897: + gfc_error ("F2018: C897 IMPORT statement at %C cannot appear in a main " + "program, an external subprogram, a module or block data"); + return MATCH_ERROR; + +C898: + gfc_error ("F2018: C898 IMPORT statement at %C is not permitted because " + "a scoping unit has an ONLY specifier, can only have IMPORT " + "with an ONLY specifier"); + return MATCH_ERROR; + +C899: + gfc_error ("F2018: C899 IMPORT, NONE shall not appear in the scoping unit" + " of a submodule as at %C"); + return MATCH_ERROR; + +C8100: + gfc_error ("F2018: C8100 IMPORT statement at %C is not permitted because " + "%s has already been declared, which must be unique in the " + "scoping unit", + gfc_current_ns->import_state == IMPORT_ALL ? "IMPORT, ALL" : + "IMPORT, NONE"); + return MATCH_ERROR; + +expecting_list: + gfc_error ("Expecting list of named entities at %C"); + return MATCH_ERROR; } diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index 6848bd1..4c85548 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -2188,6 +2188,7 @@ typedef struct gfc_symtree gfc_omp_udr *omp_udr; } n; + unsigned import_only:1; } gfc_symtree; @@ -2215,6 +2216,17 @@ typedef struct gfc_was_finalized { } gfc_was_finalized; + + /* Flag F2018 import status */ +enum importstate +{ IMPORT_NOT_SET = 0, /* Default condition. */ + IMPORT_F2008, /* Old style IMPORT. */ + IMPORT_ONLY, /* Import list used. */ + IMPORT_NONE, /* No host association. Unique in scoping unit. */ + IMPORT_ALL /* Must be unique in the scoping unit. */ +}; + + /* A namespace describes the contents of procedure, module, interface block or BLOCK construct. */ /* ??? Anything else use these? */ @@ -2328,6 +2340,10 @@ typedef struct gfc_namespace /* Set to 1 if namespace is an interface body with "IMPORT" used. */ unsigned has_import_set:1; + /* Flag F2018 import status */ + ENUM_BITFIELD (importstate) import_state :3; + + /* Set to 1 if the namespace uses "IMPLICIT NONE (export)". */ unsigned has_implicit_none_export:1; diff --git a/gcc/fortran/invoke.texi b/gcc/fortran/invoke.texi index da085d1..0b893e8 100644 --- a/gcc/fortran/invoke.texi +++ b/gcc/fortran/invoke.texi @@ -1170,6 +1170,10 @@ A @code{CHARACTER} variable is declared with negative length. With @option{-fopenmp}, for fixed-form source code, when an @code{omx} vendor-extension sentinel is encountered. (The equivalent @code{ompx}, used in free-form source code, is diagnosed by default.) + +@item +With @option{-fopenacc}, when using named constances with clauses that +take a variable as doing so has no effect. @end itemize @opindex Wtabs diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc index fe0a47a..f1acc00 100644 --- a/gcc/fortran/openmp.cc +++ b/gcc/fortran/openmp.cc @@ -8895,15 +8895,21 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses, if (list == OMP_LIST_MAP && n->sym->attr.flavor == FL_PARAMETER) { + /* OpenACC since 3.4 permits for Fortran named constants, but + permits removing then as optimization is not needed and such + ignore them. Likewise below for FIRSTPRIVATE. */ if (openacc) - gfc_error ("Object %qs is not a variable at %L; parameters" - " cannot be and need not be copied", n->sym->name, - &n->where); + gfc_warning (OPT_Wsurprising, "Clause for object %qs at %L is " + "ignored as parameters need not be copied", + n->sym->name, &n->where); else gfc_error ("Object %qs is not a variable at %L; parameters" " cannot be and need not be mapped", n->sym->name, &n->where); } + else if (openacc && n->sym->attr.flavor == FL_PARAMETER) + gfc_warning (OPT_Wsurprising, "Clause for object %qs at %L is ignored" + " as it is a parameter", n->sym->name, &n->where); else if (list != OMP_LIST_USES_ALLOCATORS) gfc_error ("Object %qs is not a variable at %L", n->sym->name, &n->where); @@ -12756,9 +12762,21 @@ gfc_resolve_oacc_declare (gfc_namespace *ns) && (n->sym->attr.flavor != FL_PROCEDURE || n->sym->result != n->sym)) { - gfc_error ("Object %qs is not a variable at %L", - n->sym->name, &oc->loc); - continue; + if (n->sym->attr.flavor != FL_PARAMETER) + { + gfc_error ("Object %qs is not a variable at %L", + n->sym->name, &oc->loc); + continue; + } + /* Note that OpenACC 3.4 permits name constants, but the + implementation is permitted to ignore the clause; + as semantically, device_resident kind of makes sense + (and the wording with it is a bit odd), the warning + is suppressed. */ + if (list != OMP_LIST_DEVICE_RESIDENT) + gfc_warning (OPT_Wsurprising, "Object %qs at %L is ignored as" + " parameters need not be copied", n->sym->name, + &oc->loc); } if (n->expr && n->expr->ref->type == REF_ARRAY) diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc index 8d4ca39..847ff37 100644 --- a/gcc/fortran/parse.cc +++ b/gcc/fortran/parse.cc @@ -6793,6 +6793,7 @@ gfc_fixup_sibling_symbols (gfc_symbol *sym, gfc_namespace *siblings) gfc_namespace *ns; gfc_symtree *st; gfc_symbol *old_sym; + bool imported; for (ns = siblings; ns; ns = ns->sibling) { @@ -6808,6 +6809,7 @@ gfc_fixup_sibling_symbols (gfc_symbol *sym, gfc_namespace *siblings) goto fixup_contained; old_sym = st->n.sym; + imported = old_sym->attr.imported == 1; if (old_sym->ns == ns && !old_sym->attr.contained @@ -6834,7 +6836,8 @@ gfc_fixup_sibling_symbols (gfc_symbol *sym, gfc_namespace *siblings) /* Replace it with the symbol from the parent namespace. */ st->n.sym = sym; sym->refs++; - + if (imported) + sym->attr.imported = 1; gfc_release_symbol (old_sym); } diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc index 4a6e951..93df5d0 100644 --- a/gcc/fortran/resolve.cc +++ b/gcc/fortran/resolve.cc @@ -3919,10 +3919,153 @@ found: } + +static bool +check_sym_import_status (gfc_symbol *sym, gfc_symtree *s, gfc_expr *e, + gfc_code *c, gfc_namespace *ns) +{ + locus *here; + + /* If the type has been imported then its vtype functions are OK. */ + if (e && e->expr_type == EXPR_FUNCTION && sym->attr.vtype) + return true; + + if (e) + here = &e->where; + else + here = &c->loc; + + if (s && !s->import_only) + s = gfc_find_symtree (ns->sym_root, sym->name); + + if (ns->import_state == IMPORT_ONLY + && sym->ns != ns + && (!s || !s->import_only)) + { + gfc_error ("F2018: C8102 %qs at %L is host associated but does not " + "appear in an IMPORT or IMPORT, ONLY list", sym->name, here); + return false; + } + else if (ns->import_state == IMPORT_NONE + && sym->ns != ns) + { + gfc_error ("F2018: C8102 %qs at %L is host associated in a scope that " + "has IMPORT, NONE", sym->name, here); + return false; + } + return true; +} + + +static bool +check_import_status (gfc_expr *e) +{ + gfc_symtree *st; + gfc_ref *ref; + gfc_symbol *sym, *der; + gfc_namespace *ns = gfc_current_ns; + + switch (e->expr_type) + { + case EXPR_VARIABLE: + case EXPR_FUNCTION: + case EXPR_SUBSTRING: + sym = e->symtree ? e->symtree->n.sym : NULL; + + /* Check the symbol itself. */ + if (sym + && !(ns->proc_name + && (sym == ns->proc_name)) + && !check_sym_import_status (sym, e->symtree, e, NULL, ns)) + return false; + + /* Check the declared derived type. */ + if (sym->ts.type == BT_DERIVED) + { + der = sym->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + else if (sym->ts.type == BT_CLASS && !UNLIMITED_POLY (sym)) + { + der = CLASS_DATA (sym) ? CLASS_DATA (sym)->ts.u.derived + : sym->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + + /* Check the declared derived types of component references. */ + for (ref = e->ref; ref; ref = ref->next) + if (ref->type == REF_COMPONENT) + { + gfc_component *c = ref->u.c.component; + if (c->ts.type == BT_DERIVED) + { + der = c->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + else if (c->ts.type == BT_CLASS && !UNLIMITED_POLY (c)) + { + der = CLASS_DATA (c) ? CLASS_DATA (c)->ts.u.derived + : c->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + } + + break; + + case EXPR_ARRAY: + case EXPR_STRUCTURE: + /* Check the declared derived type. */ + if (e->ts.type == BT_DERIVED) + { + der = e->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + else if (e->ts.type == BT_CLASS && !UNLIMITED_POLY (e)) + { + der = CLASS_DATA (e) ? CLASS_DATA (e)->ts.u.derived + : e->ts.u.derived; + st = gfc_find_symtree (ns->sym_root, der->name); + + if (!check_sym_import_status (der, st, e, NULL, ns)) + return false; + } + + break; + +/* Either not applicable or resolved away + case EXPR_OP: + case EXPR_UNKNOWN: + case EXPR_CONSTANT: + case EXPR_NULL: + case EXPR_COMPCALL: + case EXPR_PPC: */ + + default: + break; + } + + return true; +} + + /* Resolve a subroutine call. Although it was tempting to use the same code for functions, subroutines and functions are stored differently and this makes things awkward. */ + static bool resolve_call (gfc_code *c) { @@ -4080,6 +4223,11 @@ resolve_call (gfc_code *c) "Using subroutine %qs at %L is deprecated", c->resolved_sym->name, &c->loc); + csym = c->resolved_sym ? c->resolved_sym : csym; + if (t && gfc_current_ns->import_state != IMPORT_NOT_SET && !c->resolved_isym + && csym != gfc_current_ns->proc_name) + return check_sym_import_status (csym, c->symtree, NULL, c, gfc_current_ns); + return t; } @@ -7792,6 +7940,7 @@ fixup_unique_dummy (gfc_expr *e) e->symtree = st; } + /* Resolve an expression. That is, make sure that types of operands agree with their operators, intrinsic operators are converted to function calls for overloaded types and unresolved function references are resolved. */ @@ -7919,6 +8068,9 @@ gfc_resolve_expr (gfc_expr *e) && UNLIMITED_POLY (e->symtree->n.sym)) e->do_not_resolve_again = 1; + if (t && gfc_current_ns->import_state != IMPORT_NOT_SET) + t = check_import_status (e); + return t; } @@ -10572,6 +10724,7 @@ resolve_select_type (gfc_code *code, gfc_namespace *old_ns) int rank = 0, corank = 0; gfc_ref* ref = NULL; gfc_expr *selector_expr = NULL; + gfc_code *old_code = code; ns = code->ext.block.ns; if (code->expr2) @@ -10860,6 +11013,18 @@ resolve_select_type (gfc_code *code, gfc_namespace *old_ns) when this case is actually true, so build a new ASSOCIATE that does precisely this here (instead of using the 'global' one). */ + + /* First check the derived type import status. */ + if (gfc_current_ns->import_state != IMPORT_NOT_SET + && (c->ts.type == BT_DERIVED || c->ts.type == BT_CLASS)) + { + st = gfc_find_symtree (gfc_current_ns->sym_root, + c->ts.u.derived->name); + if (!check_sym_import_status (c->ts.u.derived, st, NULL, old_code, + gfc_current_ns)) + error++; + } + const char * var_name = gfc_var_name_for_select_type_temp (orig_expr1); if (c->ts.type == BT_CLASS) snprintf (name, sizeof (name), "__tmp_class_%s_%s", diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc index a2e70fc..f3d7cd4 100644 --- a/gcc/fortran/trans-openmp.cc +++ b/gcc/fortran/trans-openmp.cc @@ -2792,8 +2792,13 @@ gfc_trans_omp_variable_list (enum omp_clause_code code, gfc_omp_namelist *namelist, tree list, bool declare_simd) { + /* PARAMETER (named constants) are excluded as OpenACC 3.4 permits them now + as 'var' but permits compilers to ignore them. In expressions, it should + have been replaced by the value (and this function should not be called + anyway) and for var-using clauses, they should just be skipped. */ for (; namelist != NULL; namelist = namelist->next) - if (namelist->sym->attr.referenced || declare_simd) + if ((namelist->sym->attr.referenced || declare_simd) + && namelist->sym->attr.flavor != FL_PARAMETER) { tree t = gfc_trans_omp_variable (namelist->sym, declare_simd); if (t != error_mark_node) @@ -4029,7 +4034,8 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, case OMP_LIST_MAP: for (; n != NULL; n = n->next) { - if (!n->sym->attr.referenced) + if (!n->sym->attr.referenced + || n->sym->attr.flavor == FL_PARAMETER) continue; location_t map_loc = gfc_get_location (&n->where); @@ -4986,7 +4992,8 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses, case OMP_LIST_CACHE: for (; n != NULL; n = n->next) { - if (!n->sym->attr.referenced) + if (!n->sym->attr.referenced + && n->sym->attr.flavor != FL_PARAMETER) continue; switch (list) diff --git a/gcc/graphviz.h b/gcc/graphviz.h index 5943589..9a0fe6f 100644 --- a/gcc/graphviz.h +++ b/gcc/graphviz.h @@ -311,6 +311,16 @@ struct node_id : public ast_node m_port = std::make_unique<port> (*other.m_port); } + node_id &operator= (const node_id &other) + { + m_id = other.m_id; + if (other.m_port) + m_port = std::make_unique<port> (*other.m_port); + else + m_port = nullptr; + return *this; + } + void print (writer &w) const final override; id m_id; diff --git a/gcc/ipa-fnsummary.cc b/gcc/ipa-fnsummary.cc index 48343a7..924a54b 100644 --- a/gcc/ipa-fnsummary.cc +++ b/gcc/ipa-fnsummary.cc @@ -3421,6 +3421,21 @@ compute_fn_summary (struct cgraph_node *node, bool early) info->inlinable = tree_inlinable_function_p (node->decl); bool no_signature = false; + + /* Don't allow signature changes for functions which have + [[gnu::musttail]] or [[clang::musttail]] calls. Sometimes + (more often on targets which pass everything on the stack) + signature changes can result in tail calls being impossible + even when without the signature changes they would be ok. + See PR121023. */ + if (cfun->has_musttail) + { + if (dump_file) + fprintf (dump_file, "No signature change:" + " function has calls with musttail attribute.\n"); + no_signature = true; + } + /* Type attributes can use parameter indices to describe them. Special case fn spec since we can safely preserve them in modref summaries. */ diff --git a/gcc/json.cc b/gcc/json.cc index f3f3645..7153f08 100644 --- a/gcc/json.cc +++ b/gcc/json.cc @@ -289,6 +289,30 @@ object::print (pretty_printer *pp, bool formatted) const pp_character (pp, '}'); } +std::unique_ptr<value> +object::clone () const +{ + return clone_as_object (); +} + +std::unique_ptr<object> +object::clone_as_object () const +{ + auto result = std::make_unique<object> (); + + /* Iterate in the order that the keys were inserted. */ + unsigned i; + const char *key; + FOR_EACH_VEC_ELT (m_keys, i, key) + { + map_t &mut_map = const_cast<map_t &> (m_map); + value *value = *mut_map.get (key); + result->set (key, value->clone ()); + } + + return result; +} + /* Set the json::value * for KEY, taking ownership of V (and taking a copy of KEY if necessary). */ @@ -443,6 +467,17 @@ array::print (pretty_printer *pp, bool formatted) const pp_character (pp, ']'); } +std::unique_ptr<value> +array::clone () const +{ + auto result = std::make_unique<array> (); + unsigned i; + value *v; + FOR_EACH_VEC_ELT (m_elements, i, v) + result->append (v->clone ()); + return result; +} + /* Append non-NULL value V to a json::array, taking ownership of V. */ void @@ -473,6 +508,12 @@ float_number::print (pretty_printer *pp, pp_string (pp, tmp); } +std::unique_ptr<value> +float_number::clone () const +{ + return std::make_unique<float_number> (m_value); +} + /* class json::integer_number, a subclass of json::value, wrapping a long. */ /* Implementation of json::value::print for json::integer_number. */ @@ -486,6 +527,11 @@ integer_number::print (pretty_printer *pp, pp_string (pp, tmp); } +std::unique_ptr<value> +integer_number::clone () const +{ + return std::make_unique<integer_number> (m_value); +} /* class json::string, a subclass of json::value. */ @@ -501,9 +547,10 @@ string::string (const char *utf8) string::string (const char *utf8, size_t len) { gcc_assert (utf8); - m_utf8 = XNEWVEC (char, len); + m_utf8 = XNEWVEC (char, len + 1); m_len = len; memcpy (m_utf8, utf8, len); + m_utf8[len] = '\0'; } /* Implementation of json::value::print for json::string. */ @@ -515,6 +562,12 @@ string::print (pretty_printer *pp, print_escaped_json_string (pp, m_utf8, m_len); } +std::unique_ptr<value> +string::clone () const +{ + return std::make_unique<string> (m_utf8, m_len); +} + /* class json::literal, a subclass of json::value. */ /* Implementation of json::value::print for json::literal. */ @@ -539,6 +592,12 @@ literal::print (pretty_printer *pp, } } +std::unique_ptr<value> +literal::clone () const +{ + return std::make_unique<literal> (m_kind); +} + #if CHECKING_P @@ -914,6 +973,65 @@ test_comparisons () ASSERT_JSON_NE (arr_1, arr_2); } +/* Ensure that json::string's get_string is usable as a C-style string. */ + +static void +test_strcmp () +{ + string str ("foobar", 3); + ASSERT_EQ (strcmp (str.get_string (), "foo"), 0); +} + +static void +test_cloning () +{ + // Objects + { + object obj; + obj.set_string ("foo", "bar"); + + auto obj_clone = obj.clone (); + ASSERT_JSON_EQ (obj, *obj_clone); + } + + // Arrays + { + array arr; + arr.append (std::make_unique<string> ("foo")); + + auto arr_clone = arr.clone (); + ASSERT_JSON_EQ (arr, *arr_clone); + } + + // float_number + { + float_number f_one (1.0); + auto f_clone = f_one.clone (); + ASSERT_JSON_EQ (f_one, *f_clone); + } + + // integer_number + { + integer_number num (42); + auto num_clone = num.clone (); + ASSERT_JSON_EQ (num, *num_clone); + } + + // string + { + string str ("foo"); + auto str_clone = str.clone (); + ASSERT_JSON_EQ (str, *str_clone); + } + + // literal + { + literal lit (JSON_TRUE); + auto lit_clone = lit.clone (); + ASSERT_JSON_EQ (lit, *lit_clone); + } +} + /* Run all of the selftests within this file. */ void @@ -928,6 +1046,8 @@ json_cc_tests () test_writing_literals (); test_formatting (); test_comparisons (); + test_strcmp (); + test_cloning (); } } // namespace selftest @@ -124,11 +124,13 @@ class value virtual ~value () {} virtual enum kind get_kind () const = 0; virtual void print (pretty_printer *pp, bool formatted) const = 0; + virtual std::unique_ptr<value> clone () const = 0; void dump (FILE *, bool formatted) const; void DEBUG_FUNCTION dump () const; virtual object *dyn_cast_object () { return nullptr; } + virtual string *dyn_cast_string () { return nullptr; } static int compare (const json::value &val_a, const json::value &val_b); @@ -150,6 +152,7 @@ class object : public value enum kind get_kind () const final override { return JSON_OBJECT; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; object *dyn_cast_object () final override { return this; } @@ -182,6 +185,11 @@ class object : public value static int compare (const json::object &obj_a, const json::object &obj_b); + size_t get_num_keys () const { return m_keys.length (); } + const char *get_key (size_t i) const { return m_keys[i]; } + + std::unique_ptr<object> clone_as_object () const; + private: typedef hash_map <char *, value *, simple_hashmap_traits<nofree_string_hash, value *> > map_t; @@ -200,6 +208,7 @@ class array : public value enum kind get_kind () const final override { return JSON_ARRAY; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; void append (value *v); void append_string (const char *utf8_value); @@ -241,6 +250,7 @@ class float_number : public value enum kind get_kind () const final override { return JSON_FLOAT; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; double get () const { return m_value; } @@ -257,6 +267,7 @@ class integer_number : public value enum kind get_kind () const final override { return JSON_INTEGER; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; long get () const { return m_value; } @@ -276,6 +287,8 @@ class string : public value enum kind get_kind () const final override { return JSON_STRING; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; + string *dyn_cast_string () final override { return this; } const char *get_string () const { return m_utf8; } size_t get_length () const { return m_len; } @@ -298,6 +311,7 @@ class literal : public value enum kind get_kind () const final override { return m_kind; } void print (pretty_printer *pp, bool formatted) const final override; + std::unique_ptr<value> clone () const final override; private: enum kind m_kind; diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h index 93b8f90..4beee44 100644 --- a/gcc/libgdiagnostics++.h +++ b/gcc/libgdiagnostics++.h @@ -34,6 +34,9 @@ class execution_path; class group; class manager; class diagnostic; +class graph; +class node; +class edge; /* Wrapper around a borrowed diagnostic_text_sink *. */ @@ -246,6 +249,9 @@ public: take_execution_path (execution_path path); void + take_graph (graph g); + + void finish (const char *fmt, ...) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3); @@ -395,10 +401,99 @@ public: void set_analysis_target (file f); + void + take_global_graph (graph g); + diagnostic_manager *m_inner; bool m_owned; }; +class graph +{ +public: + graph () : m_inner (nullptr), m_owned (false) {} + + graph (diagnostic_graph *graph) + : m_inner (graph), m_owned (true) + {} + + graph (const diagnostic_graph *graph) + : m_inner (const_cast<diagnostic_graph *> (graph)), + m_owned (false) + {} + + graph (const graph &other) = delete; + graph &operator= (const graph &other) = delete; + + graph (graph &&other) + : m_inner (other.m_inner), + m_owned (other.m_owned) + { + other.m_inner = nullptr; + other.m_owned = false; + } + + graph &operator= (graph &&other) + { + m_inner = other.m_inner; + m_owned = other.m_owned; + other.m_inner = nullptr; + other.m_owned = false; + return *this; + } + + ~graph () + { + if (m_owned) + diagnostic_graph_release (m_inner); + } + + void + set_description (const char *); + + node + get_node_by_id (const char *id) const; + + edge + get_edge_by_id (const char *id) const; + + edge + add_edge (const char *id, node src_node, node dst_node, const char *label); + + diagnostic_graph *m_inner; + bool m_owned; +}; + +// Borrowed pointer to a diagnostic_node. + +class node +{ +public: + node () : m_inner (nullptr) {} + node (diagnostic_node *node_) : m_inner (node_) {} + + void + set_label (const char *); + + void + set_location (physical_location loc); + + void + set_logical_location (logical_location loc); + + diagnostic_node *m_inner; +}; + +// Borrowed edge to a diagnostic_edge. + +class edge +{ +public: + edge (diagnostic_edge *edge_) : m_inner (edge_) {} + + diagnostic_edge *m_inner; +}; + // Implementation // class file @@ -593,6 +688,14 @@ diagnostic::take_execution_path (execution_path path) } inline void +diagnostic::take_graph (graph g) +{ + diagnostic_take_graph (m_inner, + g.m_inner); + g.m_owned = false; +} + +inline void diagnostic::finish (const char *fmt, ...) { va_list ap; @@ -702,6 +805,66 @@ manager::set_analysis_target (file f) diagnostic_manager_set_analysis_target (m_inner, f.m_inner); } +inline void +manager::take_global_graph (graph g) +{ + diagnostic_manager_take_global_graph (m_inner, + g.m_inner); + g.m_owned = false; +} + +// class graph + +inline void +graph::set_description (const char *desc) +{ + diagnostic_graph_set_description (m_inner, desc); +} + +inline node +graph::get_node_by_id (const char *id) const +{ + return node (diagnostic_graph_get_node_by_id (m_inner, id)); +} + +inline edge +graph::get_edge_by_id (const char *id) const +{ + return edge (diagnostic_graph_get_edge_by_id (m_inner, id)); +} + +inline edge +graph::add_edge (const char *id, + node src_node, node dst_node, + const char *label) +{ + return edge (diagnostic_graph_add_edge (m_inner, + id, + src_node.m_inner, + dst_node.m_inner, + label)); +} + +// class node + +inline void +node::set_label (const char *label) +{ + diagnostic_node_set_label (m_inner, label); +} + +inline void +node::set_location (physical_location loc) +{ + diagnostic_node_set_location (m_inner, loc.m_inner); +} + +inline void +node::set_logical_location (logical_location loc) +{ + diagnostic_node_set_logical_location (m_inner, loc.m_inner); +} + } // namespace libgdiagnostics #endif // #ifndef LIBGDIAGNOSTICSPP_H diff --git a/gcc/libgdiagnostics-private.h b/gcc/libgdiagnostics-private.h new file mode 100644 index 0000000..0f628e4 --- /dev/null +++ b/gcc/libgdiagnostics-private.h @@ -0,0 +1,62 @@ +/* Private API entrypoints to libgdiagnostics purely for use by sarif-replay. + Copyright (C) 2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef LIBGDIAGNOSTICS_PRIVATE_H +#define LIBGDIAGNOSTICS_PRIVATE_H + +#include "libgdiagnostics.h" + +namespace json { class object; } + +extern "C" { + +/* Private entrypoints, for use only by sarif-replay. + These are subject to removal without notice. */ + +/* Entrypoints added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_event_id +private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + diagnostic_graph *state_graph, + const char *fmt, ...) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (6) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (6, 7); + +extern void +private_diagnostic_graph_set_property_bag (diagnostic_graph &graph, + std::unique_ptr<json::object> properties); + +extern void +private_diagnostic_node_set_property_bag (diagnostic_node &node, + std::unique_ptr<json::object> properties); + +extern void +private_diagnostic_edge_set_property_bag (diagnostic_edge &edge, + std::unique_ptr<json::object> properties); + +} // extern "C" + +#endif /* LIBGDIAGNOSTICS_PRIVATE_H */ diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index 74814c7..04812dc 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_MAP +#define INCLUDE_STRING #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" @@ -32,9 +33,12 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-format-sarif.h" #include "diagnostic-format-text.h" #include "diagnostic-output-spec.h" +#include "diagnostic-digraphs.h" +#include "diagnostic-state-graphs.h" #include "logical-location.h" #include "edit-context.h" #include "libgdiagnostics.h" +#include "libgdiagnostics-private.h" class owned_nullable_string { @@ -644,6 +648,9 @@ public: return m_prev_diag_logical_loc; } + void + take_global_graph (std::unique_ptr<diagnostic_graph> graph); + private: void ensure_linemap_for_file_and_line (const diagnostic_file *file, @@ -743,17 +750,53 @@ private: owned_nullable_string m_url; }; +struct diagnostic_graph : public diagnostics::digraphs::digraph +{ + diagnostic_graph (diagnostic_manager &) {} + + diagnostic_node * + add_node_with_id (std::string id, + diagnostic_node *parent_node); + diagnostic_edge * + add_edge_with_label (const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node, + const char *label); +}; + +struct diagnostic_node : public diagnostics::digraphs::node +{ + diagnostic_node (diagnostic_graph &g, + std::string id) + : node (g, std::move (id)) + { + } +}; + +struct diagnostic_edge : public diagnostics::digraphs::edge +{ + diagnostic_edge (diagnostic_graph &g, + const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node) + : edge (g, id, src_node, dst_node) + { + } +}; + class libgdiagnostics_path_event : public diagnostic_event { public: libgdiagnostics_path_event (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, + std::unique_ptr<diagnostic_graph> state_graph, const char *gmsgid, va_list *args) : m_physical_loc (physical_loc), m_logical_loc (logical_loc), - m_stack_depth (stack_depth) + m_stack_depth (stack_depth), + m_state_graph (std::move (state_graph)) { m_desc_uncolored = make_desc (gmsgid, args, false); m_desc_colored = make_desc (gmsgid, args, true); @@ -799,6 +842,15 @@ public: return 0; } + std::unique_ptr<diagnostics::digraphs::digraph> + maybe_make_diagnostic_state_graph (bool) const final override + { + if (!m_state_graph) + return nullptr; + + return m_state_graph->clone (); + } + private: static label_text make_desc (const char *gmsgid, va_list *args, @@ -824,6 +876,7 @@ private: const diagnostic_physical_location *m_physical_loc; const diagnostic_logical_location *m_logical_loc; unsigned m_stack_depth; + std::unique_ptr<diagnostic_graph> m_state_graph; label_text m_desc_uncolored; label_text m_desc_colored; }; @@ -855,6 +908,7 @@ struct diagnostic_execution_path : public diagnostic_path add_event_va (const diagnostic_physical_location *physical_loc, const diagnostic_logical_location *logical_loc, unsigned stack_depth, + std::unique_ptr<diagnostic_graph> state_graph, const char *gmsgid, va_list *args) { @@ -862,6 +916,7 @@ struct diagnostic_execution_path : public diagnostic_path (std::make_unique<libgdiagnostics_path_event> (physical_loc, logical_loc, stack_depth, + std::move (state_graph), gmsgid, args)); return m_events.size () - 1; @@ -902,6 +957,27 @@ private: std::vector<std::unique_ptr<libgdiagnostics_path_event>> m_events; }; +class prebuilt_digraphs : public diagnostics::digraphs::lazy_digraphs +{ +public: + using digraph = diagnostics::digraphs::digraph; + + std::unique_ptr<std::vector<std::unique_ptr<digraph>>> + create_digraphs () const final override + { + return std::make_unique<std::vector<std::unique_ptr<digraph>>> (std::move (m_digraphs)); + } + + void + take_graph (std::unique_ptr<diagnostic_graph> graph) + { + m_digraphs.push_back (std::move (graph)); + } + +private: + mutable std::vector<std::unique_ptr<digraph>> m_digraphs; +}; + /* This has to be a "struct" as it is exposed in the C API. */ struct diagnostic @@ -914,7 +990,9 @@ public: m_rich_loc (diag_mgr.get_line_table ()), m_logical_loc (nullptr), m_path (nullptr) - {} + { + m_metadata.set_lazy_digraphs (&m_graphs); + } diagnostic_manager &get_manager () const { @@ -990,12 +1068,25 @@ public: m_rich_loc.set_path (path); } + void + take_graph (std::unique_ptr<diagnostic_graph> graph) + { + m_graphs.take_graph (std::move (graph)); + } + + const prebuilt_digraphs & + get_graphs () const + { + return m_graphs; + } + private: diagnostic_manager &m_diag_mgr; enum diagnostic_level m_level; impl_rich_location m_rich_loc; const diagnostic_logical_location *m_logical_loc; diagnostic_metadata m_metadata; + prebuilt_digraphs m_graphs; std::vector<std::unique_ptr<range_label>> m_labels; std::vector<std::unique_ptr<impl_rule>> m_rules; std::unique_ptr<diagnostic_execution_path> m_path; @@ -1248,6 +1339,29 @@ diagnostic_manager::new_execution_path () return new diagnostic_execution_path (*mgr); } +void +diagnostic_manager::take_global_graph (std::unique_ptr<diagnostic_graph> graph) +{ + class prebuilt_lazy_digraph : public diagnostics::digraphs::lazy_digraph + { + public: + prebuilt_lazy_digraph (std::unique_ptr<diagnostic_graph> graph) + : m_graph (std::move (graph)) + { + } + + std::unique_ptr<diagnostics::digraphs::digraph> + create_digraph () const final override + { + return std::move (m_graph); + } + + private: + mutable std::unique_ptr<diagnostic_graph> m_graph; + }; + + m_dc.report_global_digraph (prebuilt_lazy_digraph (std::move (graph))); +} /* Error-checking at the API boundary. */ #define FAIL_IF_NULL(PTR_ARG) \ @@ -1876,6 +1990,7 @@ diagnostic_execution_path_add_event (diagnostic_execution_path *path, diagnostic_event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, + nullptr, gmsgid, &args); va_end (args); @@ -1898,6 +2013,7 @@ diagnostic_execution_path_add_event_va (diagnostic_execution_path *path, diagnostic_event_id_t result = path->add_event_va (physical_loc, logical_loc, stack_depth, + nullptr, gmsgid, args); return as_diagnostic_event_id (result); } @@ -2048,3 +2164,223 @@ diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, mgr->get_dc ().set_main_input_filename (file->get_name ()); } + +// struct diagnostic_graph : public diagnostics::digraphs::graph<foo_traits> + +diagnostic_node * +diagnostic_graph::add_node_with_id (std::string id, + diagnostic_node *parent_node) +{ + auto node_up = std::make_unique<diagnostic_node> (*this, std::move (id)); + diagnostic_node *new_node = node_up.get (); + if (parent_node) + parent_node->add_child (std::move (node_up)); + else + add_node (std::move (node_up)); + return new_node; +} + +diagnostic_edge * +diagnostic_graph::add_edge_with_label (const char *id, + diagnostic_node &src_node, + diagnostic_node &dst_node, + const char *label) +{ + auto edge_up + = std::make_unique<diagnostic_edge> (*this, id, + src_node, dst_node); + diagnostic_edge *new_edge = edge_up.get (); + if (label) + new_edge->set_label (label); + add_edge (std::move (edge_up)); + return new_edge; +} + +/* Public entrypoint. */ + +diagnostic_graph * +diagnostic_manager_new_graph (diagnostic_manager *manager) +{ + FAIL_IF_NULL (manager); + + return new diagnostic_graph (*manager); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_take_global_graph (diagnostic_manager *manager, + diagnostic_graph *graph) +{ + FAIL_IF_NULL (manager); + FAIL_IF_NULL (graph); + + manager->take_global_graph (std::unique_ptr<diagnostic_graph> (graph)); +} + +void +diagnostic_take_graph (diagnostic *diag, + diagnostic_graph *graph) +{ + FAIL_IF_NULL (diag); + FAIL_IF_NULL (graph); + + diag->take_graph (std::unique_ptr<diagnostic_graph> (graph)); +} + +/* Public entrypoint. */ + +void +diagnostic_graph_release (diagnostic_graph *graph) +{ + delete graph; +} + +/* Public entrypoint. */ + +void +diagnostic_graph_set_description (diagnostic_graph *graph, + const char *desc) +{ + FAIL_IF_NULL (graph); + + graph->set_description (desc); +} + +diagnostic_node * +diagnostic_graph_add_node (diagnostic_graph *graph, + const char *id, + diagnostic_node *parent_node) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return graph->add_node_with_id (id, parent_node); +} + +/* Public entrypoint. */ + +diagnostic_edge * +diagnostic_graph_add_edge (diagnostic_graph *graph, + const char *id, + diagnostic_node *src_node, + diagnostic_node *dst_node, + const char *label) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (src_node); + FAIL_IF_NULL (dst_node); + + return graph->add_edge_with_label (id, *src_node, *dst_node, label); +} + +/* Public entrypoint. */ + +diagnostic_node * +diagnostic_graph_get_node_by_id (diagnostic_graph *graph, + const char *id) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return static_cast<diagnostic_node *> (graph->get_node_by_id (id)); +} + +/* Public entrypoint. */ + +diagnostic_edge * +diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, + const char *id) +{ + FAIL_IF_NULL (graph); + FAIL_IF_NULL (id); + + return static_cast<diagnostic_edge *> (graph->get_edge_by_id (id)); +} + +/* Public entrypoint. */ + +void +diagnostic_node_set_location (diagnostic_node *node, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (node); + + node->set_physical_loc (loc ? loc->m_inner : UNKNOWN_LOCATION); +} + +/* Public entrypoint. */ + +void +diagnostic_node_set_label (diagnostic_node *node, + const char *label) +{ + FAIL_IF_NULL (node); + + node->set_label (label); +} + +void +diagnostic_node_set_logical_location (diagnostic_node *node, + const diagnostic_logical_location *logical_loc) +{ + FAIL_IF_NULL (node); + + node->set_logical_loc + (impl_logical_location_manager::key_from_ptr (logical_loc)); +} + +/* Private entrypoint. */ + +diagnostic_event_id +private_diagnostic_execution_path_add_event_2 (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + diagnostic_graph *state_graph, + const char *gmsgid, ...) + +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + va_list args; + va_start (args, gmsgid); + diagnostic_event_id_t result + = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + std::unique_ptr <diagnostic_graph> (state_graph), + gmsgid, &args); + va_end (args); + + return as_diagnostic_event_id (result); + +} + +/* Private entrypoint. */ + +void +private_diagnostic_graph_set_property_bag (diagnostic_graph &graph, + std::unique_ptr<json::object> properties) +{ + graph.set_property_bag (std::move (properties)); +} + +/* Private entrypoint. */ + +void +private_diagnostic_node_set_property_bag (diagnostic_node &node, + std::unique_ptr<json::object> properties) +{ + node.set_property_bag (std::move (properties)); +} + +/* Private entrypoint. */ + +void +private_diagnostic_edge_set_property_bag (diagnostic_edge &edge, + std::unique_ptr<json::object> properties) +{ + edge.set_property_bag (std::move (properties)); +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h index 9af2747..f79790a 100644 --- a/gcc/libgdiagnostics.h +++ b/gcc/libgdiagnostics.h @@ -765,6 +765,140 @@ diagnostic_manager_set_analysis_target (diagnostic_manager *mgr, LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); +/* Directed graphs. */ + +typedef struct diagnostic_graph diagnostic_graph; +typedef struct diagnostic_node diagnostic_node; +typedef struct diagnostic_edge diagnostic_edge; + +/* Create a new graph. + This is owned by the caller and must have one of + diagnostic_manager_take_global_graph, diagnostic_take_graph, + or diagnostic_graph_release called on it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_graph * +diagnostic_manager_new_graph (diagnostic_manager *manager) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Report this graph "globally", taking ownership of it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_manager_take_global_graph (diagnostic_manager *manager, + diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Add this graph to DIAG, transferring ownership to it. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_take_graph (diagnostic *diag, + diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Release this graph. Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_graph_release (diagnostic_graph *graph) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (1); + +/* Set the description of GRAPH for use + in the value of the SARIF "description" property + (SARIF v2.1.0 section 3.39.2). + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_graph_set_description (diagnostic_graph *graph, + const char *description) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Create and add a new node within GRAPH. + NODE_ID must be unique within nodes in GRAPH. + The new node is owned by GRAPH. + PARENT_NODE can be NULL (for a top-level node in the graph), + or non-null for a child node. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_node * +diagnostic_graph_add_node (diagnostic_graph *graph, + const char *node_id, + diagnostic_node *parent_node) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (3); + +/* Create and add a new edge within GRAPH. + + If non-null, then EDGE_ID must be unique within edges in GRAPH; + if EDGE_ID is null then a unique id of the form "edge0", "edge1", etc + will be used automatically. + + The new edge is owned by GRAPH. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_edge * +diagnostic_graph_add_edge (diagnostic_graph *graph, + const char *edge_id, + diagnostic_node *src_node, + diagnostic_node *dst_node, + const char *label) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (4) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5); + +/* Get the node in GRAPH with the given id, or null. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_node * +diagnostic_graph_get_node_by_id (diagnostic_graph *graph, + const char *node_id) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Get the edge in GRAPH with the given id, or null. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern diagnostic_edge * +diagnostic_graph_get_edge_by_id (diagnostic_graph *graph, + const char *edge_id) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Set the label of NODE for use + in the value of the SARIF "label" property + (SARIF v2.1.0 section 3.40.3). + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_label (diagnostic_node *node, + const char *label) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Set the physical location of NODE. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_location (diagnostic_node *node, + const diagnostic_physical_location *loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Set the logical location of NODE. + Added in LIBGDIAGNOSTICS_ABI_3. */ + +extern void +diagnostic_node_set_logical_location (diagnostic_node *node, + const diagnostic_logical_location *logical_loc) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + /* DEFERRED: - thread-safety - plural forms diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map index 49cabca..cae28d1 100644 --- a/gcc/libgdiagnostics.map +++ b/gcc/libgdiagnostics.map @@ -90,3 +90,26 @@ LIBGDIAGNOSTICS_ABI_2 { diagnostic_manager_add_sink_from_spec; diagnostic_manager_set_analysis_target; } LIBGDIAGNOSTICS_ABI_1; + +# Add hooks needed for diagnostic_graph support. +LIBGDIAGNOSTICS_ABI_3 { + global: + diagnostic_manager_new_graph; + diagnostic_manager_take_global_graph; + diagnostic_take_graph; + diagnostic_graph_release; + diagnostic_graph_set_description; + diagnostic_graph_add_node; + diagnostic_graph_add_edge; + diagnostic_graph_get_node_by_id; + diagnostic_graph_get_edge_by_id; + diagnostic_node_set_label; + diagnostic_node_set_location; + diagnostic_node_set_logical_location; + + # Private hooks used by sarif-replay + private_diagnostic_execution_path_add_event_2; + private_diagnostic_graph_set_property_bag; + private_diagnostic_node_set_property_bag; + private_diagnostic_edge_set_property_bag; +} LIBGDIAGNOSTICS_ABI_2; diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index 2d6c394..cad535b 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "libgdiagnostics++.h" +#include "libgdiagnostics-private.h" #include "json-parsing.h" #include "intl.h" #include "sarif-spec-urls.def" @@ -291,6 +292,8 @@ public: label_text m_label; }; +using id_map = std::map<std::string, const json::string *>; + class sarif_replayer { public: @@ -413,6 +416,30 @@ private: const json::object &run_obj, libgdiagnostics::execution_path &out); + // "graph" object (§3.39) + enum status + handle_graph_object (const json::object &graph_obj, + const json::object &run_obj, + libgdiagnostics::graph &out); + // "node" object (§3.40) + libgdiagnostics::node + handle_node_object (const json::object &node_obj, + const json::object &run_obj, + libgdiagnostics::graph &graph, + libgdiagnostics::node parent_node, + id_map &node_id_map); + + // "edge" object (§3.41) + libgdiagnostics::edge + handle_edge_object (const json::object &edge_obj, + libgdiagnostics::graph &graph, + id_map &edge_id_map); + + libgdiagnostics::node + get_graph_node_by_id_property (const json::object &edge_json_object, + const property_spec_ref &id_prop, + libgdiagnostics::graph &graph); + // reportingDescriptor lookup (§3.52.3) const json::object * lookup_rule_by_id_in_tool (const char *rule_id, @@ -446,7 +473,7 @@ private: { va_list ap; va_start (ap, gmsgid); - report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR); + report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR); va_end (ap); return status::err_invalid_sarif; } @@ -462,14 +489,25 @@ private: { va_list ap; va_start (ap, gmsgid); - report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY); + report_problem (jv, &ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY); va_end (ap); return status::err_unhandled_sarif; } void + report_note (const json::value &jv, + const char *gmsgid, ...) + LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (3, 4) + { + va_list ap; + va_start (ap, gmsgid); + report_problem (jv, nullptr, gmsgid, &ap, DIAGNOSTIC_LEVEL_NOTE); + va_end (ap); + } + + void report_problem (const json::value &jv, - const spec_ref &ref, + const spec_ref *ref, const char *gmsgid, va_list *args, enum diagnostic_level level) @@ -481,11 +519,14 @@ private: There doesn't seem to be a systematic mapping from spec sections to HTML anchors, so we can't provide URLs (filed as https://github.com/oasis-tcs/sarif-spec/issues/533 ). */ - char *ref_desc = ref.make_description (); - char *ref_url = ref.make_url (); - diag.add_rule (ref_desc, ref_url); - free (ref_desc); - free (ref_url); + if (ref) + { + char *ref_desc = ref->make_description (); + char *ref_url = ref->make_url (); + diag.add_rule (ref_desc, ref_url); + free (ref_desc); + free (ref_url); + } auto loc_range = make_physical_location (m_control_mgr, @@ -651,6 +692,38 @@ private: const string_property_value<ValueType> *value_arr, size_t num_values); + const json::object * + maybe_get_property_bag (const json::object &obj) + { + const property_spec_ref properties + ("object", "properties", "3.8.1"); + return get_optional_property<json::object> (obj, properties); + } + + /* Look for a property bag within OBJ. + If found, look for a property within it named PROPERTY_NAME + of the given type. + If successful, return the property's value. + Otherwise, return nullptr without complaining (unless the property bag + is itself not an object). */ + template <typename JsonType> + const JsonType * + maybe_get_property_bag_value (const json::object &obj, + const char *property_name) + { + auto property_bag_obj = maybe_get_property_bag (obj); + if (!property_bag_obj) + return nullptr; + const json::value *property_val = property_bag_obj->get (property_name); + if (!property_val) + return nullptr; + const JsonType *sub = dyn_cast<const JsonType *> (property_val); + if (!sub) + /* Property is wrong kind of value. Don't treat this as an error. */ + return nullptr; + return sub; + } + /* The manager to replay the SARIF files to. */ libgdiagnostics::manager m_output_mgr; @@ -929,6 +1002,31 @@ sarif_replayer::handle_run_obj (const json::object &run_obj) break; } + // §3.14.20 "graphs" + const property_spec_ref prop_graphs ("run", "graphs", "3.14.20"); + if (const json::array *graphs + = get_optional_property<json::array> (run_obj, + prop_graphs)) + { + for (auto element : *graphs) + { + if (const json::object *graph_json_obj + = require_object_for_element (*element, prop_graphs)) + { + libgdiagnostics::graph graph; + enum status s = handle_graph_object (*graph_json_obj, + run_obj, + graph); + if (s != status::ok) + return s; + + m_output_mgr.take_global_graph (std::move (graph)); + } + else + return status::err_invalid_sarif; + } + } + return status::ok; } @@ -1244,6 +1342,31 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, if (path.m_inner) err.take_execution_path (std::move (path)); + // §3.27.19 "graphs" property + const property_spec_ref prop_graphs ("result", "graphs", "3.27.19"); + if (const json::array *graphs + = get_optional_property<json::array> (result_obj, + prop_graphs)) + { + for (auto element : *graphs) + { + if (const json::object *graph_json_obj + = require_object_for_element (*element, prop_graphs)) + { + libgdiagnostics::graph graph; + enum status s = handle_graph_object (*graph_json_obj, + run_obj, + graph); + if (s != status::ok) + return s; + + err.take_graph (std::move (graph)); + } + else + return status::err_invalid_sarif; + } + } + // §3.27.22 relatedLocations property std::vector<std::pair<libgdiagnostics::diagnostic, label_text>> notes; const property_spec_ref prop_related_locations @@ -1740,16 +1863,32 @@ handle_thread_flow_location_object (const json::object &tflow_loc_obj, } } + libgdiagnostics::graph state_graph; + if (auto sarif_state_graph + = maybe_get_property_bag_value<json::object> (tflow_loc_obj, + "gcc/diagnostic_event/state_graph")) + { + enum status s + = handle_graph_object (*sarif_state_graph, run_obj, state_graph); + if (s != status::ok) + return s; + } + if (message.get ()) - path.add_event (physical_loc, - logical_loc, - stack_depth, - "%s", message.get ()); + private_diagnostic_execution_path_add_event_2 (path.m_inner, + physical_loc.m_inner, + logical_loc.m_inner, + stack_depth, + state_graph.m_inner, + "%s", message.get ()); else - path.add_event (physical_loc, - logical_loc, - stack_depth, - ""); + private_diagnostic_execution_path_add_event_2 (path.m_inner, + physical_loc.m_inner, + logical_loc.m_inner, + stack_depth, + state_graph.m_inner, + ""); + state_graph.m_owned = false; return status::ok; } @@ -2142,6 +2281,270 @@ handle_logical_location_object (const json::object &logical_loc_obj, return status::ok; } +// "graph" object (§3.39) + +enum status +sarif_replayer::handle_graph_object (const json::object &graph_json_obj, + const json::object &run_obj, + libgdiagnostics::graph &out_graph) +{ + out_graph = libgdiagnostics::graph + (diagnostic_manager_new_graph (m_output_mgr.m_inner)); + + id_map node_id_map; + id_map edge_id_map; + + if (auto properties = maybe_get_property_bag (graph_json_obj)) + private_diagnostic_graph_set_property_bag (*out_graph.m_inner, + properties->clone_as_object ()); + + // §3.39.2: MAY contain a "description" property + const property_spec_ref description_prop + ("graph", "description", "3.39.2"); + if (auto description_obj + = get_optional_property<json::object> (graph_json_obj, description_prop)) + { + label_text text + = make_plain_text_within_result_message (&run_obj, + *description_obj, + nullptr); + if (!text.get ()) + return status::err_invalid_sarif; + out_graph.set_description (text.get ()); + } + + // §3.39.3: MAY contain a "nodes" property + const property_spec_ref nodes_prop + ("graph", "nodes", "3.39.3"); + if (auto nodes_arr + = get_optional_property<json::array> (graph_json_obj, nodes_prop)) + { + for (auto element : *nodes_arr) + { + const json::object *node_json_obj + = require_object_for_element (*element, nodes_prop); + if (!node_json_obj) + return status::err_invalid_sarif; + libgdiagnostics::node node + = handle_node_object (*node_json_obj, run_obj, out_graph, + nullptr, node_id_map); + if (node.m_inner == nullptr) + return status::err_invalid_sarif; + } + } + else + // If we have no nodes, we can't handle the edges. + return status::ok; + + // §3.39.4 edges property + const property_spec_ref edges_prop + ("graph", "edges", "3.39.4"); + if (auto edges_arr + = get_optional_property<json::array> (graph_json_obj, edges_prop)) + { + for (auto element : *edges_arr) + { + const json::object *edge_json_obj + = require_object_for_element (*element, edges_prop); + if (!edge_json_obj) + return status::err_invalid_sarif; + libgdiagnostics::edge edge + = handle_edge_object (*edge_json_obj, out_graph, edge_id_map); + if (edge.m_inner == nullptr) + return status::err_invalid_sarif; + } + } + + return status::ok; +} + +// "node" object (§3.40) + +libgdiagnostics::node +sarif_replayer::handle_node_object (const json::object &node_json_obj, + const json::object &run_obj, + libgdiagnostics::graph &graph, + libgdiagnostics::node parent_node, + id_map &node_id_map) +{ + // §3.40.2 "id" property + const property_spec_ref id_prop ("node", "id", "3.40.2"); + auto id_str = get_required_property<json::string> (node_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + if (diagnostic_graph_get_node_by_id (graph.m_inner, id)) + { + // Duplicate id; fail: + libgdiagnostics::group g (m_control_mgr); + report_invalid_sarif (*id_str, + id_prop, + "duplicate node id %qs within graph", + id); + gcc_assert (node_id_map[id]); + report_note (*node_id_map[id], + "%qs already used as node id within graph here", + id); + return nullptr; + } + node_id_map[id] = id_str; + + libgdiagnostics::node new_node + = libgdiagnostics::node (diagnostic_graph_add_node (graph.m_inner, + id, + parent_node.m_inner)); + if (auto properties = maybe_get_property_bag (node_json_obj)) + private_diagnostic_node_set_property_bag (*new_node.m_inner, + properties->clone_as_object ()); + + // §3.40.3 "label" property + const property_spec_ref label_prop + ("node", "label", "3.40.3"); + if (auto label_obj + = get_optional_property<json::object> (node_json_obj, label_prop)) + { + label_text text + = make_plain_text_within_result_message (&run_obj, + *label_obj, + nullptr); + if (!text.get ()) + return nullptr; + new_node.set_label (text.get ()); + } + + // §3.40.4 "location" property + const property_spec_ref location_prop ("node", "location", "3.40.4"); + if (auto location_json_obj + = get_optional_property<json::object> (node_json_obj, location_prop)) + { + libgdiagnostics::physical_location physical_loc; + libgdiagnostics::logical_location logical_loc; + enum status s = handle_location_object (*location_json_obj, + run_obj, + physical_loc, + logical_loc, + nullptr); // annotations + if (s != status::ok) + return nullptr; + + new_node.set_location (physical_loc); + new_node.set_logical_location (logical_loc); + } + + // §3.40.5: MAY contain a "children" property + const property_spec_ref children_prop + ("graph", "children", "3.40.5"); + if (auto children_json_arr + = get_optional_property<json::array> (node_json_obj, children_prop)) + { + for (auto json_element : *children_json_arr) + { + const json::object *child_json_obj + = require_object_for_element (*json_element, children_prop); + if (!child_json_obj) + return nullptr; + libgdiagnostics::node child_node + = handle_node_object (*child_json_obj, run_obj, graph, + new_node, node_id_map); + if (child_node.m_inner == nullptr) + return nullptr; + } + } + + return new_node; +} + +// "edge" object (§3.41) + +libgdiagnostics::edge +sarif_replayer::handle_edge_object (const json::object &edge_json_obj, + libgdiagnostics::graph &graph, + id_map &edge_id_map) +{ + // §3.41.2 "id" property + const property_spec_ref id_prop ("edge", "id", "3.41.2"); + auto id_str = get_required_property<json::string> (edge_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + if (diagnostic_graph_get_edge_by_id (graph.m_inner, id)) + { + // Duplicate id; fail: + libgdiagnostics::group g (m_control_mgr); + report_invalid_sarif (*id_str, + id_prop, + "duplicate edge id %qs within graph", + id); + gcc_assert (edge_id_map[id]); + report_note (*edge_id_map[id], + "%qs already used as edge id within graph here", + id); + return nullptr; + } + edge_id_map[id] = id_str; + + // §3.41.3 "label" property + label_text label; + const property_spec_ref label_prop + ("edge", "label", "3.41.3"); + if (auto label_obj + = get_optional_property<json::object> (edge_json_obj, label_prop)) + { + label = make_plain_text_within_result_message (nullptr, + *label_obj, + nullptr); + if (!label.get ()) + return nullptr; + } + + // §3.41.4 "sourceNodeId" property + const property_spec_ref src_id_prop ("edge", "sourceNodeId", "3.41.4"); + auto src_node = get_graph_node_by_id_property (edge_json_obj, + src_id_prop, + graph); + if (!src_node.m_inner) + return nullptr; + + // §3.41.5 "targetNodeId" property + const property_spec_ref dst_id_prop ("edge", "targetNodeId", "3.41.5"); + auto dst_node = get_graph_node_by_id_property (edge_json_obj, + dst_id_prop, + graph); + if (!dst_node.m_inner) + return nullptr; + + auto result = graph.add_edge (id, src_node, dst_node, label.get ()); + + if (auto properties = maybe_get_property_bag (edge_json_obj)) + private_diagnostic_edge_set_property_bag (*result.m_inner, + properties->clone_as_object ()); + + return result; +} + +libgdiagnostics::node +sarif_replayer:: +get_graph_node_by_id_property (const json::object &edge_json_obj, + const property_spec_ref &id_prop, + libgdiagnostics::graph &graph) +{ + auto id_str = get_required_property<json::string> (edge_json_obj, id_prop); + if (!id_str) + return nullptr; + const char *id = id_str->get_string (); + auto node = graph.get_node_by_id (id); + if (!node.m_inner) + { + // id not found; complain: + report_invalid_sarif (*id_str, + id_prop, + "no node with id %qs in graph", + id); + return nullptr; + } + return node; +} + // 3.52.3 reportingDescriptor lookup // "For an example of the interaction between ruleId and rule.id, see §3.52.4." diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc index 68aaf86..c941d2f 100644 --- a/gcc/lra-constraints.cc +++ b/gcc/lra-constraints.cc @@ -2416,14 +2416,15 @@ process_alt_operands (int only_alternative) if (curr_static_id->operand[nop].type == OP_INOUT || curr_static_id->operand[m].type == OP_INOUT) break; - /* Operands don't match. If the operands are - different user defined explicit hard + /* Operands don't match. For asm if the operands + are different user defined explicit hard registers, then we cannot make them match when one is early clobber operand. */ if ((REG_P (*curr_id->operand_loc[nop]) || SUBREG_P (*curr_id->operand_loc[nop])) && (REG_P (*curr_id->operand_loc[m]) - || SUBREG_P (*curr_id->operand_loc[m]))) + || SUBREG_P (*curr_id->operand_loc[m])) + && INSN_CODE (curr_insn) < 0) { rtx nop_reg = *curr_id->operand_loc[nop]; if (SUBREG_P (nop_reg)) @@ -3328,19 +3329,15 @@ process_alt_operands (int only_alternative) first_conflict_j = j; last_conflict_j = j; /* Both the earlyclobber operand and conflicting operand - cannot both be user defined hard registers. */ + cannot both be user defined hard registers for asm. + Let curr_insn_transform diagnose it. */ if (HARD_REGISTER_P (operand_reg[i]) && REG_USERVAR_P (operand_reg[i]) && operand_reg[j] != NULL_RTX && HARD_REGISTER_P (operand_reg[j]) - && REG_USERVAR_P (operand_reg[j])) - { - /* For asm, let curr_insn_transform diagnose it. */ - if (INSN_CODE (curr_insn) < 0) + && REG_USERVAR_P (operand_reg[j]) + && INSN_CODE (curr_insn) < 0) return false; - fatal_insn ("unable to generate reloads for " - "impossible constraints:", curr_insn); - } } if (last_conflict_j < 0) continue; @@ -3930,6 +3927,16 @@ process_address_1 (int nop, bool check_only_p, enum reg_class cl; rtx set; rtx_insn *insns, *last_insn; + + cl = base_reg_class (ad.mode, ad.as, ad.base_outer_code, + get_index_code (&ad), curr_insn); + + if (REG_P (*ad.base_term) + && ira_class_subset_p[get_reg_class (REGNO (*ad.base_term))][cl]) + /* It seems base reg is already in the base reg class and changing it + does not make a progress. So reload the whole inner address. */ + goto reload_inner_addr; + /* Try to reload base into register only if the base is invalid for the address but with valid offset, case (4) above. */ start_sequence (); @@ -3975,8 +3982,6 @@ process_address_1 (int nop, bool check_only_p, { *ad.base_term = XEXP (SET_SRC (set), 0); *ad.disp_term = XEXP (SET_SRC (set), 1); - cl = base_reg_class (ad.mode, ad.as, ad.base_outer_code, - get_index_code (&ad), curr_insn); regno = REGNO (*ad.base_term); if (regno >= FIRST_PSEUDO_REGISTER && cl != lra_get_allocno_class (regno)) @@ -4019,11 +4024,11 @@ process_address_1 (int nop, bool check_only_p, } else { - enum reg_class cl = base_reg_class (ad.mode, ad.as, - SCRATCH, SCRATCH, - curr_insn); - rtx addr = *ad.inner; - + enum reg_class cl; + rtx addr; + reload_inner_addr: + cl = base_reg_class (ad.mode, ad.as, SCRATCH, SCRATCH, curr_insn); + addr = *ad.inner; new_reg = lra_create_new_reg (Pmode, NULL_RTX, cl, NULL, "addr"); /* addr => new_base. */ lra_emit_move (new_reg, addr); @@ -4044,14 +4049,21 @@ process_address (int nop, bool check_only_p, rtx_insn **before, rtx_insn **after) { bool res = false; - - while (process_address_1 (nop, check_only_p, before, after)) + /* Use enough iterations to process all address parts: */ + for (int i = 0; i < 10; i++) { - if (check_only_p) - return true; - res = true; + if (!process_address_1 (nop, check_only_p, before, after)) + { + return res; + } + else + { + if (check_only_p) + return true; + res = true; + } } - return res; + fatal_insn ("unable to reload address in ", curr_insn); } /* Override the generic address_reload_context in order to diff --git a/gcc/opts.cc b/gcc/opts.cc index 6ca1ec7..063fbd0 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1411,11 +1411,14 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, opts->x_debug_info_level = DINFO_LEVEL_NONE; } + /* Also enable markers with -fauto-profile even when debug info is disabled, + so we assign same discriminators and can read back the profile info. */ if (!opts_set->x_debug_nonbind_markers_p) opts->x_debug_nonbind_markers_p = (opts->x_optimize - && opts->x_debug_info_level >= DINFO_LEVEL_NORMAL - && (dwarf_debuginfo_p (opts) || codeview_debuginfo_p ()) + && ((opts->x_debug_info_level >= DINFO_LEVEL_NORMAL + && (dwarf_debuginfo_p (opts) || codeview_debuginfo_p ())) + || opts->x_flag_auto_profile) && !(opts->x_flag_selective_scheduling || opts->x_flag_selective_scheduling2)); diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index 3245453..d43d9dd 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -102,7 +102,9 @@ selftest::run_tests () diagnostic_show_locus_cc_tests (); diagnostic_format_html_cc_tests (); diagnostic_format_sarif_cc_tests (); + diagnostic_digraphs_cc_tests (); diagnostic_output_spec_cc_tests (); + diagnostic_state_graphs_cc_tests (); edit_context_cc_tests (); fold_const_cc_tests (); spellcheck_cc_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index eb9e604..06485e1 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -222,11 +222,13 @@ extern void cgraph_cc_tests (); extern void convert_cc_tests (); extern void dbgcnt_cc_tests (); extern void diagnostic_color_cc_tests (); +extern void diagnostic_digraphs_cc_tests (); extern void diagnostic_format_html_cc_tests (); extern void diagnostic_format_sarif_cc_tests (); extern void diagnostic_output_spec_cc_tests (); extern void diagnostic_path_output_cc_tests (); extern void diagnostic_show_locus_cc_tests (); +extern void diagnostic_state_graphs_cc_tests (); extern void digraph_cc_tests (); extern void dumpfile_cc_tests (); extern void edit_context_cc_tests (); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7cb2440..4f1b932 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,494 @@ +2025-07-11 David Malcolm <dmalcolm@redhat.com> + + * gcc.dg/analyzer/state-diagram-1-sarif.py (test_xml_state): + Rename to... + (test_state_graph): ...this. Port from XML to SARIF graphs. + * gcc.dg/analyzer/state-diagram-1.c: Update sink option + from "sarif:xml-state=yes" to "sarif:state-graphs=yes". + * gcc.dg/analyzer/state-diagram-5-sarif.c: Likewise. + * gcc.dg/analyzer/state-diagram-5-sarif.py: Drop import of ET. + (test_nested_types_in_xml_state): Rename to... + (test_nested_types_in_state_graph): ...this. Port from XML to + SARIF graphs. + * gcc.dg/plugin/diagnostic-test-graphs-html.c: New test. + * gcc.dg/plugin/diagnostic-test-graphs-html.py: New test script. + * gcc.dg/plugin/diagnostic-test-graphs-sarif.c: New test. + * gcc.dg/plugin/diagnostic-test-graphs-sarif.py: New test script. + * gcc.dg/plugin/diagnostic-test-graphs.c: New test. + * gcc.dg/plugin/diagnostic_plugin_test_graphs.cc: New test plugin. + * gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above. + * lib/sarif.py (get_xml_state): Delete. + (get_state_graph): New. + (def get_state_node_attr): New. + (get_state_node_kind): New. + (get_state_node_name): New. + (get_state_node_type): New. + (get_state_node_value): New. + * sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif: + New test. + * sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif: + New test. + * sarif-replay.dg/2.1.0-valid/graphs-check-html.py: New test + script. + * sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py: New + test script. + * sarif-replay.dg/2.1.0-valid/graphs.sarif: New test. + +2025-07-11 Vladimir N. Makarov <vmakarov@redhat.com> + + * gcc.target/powerpc/pr121007.c: New. + +2025-07-11 Jakub Jelinek <jakub@redhat.com> + + PR c++/119064 + * g++.dg/cpp26/feat-cxx26.C: Add test for + __cpp_trivial_relocatability. + * g++.dg/cpp26/trivially-relocatable1.C: New test. + * g++.dg/cpp26/trivially-relocatable2.C: New test. + * g++.dg/cpp26/trivially-relocatable3.C: New test. + * g++.dg/cpp26/trivially-relocatable4.C: New test. + * g++.dg/cpp26/trivially-relocatable5.C: New test. + * g++.dg/cpp26/trivially-relocatable6.C: New test. + * g++.dg/cpp26/trivially-relocatable7.C: New test. + * g++.dg/cpp26/trivially-relocatable8.C: New test. + * g++.dg/cpp26/trivially-relocatable9.C: New test. + * g++.dg/cpp26/trivially-relocatable10.C: New test. + * g++.dg/cpp26/trivially-relocatable11.C: New test. + +2025-07-11 Richard Sandiford <richard.sandiford@arm.com> + + PR target/121027 + * gcc.target/aarch64/sve/acle/general/perm_1.c: New test. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * gcc.target/aarch64/simd/eor3_d.c: Add tests for DImode operands. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * gcc.target/aarch64/simd/bcax_d.c: Add tests for DImode arguments. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * gcc.target/aarch64/simd/eor3_d.c: New test. + +2025-07-11 Kyrylo Tkachov <ktkachov@nvidia.com> + + * gcc.target/aarch64/simd/bcax_d.c: New test. + +2025-07-11 Richard Biener <rguenther@suse.de> + + PR tree-optimization/121034 + * gcc.dg/vect/pr121034.c: New testcase. + +2025-07-11 Jakub Jelinek <jakub@redhat.com> + + PR c++/120954 + * c-c++-common/Warray-bounds-11.c: New test. + +2025-07-11 Jan Hubicka <hubicka@ucw.cz> + + * g++.dg/lto/pr114790_0.C: New test. + * g++.dg/lto/pr114790_1.C: New test. + +2025-07-11 Jakub Jelinek <jakub@redhat.com> + Martin Jambor <mjambor@suse.cz> + + PR ipa/121023 + * c-c++-common/musttail32.c: New test. + +2025-07-11 Hu, Lin1 <lin1.hu@intel.com> + + PR target/91384 + * gcc.target/i386/pr91384-1.c: New test. + +2025-07-11 Paul Thomas <pault@gcc.gnu.org> + + PR fortran/106135 + * gfortran.dg/import3.f90: Use -std=f2008 and comment on change + in error message texts with f2018. + * gfortran.dg/import12.f90: New test. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120628 + * g++.dg/cpp0x/final1.C: New test. + * g++.dg/cpp0x/final2.C: New test. + * g++.dg/cpp0x/override6.C: New test. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120569 + * g++.dg/cpp0x/override2.C: Expect different diagnostics with + override or duplicate final. + * g++.dg/cpp0x/override5.C: New test. + * g++.dg/cpp0x/duplicate1.C: Expect different diagnostics with + duplicate final. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/117785 + * g++.dg/cpp0x/constexpr-ellipsis2.C: Expect different diagnostics for + C++26. + * g++.dg/cpp0x/constexpr-throw.C: Likewise. + * g++.dg/cpp1y/constexpr-84192.C: Expect different diagnostics. + * g++.dg/cpp1y/constexpr-throw.C: Expect different diagnostics for + C++26. + * g++.dg/cpp1z/constexpr-asm-5.C: Likewise. + * g++.dg/cpp26/constexpr-eh1.C: New test. + * g++.dg/cpp26/constexpr-eh2.C: New test. + * g++.dg/cpp26/constexpr-eh3.C: New test. + * g++.dg/cpp26/constexpr-eh4.C: New test. + * g++.dg/cpp26/constexpr-eh5.C: New test. + * g++.dg/cpp26/constexpr-eh6.C: New test. + * g++.dg/cpp26/constexpr-eh7.C: New test. + * g++.dg/cpp26/constexpr-eh8.C: New test. + * g++.dg/cpp26/constexpr-eh9.C: New test. + * g++.dg/cpp26/constexpr-eh10.C: New test. + * g++.dg/cpp26/constexpr-eh11.C: New test. + * g++.dg/cpp26/constexpr-eh12.C: New test. + * g++.dg/cpp26/constexpr-eh13.C: New test. + * g++.dg/cpp26/constexpr-eh14.C: New test. + * g++.dg/cpp26/constexpr-eh15.C: New test. + * g++.dg/cpp26/feat-cxx26.C: Change formatting in __cpp_pack_indexing + and __cpp_pp_embed test. Add __cpp_constexpr_exceptions test. + * g++.dg/cpp26/static_assert1.C: Expect different diagnostics for + C++26. + * g++.dg/cpp2a/consteval34.C: Likewise. + * g++.dg/cpp2a/consteval-memfn1.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic4.C: For C++26 add std::exception and + std::bad_cast definitions and expect different diagnostics. + * g++.dg/cpp2a/constexpr-dynamic6.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic7.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic8.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic9.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic11.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic14.C: Likewise. + * g++.dg/cpp2a/constexpr-dynamic18.C: Likewise. + * g++.dg/cpp2a/constexpr-new27.C: New test. + * g++.dg/cpp2a/constexpr-typeid5.C: New test. + +2025-07-10 Qing Zhao <qing.zhao@oracle.com> + + PR middle-end/121000 + * gcc.dg/flex-array-counted-by-pr121000.c: New test. + +2025-07-10 Mikael Morin <mikael@gcc.gnu.org> + + * gfortran.dg/asan/array_constructor_1.f90: Allocate array + before using it. + +2025-07-10 Jan Hubicka <hubicka@ucw.cz> + + * gcc.dg/tree-prof/afdo-inline.c: Add user symbol names. + +2025-07-10 Richard Sandiford <richard.sandiford@arm.com> + + * gcc.target/aarch64/sve/pfalse-binary.c: Add -funwind-tables. + * gcc.target/aarch64/sve/pfalse-binary_int_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binary_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binary_opt_single_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binary_rotate.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binary_uint64_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binary_uint_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-binaryxn.c: Likewise. + * gcc.target/aarch64/sve/pfalse-clast.c: Likewise. + * gcc.target/aarch64/sve/pfalse-compare_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-compare_wide_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-count_pred.c: Likewise. + * gcc.target/aarch64/sve/pfalse-fold_left.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_ext.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_ext_gather_index.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_ext_gather_offset.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_gather_sv.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_gather_vs.c: Likewise. + * gcc.target/aarch64/sve/pfalse-load_replicate.c: Likewise. + * gcc.target/aarch64/sve/pfalse-prefetch.c: Likewise. + * gcc.target/aarch64/sve/pfalse-prefetch_gather_index.c: Likewise. + * gcc.target/aarch64/sve/pfalse-prefetch_gather_offset.c: Likewise. + * gcc.target/aarch64/sve/pfalse-ptest.c: Likewise. + * gcc.target/aarch64/sve/pfalse-rdffr.c: Likewise. + * gcc.target/aarch64/sve/pfalse-reduction.c: Likewise. + * gcc.target/aarch64/sve/pfalse-reduction_wide.c: Likewise. + * gcc.target/aarch64/sve/pfalse-shift_right_imm.c: Likewise. + * gcc.target/aarch64/sve/pfalse-store.c: Likewise. + * gcc.target/aarch64/sve/pfalse-store_scatter_index.c: Likewise. + * gcc.target/aarch64/sve/pfalse-store_scatter_offset.c: Likewise. + * gcc.target/aarch64/sve/pfalse-storexn.c: Likewise. + * gcc.target/aarch64/sve/pfalse-ternary_opt_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-ternary_rotate.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary_convert_narrowt.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary_convertxn.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary_n.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary_pred.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unary_to_uint.c: Likewise. + * gcc.target/aarch64/sve/pfalse-unaryxn.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_int_opt_n.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_int_opt_single_n.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_opt_n.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_opt_single_n.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_to_uint.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_uint_opt_n.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-binary_wide.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-compare.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-load_ext_gather_index_restricted.c, + * gcc.target/aarch64/sve2/pfalse-load_ext_gather_offset_restricted.c, + * gcc.target/aarch64/sve2/pfalse-load_gather_sv_restricted.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-load_gather_vs.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-shift_left_imm_to_uint.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-shift_right_imm.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-store_scatter_index_restricted.c, + * gcc.target/aarch64/sve2/pfalse-store_scatter_offset_restricted.c, + * gcc.target/aarch64/sve2/pfalse-unary.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-unary_convert.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-unary_convert_narrowt.c: Likewise. + * gcc.target/aarch64/sve2/pfalse-unary_to_int.c: Likewise. + +2025-07-10 Kyrylo Tkachov <ktkachov@nvidia.com> + + PR target/120999 + * gcc.target/aarch64/sve2/pr120999.c: New test. + +2025-07-10 Richard Sandiford <richard.sandiford@arm.com> + + * lib/target-supports.exp (check_effective_target_aarch64_sve2p1_hw): + New proc. + * gcc.target/aarch64/sve2/dupq_1.c: Extend to big-endian. Add + noipa attributes. + * gcc.target/aarch64/sve2/extq_1.c: Likewise. + * gcc.target/aarch64/sve2/uzpq_1.c: Likewise. + * gcc.target/aarch64/sve2/zipq_1.c: Likewise. + * gcc.target/aarch64/sve2/dupq_1_run.c: New test. + * gcc.target/aarch64/sve2/extq_1_run.c: Likewise. + * gcc.target/aarch64/sve2/uzpq_1_run.c: Likewise. + * gcc.target/aarch64/sve2/zipq_1_run.c: Likewise. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * gfortran.dg/g77/980310-3.f: Comment spelling fix: bellow -> below. + * jit.dg/test-debuginfo.c: Likewise. + +2025-07-10 Pan Li <pan2.li@intel.com> + + * gcc.target/riscv/sat/sat_s_add-run-1-i16.c: Take rv32 || rv64 + instead of riscv_v for scalar run test. + * gcc.target/riscv/sat/sat_s_add-run-1-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-1-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-1-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-2-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-2-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-2-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-2-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-3-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-3-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-3-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-3-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-4-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-4-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-4-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_add-run-4-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-1-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-1-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-1-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-1-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-2-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-2-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-2-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-2-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-3-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-3-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-3-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-3-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-4-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-4-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-4-i64.c: Ditto. + * gcc.target/riscv/sat/sat_s_sub-run-4-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-1-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-2-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-3-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-4-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-5-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-6-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-7-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i16-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i32-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i32-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i64-to-i16.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i64-to-i32.c: Ditto. + * gcc.target/riscv/sat/sat_s_trunc-run-8-i64-to-i8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-1-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-1-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-1-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-1-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-2-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-2-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-2-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-2-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-3-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-3-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-3-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-3-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-4-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-4-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-4-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-4-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-5-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-5-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-5-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-5-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-6-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-6-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-6-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-6-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u16-from-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u16-from-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u32-from-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u8-from-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u8-from-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add-run-7-u8-from-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-1-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-1-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-1-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-1-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-2-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-2-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-2-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-2-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-3-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-3-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-3-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-3-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-4-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-4-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-4-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_add_imm-run-4-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-1-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-1-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-1-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-1-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-10-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-10-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-10-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-10-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-11-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-11-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-11-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-11-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-12-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-12-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-12-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-12-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-2-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-2-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-2-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-2-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-3-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-3-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-3-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-3-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-4-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-4-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-4-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-4-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-5-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-5-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-5-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-5-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-6-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-6-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-6-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-6-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-7-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-7-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-7-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-7-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-8-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-8-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-8-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-8-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-9-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-9-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-9-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub-run-9-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-1-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-1-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-1-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-1-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-2-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-2-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-2-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-2-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-3-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-3-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-3-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-3-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-4-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-4-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-4-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_sub_imm-run-4-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-1-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-1-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-1-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-1-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-2-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-2-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-2-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-2-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-3-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-3-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-3-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-3-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-4-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-4-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-4-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-4-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-5-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-5-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-5-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-5-u8.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-6-u16.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-6-u32.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-6-u64.c: Ditto. + * gcc.target/riscv/sat/sat_u_trunc-run-6-u8.c: Ditto. + 2025-07-09 Jason Merrill <jason@redhat.com> PR c++/120243 diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-11.c b/gcc/testsuite/c-c++-common/Warray-bounds-11.c new file mode 100644 index 0000000..686a94d --- /dev/null +++ b/gcc/testsuite/c-c++-common/Warray-bounds-11.c @@ -0,0 +1,21 @@ +/* PR c++/120954 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -Warray-bounds=2" } */ + +static const int a[32] = { 11, 12, 13, 14, 15 }; +static const int b[32] = { 21, 22, 23, 24, 25 }; +static const int c[32] = { 31, 32, 33, 34, 35 }; +static const int d[32] = { 111, 112, 113, 114, 115 }; +static const int e[32] = { 121, 122, 123, 124, 125 }; +static const int f[32] = { 131, 132, 133, 134, 135 }; + +int +foo (int x, int y) +{ + int r = 0; + if (x >= 0 && x < 32) + r = (y >= 4 ? (y >= 0x65 ? a : b ) : c)[x]; + else if (x >= 0x100 && x < 0x120) + r = (y >= 4 ? (y >= 0x65 ? d : e ) : f)[x - 0x100]; + return r; +} diff --git a/gcc/testsuite/c-c++-common/musttail32.c b/gcc/testsuite/c-c++-common/musttail32.c new file mode 100644 index 0000000..f1b7052 --- /dev/null +++ b/gcc/testsuite/c-c++-common/musttail32.c @@ -0,0 +1,23 @@ +/* PR ipa/121023 */ +/* { dg-do compile { target musttail } } */ +/* { dg-options "-O2" } */ + +struct S { int a, b; }; + +[[gnu::noipa]] int +foo (struct S x, int y, int z) +{ + return x.a + y + z; +} + +[[gnu::noinline]] static int +bar (struct S x, int y, int z) +{ + [[gnu::musttail]] return foo ((struct S) { x.a, 0 }, y, 1); +} + +int +baz (int x) +{ + return bar ((struct S) { 1, 2 }, x, 2) + bar ((struct S) { 2, 3 }, x + 1, 2); +} diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 97bd36b..cfc5f61 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -646,3 +646,9 @@ #elif __cpp_constexpr_exceptions != 202411 # error "__cpp_constexpr_exceptions != 202411" #endif + +#ifndef __cpp_trivial_relocatability +# error "__cpp_trivial_relocatability" +#elif __cpp_trivial_relocatability != 202502 +# error "__cpp_trivial_relocatability != 202502" +#endif diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable1.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable1.C new file mode 100644 index 0000000..29ba907 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable1.C @@ -0,0 +1,137 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A {}; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { + B (); + ~B (); + B (const B &); + B (B &&); + B &operator= (const B &); + B &operator= (B &&); +}; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +struct C { + C (C &&) = delete; + C &operator= (C &&) = delete; + C () = default; +}; + +// Note, P2786R13 says it is trivially relocatable, but I think +// it isn't default-movable because overload resolution in both +// cases selects a deleted special member fn. +static_assert (!std::is_trivially_relocatable_v <C>, ""); +static_assert (!std::is_replaceable_v <C>, ""); + +struct D : A {}; + +static_assert (std::is_trivially_relocatable_v <D>, ""); +static_assert (std::is_replaceable_v <D>, ""); + +struct E : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +struct F trivially_relocatable_if_eligible : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +struct G { B data; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +struct H { ~H () = default; }; + +static_assert (std::is_trivially_relocatable_v <H>, ""); +static_assert (std::is_replaceable_v <H>, ""); + +struct I { ~I (); }; +I::~I () = default; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +struct J { virtual ~J () = default; }; + +// Note, P2786R13 says otherwise for both, but that looks like +// a bug in the paper, it otherwise says that polymorphic types +// can be both trivially relocatable and replaceable. +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (std::is_replaceable_v <J>, ""); + +struct K { ~K () = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (L &&) = default; }; + +// Note, P2786R13 says otherwise for both, but that looks like +// a bug in the paper to me. While move ctor is trivial here, +// copy assignment operator is implicitly declared as deleted +// and move assignent operator is not declared. +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M (M &&); }; +M::M (M &&) = default; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N (N &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable10.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable10.C new file mode 100644 index 0000000..2ed1d5e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable10.C @@ -0,0 +1,135 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A replaceable_if_eligible { A (A &&); A &operator= (A &&); ~A (); int a; }; + +static_assert (std::is_replaceable_v <A>, ""); +static_assert (!std::is_replaceable_v <const A>, ""); +static_assert (!std::is_replaceable_v <A volatile>, ""); +static_assert (!std::is_replaceable_v <const A volatile>, ""); + +struct B { B (B &&); B &operator= (B &&); ~B (); int a; }; + +static_assert (!std::is_replaceable_v <B>, ""); + +struct C replaceable_if_eligible : public A { C (C &&); C &operator= (C &&); ~C (); int a; }; + +static_assert (std::is_replaceable_v <C>, ""); + +struct D replaceable_if_eligible : public B { D (D &&); D &operator= (D &&); ~D (); int a; }; + +static_assert (!std::is_replaceable_v <D>, ""); + +struct E replaceable_if_eligible { E (E &&); E &operator= (E &&); ~E (); A a; }; + +static_assert (std::is_replaceable_v <E>, ""); + +struct F replaceable_if_eligible { F (F &&); F &operator= (F &&); ~F (); B a; }; + +static_assert (!std::is_replaceable_v <F>, ""); + +struct G replaceable_if_eligible { G (G &&); G &operator= (G &&); ~G () = delete; int a; }; + +static_assert (!std::is_replaceable_v <G>, ""); + +struct H replaceable_if_eligible : virtual A { H (H &&); H &operator= (H &&); ~H (); int a; }; + +static_assert (std::is_replaceable_v <H>, ""); + +struct I replaceable_if_eligible { I (I &&) = delete; I &operator= (I &&); ~I (); int a; }; + +static_assert (!std::is_replaceable_v <I>, ""); + +struct J replaceable_if_eligible { J (J &&); J &operator= (J &&) = delete; ~J (); int a; }; + +static_assert (!std::is_replaceable_v <J>, ""); + +struct K replaceable_if_eligible { K (const K &) = delete; K &operator= (K &&); ~K (); int a; }; + +static_assert (!std::is_replaceable_v <K>, ""); + +struct L replaceable_if_eligible { L (L &&); L &operator= (const L &) = delete; ~L (); int a; }; + +static_assert (!std::is_replaceable_v <L>, ""); + +struct M replaceable_if_eligible { M (); private: M (M &&); M &operator= (M &&); ~M (); int a; }; + +static_assert (std::is_replaceable_v <M>, ""); + +struct N replaceable_if_eligible { N (N &&); N &operator= (N &&); ~N (); const A a; }; + +static_assert (!std::is_replaceable_v <N>, ""); + +struct O replaceable_if_eligible { O (O &&); O &operator= (O &&); ~O (); volatile A a; }; + +static_assert (!std::is_replaceable_v <O>, ""); + +struct P replaceable_if_eligible { P (P &&); P &operator= (P &&); ~P (); const volatile A a; }; + +static_assert (!std::is_replaceable_v <P>, ""); + +struct Q replaceable_if_eligible { Q (Q &&); Q &operator= (Q &&); ~Q (); union { A a; int b; char c; }; }; + +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R replaceable_if_eligible { R (R &&); R &operator= (R &&); ~R (); union { int a; B b; short c; }; }; + +static_assert (!std::is_replaceable_v <R>, ""); + +struct S replaceable_if_eligible { S (S &&); S &operator= (S &&); ~S (); union { int a; const int b; short c; }; }; + +static_assert (!std::is_replaceable_v <S>, ""); + +struct T replaceable_if_eligible { T (T &&); T &operator= (T &&); ~T () = default; int a; }; + +static_assert (std::is_replaceable_v <T>, ""); + +struct U replaceable_if_eligible { U (U &&); U &operator= (U &&); ~U (); union { T a; int b; char c; }; }; + +static_assert (!std::is_replaceable_v <U>, ""); + +struct V replaceable_if_eligible { V (V &&); V &operator= (V &&); ~V (); union { unsigned long long a; int b; char c; }; }; + +static_assert (std::is_replaceable_v <V>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable11.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable11.C new file mode 100644 index 0000000..71fc6f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable11.C @@ -0,0 +1,134 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A { A (A &&) = default; A &operator= (A &&) = default; ~A () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&) = default; ~B () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +union C { int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_replaceable_v <C>, ""); + +union D { int a; A b; B c; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); +static_assert (!std::is_replaceable_v <D>, ""); + +union E { E (); int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +union F { F () = default; int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +union G { G (const G &); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +union H { H (const H &) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +union I { I (I &&); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +union J { J (J &&) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +union K { K &operator= (const K &); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +union L { L &operator= (const L &) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +union M { M &operator= (M &&); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +union N { N &operator= (N &&) = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); + +union O { ~O (); int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +union P { ~P () = default; int a; A b; }; + +static_assert (!std::is_trivially_relocatable_v <P>, ""); +static_assert (!std::is_replaceable_v <P>, ""); + +union Q { int a; const A b; }; + +static_assert (std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +union S { volatile int a; A b; }; + +static_assert (std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable2.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable2.C new file mode 100644 index 0000000..b740061 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable2.C @@ -0,0 +1,204 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +class A {}; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); +static_assert (std::is_trivially_relocatable <A>::value, ""); +static_assert (std::is_replaceable <A>::value, ""); + +struct B { ~B (); }; +static B z; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); +static_assert (!std::is_trivially_relocatable <B>::value, ""); +static_assert (!std::is_replaceable <B>::value, ""); + +class C trivially_relocatable_if_eligible {}; + +static_assert (std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_replaceable_v <C>, ""); + +class D trivially_relocatable_if_eligible : A {}; + +static_assert (std::is_trivially_relocatable_v <D>, ""); +static_assert (std::is_replaceable_v <D>, ""); + +class E trivially_relocatable_if_eligible { + int a; + void *b; + int c[3]; + A d[3]; + B &e = z; +}; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +class F trivially_relocatable_if_eligible : A {}; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +class G trivially_relocatable_if_eligible : virtual A {}; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +class H trivially_relocatable_if_eligible : B {}; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +class I trivially_relocatable_if_eligible { I (I &&); }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +class J trivially_relocatable_if_eligible { ~J (); }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +class K trivially_relocatable_if_eligible { + B a; + B b[1]; + const B c; + const B d[1]; +}; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +class L trivially_relocatable_if_eligible: virtual A, B { B a; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +static_assert (!std::is_trivially_relocatable_v <void>, ""); +static_assert (!std::is_trivially_relocatable_v <const void>, ""); +static_assert (std::is_trivially_relocatable_v <int>, ""); +static_assert (std::is_trivially_relocatable_v <const int>, ""); +static_assert (std::is_trivially_relocatable_v <char>, ""); +static_assert (std::is_trivially_relocatable_v <char const volatile>, ""); +static_assert (std::is_trivially_relocatable_v <unsigned long long>, ""); +static_assert (std::is_trivially_relocatable_v <void *>, ""); +static_assert (std::is_trivially_relocatable_v <const int *>, ""); +static_assert (!std::is_trivially_relocatable_v <int &>, ""); +static_assert (!std::is_trivially_relocatable_v <A &>, ""); +static_assert (std::is_trivially_relocatable_v <const A>, ""); +static_assert (std::is_trivially_relocatable_v <A [1]>, ""); +static_assert (std::is_trivially_relocatable_v <A []>, ""); +static_assert (!std::is_replaceable_v <void>, ""); +static_assert (!std::is_replaceable_v <const void>, ""); +static_assert (std::is_replaceable_v <int>, ""); +static_assert (!std::is_replaceable_v <const int>, ""); +static_assert (std::is_replaceable_v <char>, ""); +static_assert (!std::is_replaceable_v <char const volatile>, ""); +static_assert (std::is_replaceable_v <unsigned long long>, ""); +static_assert (std::is_replaceable_v <void *>, ""); +static_assert (std::is_replaceable_v <const int *>, ""); +static_assert (!std::is_replaceable_v <int &>, ""); +static_assert (!std::is_replaceable_v <A &>, ""); +static_assert (!std::is_replaceable_v <const A>, ""); +static_assert (std::is_replaceable_v <A [1]>, ""); +static_assert (std::is_replaceable_v <A []>, ""); + +struct M { const int i; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N trivially_relocatable_if_eligible { const int i; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); + +struct O { ~O (); }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +struct P { ~P () = default; }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q { Q (Q &&); Q (const Q &) = default; }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R { R (R &&); }; + +static_assert (!std::is_trivially_relocatable_v <R>, ""); +static_assert (!std::is_replaceable_v <R>, ""); + +struct S { S (S &&) = default; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T { T (T &&) = default; T &operator= (T &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <T>, ""); +static_assert (std::is_replaceable_v <T>, ""); + +struct U { U (const U &); }; + +static_assert (!std::is_trivially_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +struct V { V (const V&) = default; }; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); + +struct W { W (W &&) = delete; W (const W &) = default; }; + +static_assert (!std::is_trivially_relocatable_v <W>, ""); +static_assert (!std::is_replaceable_v <W>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable3.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable3.C new file mode 100644 index 0000000..312c11b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable3.C @@ -0,0 +1,213 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +class A {}; + +struct B { ~B (); }; + +class C trivially_relocatable_if_eligible { C (C &&); }; + +template <typename T> +class D trivially_relocatable_if_eligible : T {}; +D<A> a; +D<B> b; + +static_assert (std::is_trivially_relocatable_v <D<A>>, ""); +static_assert (!std::is_trivially_relocatable_v <D<B>>, ""); +static_assert (std::is_replaceable_v <D<A>>, ""); +static_assert (!std::is_replaceable_v <D<B>>, ""); + +struct E { E (E &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +struct F { F (const F &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (!std::is_replaceable_v <F>, ""); + +struct G { G &operator= (G &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_replaceable_v <G>, ""); + +struct H { ~H () = delete; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +union U { C u; }; + +static_assert (std::is_trivially_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +template <typename T> +struct I { int s; T t; }; + +static_assert (std::is_trivially_relocatable_v <I<int>>, ""); +static_assert (std::is_trivially_relocatable_v <I<volatile int>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int &>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<int &>>, ""); +static_assert (std::is_trivially_relocatable_v <I<int [2]>>, ""); +static_assert (!std::is_trivially_relocatable_v <I<const int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <I<int []>>, ""); +static_assert (std::is_replaceable_v <I<int>>, ""); +static_assert (!std::is_replaceable_v <I<volatile int>>, ""); +static_assert (!std::is_replaceable_v <I<const int>>, ""); +static_assert (!std::is_replaceable_v <I<const int &>>, ""); +static_assert (!std::is_replaceable_v <I<int &>>, ""); +static_assert (std::is_replaceable_v <I<int [2]>>, ""); +static_assert (!std::is_replaceable_v <I<const int [2]>>, ""); + +template <typename T> +struct J trivially_relocatable_if_eligible { int s; T t; }; + +static_assert (std::is_trivially_relocatable_v <J<int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<volatile int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int &>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int &>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <J<const int [2]>>, ""); +static_assert (std::is_trivially_relocatable_v <J<int []>>, ""); +static_assert (std::is_replaceable_v <J<int>>, ""); +static_assert (!std::is_replaceable_v <J<volatile int>>, ""); +static_assert (!std::is_replaceable_v <J<const int>>, ""); +static_assert (!std::is_replaceable_v <J<const int &>>, ""); +static_assert (!std::is_replaceable_v <J<int &>>, ""); +static_assert (std::is_replaceable_v <J<int [2]>>, ""); +static_assert (!std::is_replaceable_v <J<const int [2]>>, ""); +static_assert (std::is_replaceable_v <J<int []>>, ""); + +struct K { K (K &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (const L &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M &operator= (M &&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N (N &&) = default; N &operator= (N &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (std::is_replaceable_v <N>, ""); + +struct O { + O (const O &) = default; + O (O &&) = default; + O &operator= (O &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <O>, ""); +static_assert (std::is_replaceable_v <O>, ""); + +struct P { P (P &&) = default; P &operator= (P &&) = default; }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q { Q (Q &&) {} }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct R { R (const R &) {} }; + +static_assert (!std::is_trivially_relocatable_v <R>, ""); +static_assert (!std::is_replaceable_v <R>, ""); + +struct S { S &operator= (const S &) { return *this; }; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T {}; + +static_assert (std::is_trivially_relocatable_v <T>, ""); +static_assert (std::is_replaceable_v <T>, ""); + +struct V replaceable_if_eligible {}; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); + +struct W { template <typename U> W (const U &) = delete; }; + +static_assert (std::is_trivially_relocatable_v <W>, ""); +static_assert (std::is_replaceable_v <W>, ""); + +template <typename T> +struct X : T {}; + +static_assert (!std::is_trivially_relocatable_v <X<Q>>, ""); +static_assert (!std::is_replaceable_v <X<Q>>, ""); + +template <typename T> +struct Y : virtual T {}; + +static_assert (!std::is_trivially_relocatable_v <Y<I<int>>>, ""); +static_assert (!std::is_trivially_relocatable_v <Y<I<const int>>>, ""); +static_assert (!std::is_trivially_relocatable_v <Y<Q>>, ""); +static_assert (std::is_replaceable_v <Y<I<int>>>, ""); +static_assert (!std::is_replaceable_v <Y<I<const int>>>, ""); +static_assert (!std::is_replaceable_v <Y<Q>>, ""); + +struct Z { + virtual ~Z () = default; + Z (Z &&) = default; + Z &operator= (Z &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <Z>, ""); +static_assert (std::is_replaceable_v <Z>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable4.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable4.C new file mode 100644 index 0000000..10aafc1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable4.C @@ -0,0 +1,128 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A replaceable_if_eligible { + ~A () = delete; + A (A &&) = default; + A &operator= (A &&) = default; +}; + +static_assert (!std::is_trivially_relocatable_v <A>, ""); +static_assert (!std::is_replaceable_v <A>, ""); + +struct B replaceable_if_eligible { B (const B &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +template <typename T> +struct C replaceable_if_eligible : virtual T {}; + +static_assert (!std::is_trivially_relocatable_v <C<A>>, ""); +static_assert (!std::is_trivially_relocatable_v <C<B>>, ""); +static_assert (!std::is_replaceable_v <C<A>>, ""); +static_assert (!std::is_replaceable_v <C<B>>, ""); + +template <typename T> +struct D { int s; T t; }; + +static_assert (!std::is_trivially_relocatable_v <C<D<int>>>, ""); +static_assert (std::is_replaceable_v <C<D<int>>>, ""); + +struct E trivially_relocatable_if_eligible replaceable_if_eligible { + E (E &&); + E &operator= (E &&) = default; +}; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_replaceable_v <E>, ""); + +struct F trivially_relocatable_if_eligible replaceable_if_eligible { + F (F &&) = default; + F &operator= (F &&); +}; + +static_assert (std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_replaceable_v <F>, ""); + +struct G replaceable_if_eligible { G (G const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +struct H { H (H const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <H>, ""); +static_assert (std::is_replaceable_v <H>, ""); + +struct I replaceable_if_eligible { I &operator= (const I &) = default; }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); +static_assert (std::is_replaceable_v <I>, ""); + +struct J { J &operator= (J const &) = default; }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (std::is_replaceable_v <J>, ""); + +struct K { K (const K &) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct L { L (L&&) = delete; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_replaceable_v <L>, ""); + +struct M { M operator= (M); }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct N { N operator= (N &&); }; + +static_assert (!std::is_trivially_relocatable_v <N>, ""); +static_assert (!std::is_replaceable_v <N>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable5.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable5.C new file mode 100644 index 0000000..0416137 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable5.C @@ -0,0 +1,77 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-additional-options "-Wc++26-compat" } + +struct A __trivially_relocatable_if_eligible { A (const A &&); }; +struct B __replaceable_if_eligible { B (const B &&); B &operator= (B &&); }; +struct C __replaceable_if_eligible __final __trivially_relocatable_if_eligible { C (const C &&); C &operator= (C &&); }; +#if __cpp_trivial_relocatability >= 202502L +struct D trivially_relocatable_if_eligible { D (const D &&); }; +struct E replaceable_if_eligible { E (const E &&); E &operator= (E &&); }; +struct F trivially_relocatable_if_eligible replaceable_if_eligible final { F (const F &&); F &operator= (F &&); }; +#else +struct D trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "variable 'D trivially_relocatable_if_eligible' has initializer but incomplete type" "" { target c++23_down } .-1 } +struct E replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "variable 'E replaceable_if_eligible' has initializer but incomplete type" "" { target c++23_down } .-1 } +struct F trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target c++23_down } .-1 } +#endif +#if __cplusplus <= 202302L +struct G {}; +struct G trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct H {}; +struct H replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct I {}; +struct I trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +#endif // { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target c++23_down } .-1 } +struct J {}; +struct J __trivially_relocatable_if_eligible {}; // { dg-error "redefinition of 'struct J'" } +struct K {}; +struct K __replaceable_if_eligible {}; // { dg-error "redefinition of 'struct K'" } +struct L {}; +struct L __trivially_relocatable_if_eligible __replaceable_if_eligible {}; // { dg-error "redefinition of 'struct L'" } +struct M __trivially_relocatable_if_eligible __trivially_relocatable_if_eligible {}; // { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" } +struct N __replaceable_if_eligible __replaceable_if_eligible {}; // { dg-error "duplicate '__replaceable_if_eligible' specifier" } +struct O __trivially_relocatable_if_eligible __replaceable_if_eligible __replaceable_if_eligible __trivially_relocatable_if_eligible final final {}; +// { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target *-*-* } .-1 } +// { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target *-*-* } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target *-*-* } .-3 } +#if __cpp_trivial_relocatability >= 202502L +struct P trivially_relocatable_if_eligible trivially_relocatable_if_eligible {}; // { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct Q replaceable_if_eligible replaceable_if_eligible {}; // { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } } +struct R trivially_relocatable_if_eligible replaceable_if_eligible replaceable_if_eligible trivially_relocatable_if_eligible final final {}; +// { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target c++26 } .-3 } +struct S trivially_relocatable_if_eligible __trivially_relocatable_if_eligible {}; // { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct T replaceable_if_eligible __replaceable_if_eligible {}; // { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target c++26 } } +struct U trivially_relocatable_if_eligible replaceable_if_eligible __replaceable_if_eligible __trivially_relocatable_if_eligible final __final {}; +// { dg-error "duplicate '__replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate '__trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate '__final' specifier" "" { target c++26 } .-3 } +struct V __trivially_relocatable_if_eligible trivially_relocatable_if_eligible {}; // { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } } +struct W __replaceable_if_eligible replaceable_if_eligible {}; // { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } } +struct X __trivially_relocatable_if_eligible __replaceable_if_eligible replaceable_if_eligible trivially_relocatable_if_eligible __final final {}; +// { dg-error "duplicate 'replaceable_if_eligible' specifier" "" { target c++26 } .-1 } +// { dg-error "duplicate 'trivially_relocatable_if_eligible' specifier" "" { target c++26 } .-2 } +// { dg-error "duplicate 'final' specifier" "" { target c++26 } .-3 } +#else +struct Y {}; +Y foo (); +struct Y trivially_relocatable_if_eligible = foo (); // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +struct Z {}; +Z bar (); +struct Z replaceable_if_eligible = bar (); // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" "" { target c++23_down } } +#endif + +static_assert (__builtin_is_trivially_relocatable (A), ""); +static_assert (__builtin_is_replaceable (B), ""); +static_assert (__builtin_is_trivially_relocatable (C), ""); +static_assert (__builtin_is_replaceable (C), ""); +#if __cpp_trivial_relocatability >= 202502L +static_assert (__builtin_is_trivially_relocatable (D), ""); +static_assert (__builtin_is_replaceable (E), ""); +static_assert (__builtin_is_trivially_relocatable (F), ""); +static_assert (__builtin_is_replaceable (F), ""); +#endif diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable6.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable6.C new file mode 100644 index 0000000..ffcf12b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable6.C @@ -0,0 +1,30 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++98_only } } +// { dg-additional-options "-Wc++26-compat" } + +struct A __trivially_relocatable_if_eligible {}; +struct B __replaceable_if_eligible {}; +struct C __replaceable_if_eligible __final __trivially_relocatable_if_eligible {}; +struct D trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "variable 'D trivially_relocatable_if_eligible' has initializer but incomplete type" "" { target *-*-* } .-1 } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-2 } +struct E replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } +// { dg-error "variable 'E replaceable_if_eligible' has initializer but incomplete type" "" { target *-*-* } .-1 } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-2 } +struct F trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target *-*-* } .-1 } +struct G {}; +struct G trivially_relocatable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-1 } +struct H {}; +struct H replaceable_if_eligible {}; // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } +// { dg-error "extended initializer lists only available with" "" { target *-*-* } .-1 } +struct I {}; +struct I trivially_relocatable_if_eligible replaceable_if_eligible {}; // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +// { dg-error "expected initializer before 'replaceable_if_eligible'" "" { target *-*-* } .-1 } +struct J {}; +J foo (); +struct J trivially_relocatable_if_eligible = foo (); // { dg-warning "identifier 'trivially_relocatable_if_eligible' is a conditional keyword in" } +struct K {}; +K bar (); +struct K replaceable_if_eligible = bar (); // { dg-warning "identifier 'replaceable_if_eligible' is a conditional keyword in" } diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable7.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable7.C new file mode 100644 index 0000000..608c245 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable7.C @@ -0,0 +1,33 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + // { dg-error "invalid use of incomplete type 'struct A'" "" { target *-*-* } .-1 } + +template <typename T> +inline constexpr bool is_nothrow_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_nothrow_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + // { dg-error "invalid use of incomplete type 'struct A'" "" { target *-*-* } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} // { dg-error "invalid use of incomplete type 'struct A'" "" { target *-*-* } .-1 } + +struct A; // { dg-message "forward declaration of 'struct A'" } + +auto a = std::is_trivially_relocatable_v <A>; // { dg-message "required from here" } +auto b = std::is_nothrow_relocatable_v <A>; // { dg-message "required from here" } +auto c = std::is_replaceable_v <A>; // { dg-message "required from here" } diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable8.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable8.C new file mode 100644 index 0000000..5f8390d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable8.C @@ -0,0 +1,190 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_nothrow_relocatable + : public integral_constant <bool, __builtin_is_nothrow_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_nothrow_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_nothrow_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A { A (A &&) = default; A &operator= (A &&) = default; ~A () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_nothrow_relocatable_v <A>, ""); +static_assert (std::is_replaceable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&) = default; ~B () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_nothrow_relocatable_v <B>, ""); +static_assert (!std::is_replaceable_v <B>, ""); + +struct C { C (C &&) = default; C &operator= (C &&); ~C () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_nothrow_relocatable_v <C>, ""); +static_assert (!std::is_replaceable_v <C>, ""); + +struct D { D (D &&) = delete; D &operator= (D &&) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); +static_assert (!std::is_nothrow_relocatable_v <D>, ""); +static_assert (!std::is_replaceable_v <D>, ""); + +struct E { E (E &&) = default; E &operator= (E &&) = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_nothrow_relocatable_v <E>, ""); +static_assert (!std::is_replaceable_v <E>, ""); + +struct F { F (F &&) = default; F &operator= (F &&) = default; ~F () = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (!std::is_nothrow_relocatable_v <F>, ""); +static_assert (!std::is_replaceable_v <F>, ""); + +struct G { G (const G &) = default; G &operator= (const G &) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <G>, ""); +static_assert (std::is_nothrow_relocatable_v <G>, ""); +static_assert (std::is_replaceable_v <G>, ""); + +struct H { H (const H &); H &operator= (const H &) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_nothrow_relocatable_v <H>, ""); +static_assert (!std::is_replaceable_v <H>, ""); + +struct I { I (const I &) = default; I &operator= (const I &); ~I () = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <I>, ""); +static_assert (std::is_nothrow_relocatable_v <I>, ""); +static_assert (!std::is_replaceable_v <I>, ""); + +struct J { J (const J &) = delete; J &operator= (const J &) = default; int a; }; + +static_assert (!std::is_trivially_relocatable_v <J>, ""); +static_assert (!std::is_nothrow_relocatable_v <J>, ""); +static_assert (!std::is_replaceable_v <J>, ""); + +struct K { K (const K &) = default; K &operator= (const K &) = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (std::is_nothrow_relocatable_v <K>, ""); +static_assert (!std::is_replaceable_v <K>, ""); + +struct M; +struct L { L (L &&) = default; L (M &&); L &operator= (L &&) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <L>, ""); +static_assert (std::is_nothrow_relocatable_v <L>, ""); +static_assert (std::is_replaceable_v <L>, ""); + +struct M : public L { using L::L; M (const M &); M &operator= (M &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <M>, ""); +static_assert (!std::is_nothrow_relocatable_v <M>, ""); +static_assert (!std::is_replaceable_v <M>, ""); + +struct O; +struct N { N (N &&) = default; N &operator= (N &&) = default; N &operator= (O &&); int a; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (std::is_nothrow_relocatable_v <N>, ""); +static_assert (std::is_replaceable_v <N>, ""); + +struct O : public N { using N::operator=; O (O &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <O>, ""); +static_assert (std::is_nothrow_relocatable_v <O>, ""); +static_assert (!std::is_replaceable_v <O>, ""); + +struct Q; +struct P { template <typename T> P (T &&) {} }; + +static_assert (std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_nothrow_relocatable_v <P>, ""); +static_assert (std::is_replaceable_v <P>, ""); + +struct Q : public P { using P::P; Q (const Q &); }; + +static_assert (!std::is_trivially_relocatable_v <Q>, ""); +static_assert (!std::is_nothrow_relocatable_v <Q>, ""); +static_assert (!std::is_replaceable_v <Q>, ""); + +struct S; +struct R { R (const R &) = default; R (const M &); R &operator= (R &&) = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <R>, ""); +static_assert (std::is_nothrow_relocatable_v <R>, ""); +static_assert (std::is_replaceable_v <R>, ""); + +struct S : public R { using R::R; S &operator= (S &&) = default; int b; }; + +static_assert (!std::is_trivially_relocatable_v <S>, ""); +static_assert (!std::is_nothrow_relocatable_v <S>, ""); +static_assert (!std::is_replaceable_v <S>, ""); + +struct T { T (T &&) = default; T &operator= (T &&) = default; ~T (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <T>, ""); +static_assert (std::is_nothrow_relocatable_v <T>, ""); +static_assert (!std::is_replaceable_v <T>, ""); + +struct U { U (const U &) = default; U &operator= (const U &) = default; ~U (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <U>, ""); +static_assert (std::is_nothrow_relocatable_v <U>, ""); +static_assert (!std::is_replaceable_v <U>, ""); + +struct V { public: V (); private: V (V &&) = default; V &operator= (V &&) = default; ~V () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <V>, ""); +static_assert (std::is_nothrow_relocatable_v <V>, ""); +static_assert (std::is_replaceable_v <V>, ""); diff --git a/gcc/testsuite/g++.dg/cpp26/trivially-relocatable9.C b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable9.C new file mode 100644 index 0000000..1c45b53 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/trivially-relocatable9.C @@ -0,0 +1,134 @@ +// P2786R13 - C++26 Trivial Relocatability +// { dg-do compile { target c++11 } } +// { dg-options "" } +// { dg-additional-options "-pedantic" { target c++17 } } + +#if __cpp_trivial_relocatability < 202502L +#define trivially_relocatable_if_eligible __trivially_relocatable_if_eligible +#define replaceable_if_eligible __replaceable_if_eligible +#endif + +namespace std +{ +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; +}; + +template <typename> +struct is_trivially_relocatable; + +template <typename> +struct is_replaceable; + +template<typename T> +struct is_trivially_relocatable + : public integral_constant <bool, __builtin_is_trivially_relocatable (T)> +{ +}; + +template<typename T> +struct is_nothrow_relocatable + : public integral_constant <bool, __builtin_is_nothrow_relocatable (T)> +{ +}; + +template<typename T> +struct is_replaceable + : public integral_constant <bool, __builtin_is_replaceable (T)> +{ +}; + +template <typename T> +inline constexpr bool is_trivially_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_trivially_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_nothrow_relocatable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_nothrow_relocatable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } + +template <typename T> +inline constexpr bool is_replaceable_v // { dg-warning "inline variables are only available with" "" { target c++14_down } } + = __builtin_is_replaceable (T); // { dg-warning "variable templates only available with" "" { target c++11_down } .-1 } +} + +struct A trivially_relocatable_if_eligible { A (A &&); A &operator= (A &&); ~A (); int a; }; + +static_assert (std::is_trivially_relocatable_v <A>, ""); +static_assert (std::is_nothrow_relocatable_v <A>, ""); + +struct B { B (B &&); B &operator= (B &&); ~B (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <B>, ""); +static_assert (!std::is_nothrow_relocatable_v <B>, ""); + +struct C trivially_relocatable_if_eligible : public A { C (C &&); C &operator= (C &&); ~C (); int a; }; + +static_assert (std::is_trivially_relocatable_v <C>, ""); +static_assert (std::is_nothrow_relocatable_v <C>, ""); + +struct D trivially_relocatable_if_eligible : public B { D (D &&); D &operator= (D &&); ~D (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <D>, ""); +static_assert (!std::is_nothrow_relocatable_v <D>, ""); + +struct E trivially_relocatable_if_eligible { E (E &&); E &operator= (E &&); ~E (); A a; }; + +static_assert (std::is_trivially_relocatable_v <E>, ""); +static_assert (std::is_nothrow_relocatable_v <E>, ""); + +struct F trivially_relocatable_if_eligible { F (F &&) noexcept; F &operator= (F &&); ~F (); B a; }; + +static_assert (!std::is_trivially_relocatable_v <F>, ""); +static_assert (std::is_nothrow_relocatable_v <F>, ""); + +struct G trivially_relocatable_if_eligible { G (G &&); G &operator= (G &&); ~G () = delete; int a; }; + +static_assert (!std::is_trivially_relocatable_v <G>, ""); +static_assert (!std::is_nothrow_relocatable_v <G>, ""); + +struct H trivially_relocatable_if_eligible : virtual A { H (H &&); H &operator= (H &&); ~H (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <H>, ""); +static_assert (!std::is_nothrow_relocatable_v <H>, ""); + +struct I trivially_relocatable_if_eligible { I (I &&); I &operator= (I &&); ~I (); A &a; int &b; }; + +static_assert (std::is_trivially_relocatable_v <I>, ""); +static_assert (std::is_nothrow_relocatable_v <I>, ""); + +struct J trivially_relocatable_if_eligible { J (J &&); J &operator= (J &&); ~J (); B &a; int &b; }; + +static_assert (std::is_trivially_relocatable_v <J>, ""); +static_assert (std::is_nothrow_relocatable_v <J>, ""); + +struct K trivially_relocatable_if_eligible { K (K &&) noexcept; K &operator= (K &&); ~K (); union { A a; int b; char c; }; }; + +static_assert (!std::is_trivially_relocatable_v <K>, ""); +static_assert (std::is_nothrow_relocatable_v <K>, ""); + +struct L trivially_relocatable_if_eligible { L (L &&); L &operator= (L &&); ~L (); union { int a; B b; short c; }; }; + +static_assert (!std::is_trivially_relocatable_v <L>, ""); +static_assert (!std::is_nothrow_relocatable_v <L>, ""); + +struct M trivially_relocatable_if_eligible { M (M &&); M &operator= (M &&); ~M () = default; int a; }; + +static_assert (std::is_trivially_relocatable_v <M>, ""); +static_assert (std::is_nothrow_relocatable_v <M>, ""); + +struct N trivially_relocatable_if_eligible { N (N &&); N &operator= (N &&); ~N (); union { M a; int b; char c; }; }; + +static_assert (std::is_trivially_relocatable_v <N>, ""); +static_assert (std::is_nothrow_relocatable_v <N>, ""); + +struct O trivially_relocatable_if_eligible { O (O &&); O &operator= (O &&); ~O (); union { unsigned long long a; int b; char c; }; }; + +static_assert (std::is_trivially_relocatable_v <O>, ""); +static_assert (std::is_nothrow_relocatable_v <O>, ""); + +struct P { P (P &&) noexcept; P &operator= (P &&); ~P (); int a; }; + +static_assert (!std::is_trivially_relocatable_v <P>, ""); +static_assert (std::is_nothrow_relocatable_v <P>, ""); diff --git a/gcc/testsuite/g++.dg/lto/pr114790_0.C b/gcc/testsuite/g++.dg/lto/pr114790_0.C new file mode 100644 index 0000000..eed112d --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr114790_0.C @@ -0,0 +1,16 @@ +// { dg-lto-do link } +// { dg-lto-options { { -w -flto -g -flto-partition=1to1 -O2 -shared -fPIC -fvisibility=hidden} } } +// { dg-require-effective-target fpic } +// { dg-require-effective-target shared } +struct APITracerContext { + virtual ~APITracerContext() = default; + virtual void releaseActivetracersList() = 0; +}; +struct APITracerContextImp : APITracerContext { + ~APITracerContextImp() override; + void releaseActivetracersList() override; +}; +struct APITracerContextImp globalAPITracerContextImp; +struct APITracerContextImp *pGlobalAPITracerContextImp = &globalAPITracerContextImp; +APITracerContextImp::~APITracerContextImp() {} + diff --git a/gcc/testsuite/g++.dg/lto/pr114790_1.C b/gcc/testsuite/g++.dg/lto/pr114790_1.C new file mode 100644 index 0000000..511fae4 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr114790_1.C @@ -0,0 +1,15 @@ +struct APITracerContext { + virtual void releaseActivetracersList() = 0; +}; +extern struct APITracerContextImp *pGlobalAPITracerContextImp; +struct APITracerContextImp : APITracerContext { void releaseActivetracersList();}; +int g(); +inline int +apiTracerWrapperImp( ) { + for (int i = 0; i < g(); i++) + pGlobalAPITracerContextImp->releaseActivetracersList(); +} +__attribute__((visibility("default"))) int +zeCommandListAppendMemoryCopyTracing() { + return apiTracerWrapperImp( ); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py index d2967d4..d92af83 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1-sarif.py @@ -6,7 +6,7 @@ import pytest def sarif(): return sarif_from_env() -def test_xml_state(sarif): +def test_state_graph(sarif): result = get_result_by_index(sarif, 0) assert result['level'] == 'warning' @@ -17,16 +17,49 @@ def test_xml_state(sarif): # Event "(1)": "entry to 'test'" (index == 0) assert events[0]['location']['message']['text'] == "entry to 'test'" - state0 = get_xml_state(events, 0) - memory_regions = state0.find('memory-regions') - assert memory_regions is not None - stack = memory_regions.find('stack') - assert stack is not None - frame = stack.find('stack-frame') - assert frame.get('function') == 'test' + state0 = get_state_graph(events, 0) + + stack = state0['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' + + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' # Final event: assert events[-1]['location']['message']['text'].startswith("use after 'free' of ") - state = get_xml_state(events, -1) - # TODO + state = get_state_graph(events, -1) + + stack = state['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' + + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' + + heap = state['nodes'][1] + assert heap['id'] == 'heap' + assert get_state_node_kind(heap) == 'heap' + + assert len(heap['children']) == 3 + heap_buffer0 = heap['children'][0] + assert heap_buffer0['id'].startswith('heap-allocated-region-') + assert get_state_node_kind(heap_buffer0) == 'dynalloc-buffer' + + globals_ = state['nodes'][2] + assert globals_['id'] == 'globals' + assert get_state_node_kind(globals_) == 'globals' + first = globals_['children'][0] + assert first['id'].startswith('decl-region-') + assert get_state_node_kind(first) == 'variable' + assert get_state_node_name(first) == 'first' + assert get_state_node_type(first) == 'struct node *' + + assert len(state['edges']) == 3 diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c index 3d853d2..3b35cfa 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-1.c @@ -1,4 +1,4 @@ -/* { dg-additional-options "-fdiagnostics-add-output=sarif:xml-state=yes" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:state-graphs=yes" } */ #include "analyzer-decls.h" diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c index 28cf580..b981cf9 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.c @@ -1,4 +1,4 @@ -/* { dg-additional-options "-fdiagnostics-add-output=sarif:xml-state=yes" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:state-graphs=yes" } */ #include "analyzer-decls.h" diff --git a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py index 484da96..3a2c6f8 100644 --- a/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py +++ b/gcc/testsuite/gcc.dg/analyzer/state-diagram-5-sarif.py @@ -1,5 +1,3 @@ -import xml.etree.ElementTree as ET - from sarif import * import pytest @@ -8,7 +6,7 @@ import pytest def sarif(): return sarif_from_env() -def test_nested_types_in_xml_state(sarif): +def test_nested_types_in_state_graph(sarif): result = get_result_by_index(sarif, 0) assert result['level'] == 'note' @@ -16,16 +14,17 @@ def test_nested_types_in_xml_state(sarif): events = result["codeFlows"][0]["threadFlows"][0]['locations'] assert events[0]['location']['message']['text'] == 'here' - state = get_xml_state(events, 0) - - memory_regions = state.find('memory-regions') - assert memory_regions is not None + state = get_state_graph(events, 0) - stack = memory_regions.find('stack') - assert stack is not None + stack = state['nodes'][0] + assert stack['id'] == 'stack' + assert get_state_node_kind(stack) == 'stack' - frame = stack.find('stack-frame') - assert frame.get('function') == 'test' + frame = stack['children'][0] + assert frame['id'].startswith('frame-region-') + assert get_state_node_kind(frame) == 'stack-frame' + assert get_state_node_attr(frame, 'function') == 'test' + assert frame['location']['logicalLocations'][0]['fullyQualifiedName'] == 'test' # We have: # baz_arr[1].m_bars[1].m_foos[2].m_ints[1] = 42; @@ -34,40 +33,57 @@ def test_nested_types_in_xml_state(sarif): # representation to nested elements and fields. # "baz_arr": - baz_arr = frame.find("variable[@name='baz_arr']") - assert baz_arr.get('type') == 'struct baz[2]' + baz_arr = frame['children'][0] + assert get_state_node_kind(baz_arr) == 'variable' + assert get_state_node_type(baz_arr) == 'struct baz[2]' + + assert len(baz_arr['children']) == 2 + + bindings = baz_arr['children'][0] + assert bindings['id'].startswith('concrete-bindings-') + assert get_state_node_kind(bindings) == 'other' + assert get_state_node_value(bindings['children'][0]) == '(int)42' # "baz_arr[1]": - baz_arr_1 = baz_arr.find("element[@index='1']") - assert baz_arr_1.get('type') == 'struct baz' + baz_arr_1 = baz_arr['children'][1] + assert get_state_node_type(baz_arr_1) == 'struct baz' + assert get_state_node_kind(baz_arr_1) == 'element' + assert get_state_node_attr(baz_arr_1, 'index') == '1' - assert baz_arr.find("element[@index='0']") is None + assert len(baz_arr_1['children']) == 1 # "baz_arr[1].m_bars": - baz_arr_1_m_bars = baz_arr_1.find("field[@name='m_bars']") - assert baz_arr_1_m_bars.get('type') == 'struct bar[2]' + baz_arr_1_m_bars = baz_arr_1['children'][0] + assert get_state_node_name(baz_arr_1_m_bars) == 'm_bars' + assert get_state_node_type(baz_arr_1_m_bars) == 'struct bar[2]' # "baz_arr[1].m_bars[1]" - baz_arr_1_m_bars_1 = baz_arr_1_m_bars.find("element[@index='1']") - assert baz_arr_1_m_bars_1.get('type') == 'struct bar' + baz_arr_1_m_bars_1 = baz_arr_1_m_bars['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1) == 'struct bar' + assert get_state_node_kind(baz_arr_1_m_bars_1) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1, 'index') == '1' # "baz_arr[1].m_bars[1].m_foos" - baz_arr_1_m_bars_1_m_foos = baz_arr_1_m_bars_1.find("field[@name='m_foos']") - assert baz_arr_1_m_bars_1_m_foos.get('type') == 'struct foo[3]' + baz_arr_1_m_bars_1_m_foos = baz_arr_1_m_bars_1['children'][0] + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos) == 'field' + assert get_state_node_name(baz_arr_1_m_bars_1_m_foos) == 'm_foos' + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos) == 'struct foo[3]' # "baz_arr[1].m_bars[1].m_foos[2]" - baz_arr_1_m_bars_1_m_foos_2 = baz_arr_1_m_bars_1_m_foos.find("element[@index='2']") - assert baz_arr_1_m_bars_1_m_foos_2.get('type') == 'struct foo' - + baz_arr_1_m_bars_1_m_foos_2 = baz_arr_1_m_bars_1_m_foos['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2) == 'struct foo' + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1_m_foos_2, 'index') == '2' + # "baz_arr[1].m_bars[1].m_foos[2].m_ints" - baz_arr_1_m_bars_1_m_foos_2_m_ints = baz_arr_1_m_bars_1_m_foos_2.find('field[@name="m_ints"]') - assert baz_arr_1_m_bars_1_m_foos_2_m_ints.get('type') == 'int[4]' - - # "baz_arr[1].m_bars[1].m_foos[2].m_ints[1]" - baz_arr_1_m_bars_1_m_foos_2_m_ints_1 = baz_arr_1_m_bars_1_m_foos_2_m_ints.find('element[@index="1"]') - assert baz_arr_1_m_bars_1_m_foos_2_m_ints_1.get('type') == 'int' + baz_arr_1_m_bars_1_m_foos_2_m_ints = baz_arr_1_m_bars_1_m_foos_2['children'][0] + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'field' + assert get_state_node_name(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'm_ints' + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2_m_ints) == 'int[4]' - value = baz_arr_1_m_bars_1_m_foos_2_m_ints_1.find('value-of-region') - constant = value.find('constant') - assert constant.get('value') == '42' - assert constant.get('type') == 'int' + # "baz_arr[1].m_bars[1].m_foos[2].m_ints[1]" + baz_arr_1_m_bars_1_m_foos_2_m_ints_1 = baz_arr_1_m_bars_1_m_foos_2_m_ints['children'][0] + assert get_state_node_type(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == 'int' + assert get_state_node_kind(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == 'element' + assert get_state_node_attr(baz_arr_1_m_bars_1_m_foos_2_m_ints_1, 'index') == '1' + assert get_state_node_value(baz_arr_1_m_bars_1_m_foos_2_m_ints_1) == '(int)42' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c new file mode 100644 index 0000000..2256a63 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} + +/* Use a Python script to verify various properties about the generated + HTML file: + { dg-final { run-html-pytest diagnostic-test-graphs-html.c "diagnostic-test-graphs-html.py" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py new file mode 100644 index 0000000..11e5fd1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-html.py @@ -0,0 +1,48 @@ +# Verify that metadata works in HTML output. + +from htmltest import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def html_tree(): + return html_tree_from_env() + +def test_result_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + diag_list = body.find('xhtml:div', ns) + assert diag_list is not None + assert diag_list.attrib['class'] == 'gcc-diagnostic-list' + + diag = diag_list.find('xhtml:div', ns) + assert diag is not None + + message = diag.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-0-message' + + assert message[0].tag == make_tag('strong') + assert message[0].tail == ' this is a placeholder error, with graphs ' + + graph = diag.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'foo' + +def test_run_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + graph = body.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'Optimization Passes' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c new file mode 100644 index 0000000..4170f51 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-add-output=sarif" } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest diagnostic-test-graphs-sarif.c "diagnostic-test-graphs-sarif.py" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py new file mode 100644 index 0000000..4bb7535 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.py @@ -0,0 +1,55 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_basics(sarif): + schema = sarif['$schema'] + assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json" + + version = sarif['version'] + assert version == "2.1.0" + +def test_result_graph(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + assert len(results) == 1 + + result = results[0] + assert result['level'] == 'error' + assert result['message']['text'] == "this is a placeholder error, with graphs" + + assert len(result['graphs']) == 2 + + assert result['graphs'][0]['description']['text'] == 'foo' + + assert len(result['graphs'][0]['nodes']) == 2 + assert result['graphs'][0]['nodes'][0]['id'] == 'a' + assert result['graphs'][0]['nodes'][1]['id'] == 'b' + assert result['graphs'][0]['nodes'][1]['properties']['/placeholder-prefix/color'] == 'red' + assert len(result['graphs'][0]['nodes'][1]['children']) == 1 + assert result['graphs'][0]['nodes'][1]['children'][0]['id'] == 'c' + assert result['graphs'][0]['nodes'][1]['children'][0]['label']['text'] == 'I am a node label' + + assert len(result['graphs'][0]['edges']) == 1 + result['graphs'][0]['edges'][0]['id'] == 'my-edge' + assert result['graphs'][0]['edges'][0]['label']['text'] == 'I am an edge label' + assert result['graphs'][0]['edges'][0]['sourceNodeId'] == 'a' + assert result['graphs'][0]['edges'][0]['targetNodeId'] == 'c' + + assert result['graphs'][1]['description']['text'] == 'bar' + +def test_run_graph(sarif): + runs = sarif['runs'] + run = runs[0] + + assert len(run['graphs']) == 1 + + assert run['graphs'][0]['description']['text'] == 'Optimization Passes' + assert run['graphs'][0]['nodes'][0]['id'] == 'all_lowering_passes' + assert run['graphs'][0]['edges'][0]['id'] == 'edge0' diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c new file mode 100644 index 0000000..7973954 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ + +extern void here (void); + +void test_graphs (void) +{ + here (); /* { dg-error "this is a placeholder error, with graphs" } */ +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc new file mode 100644 index 0000000..d432161 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_graphs.cc @@ -0,0 +1,283 @@ +/* This plugin exercises diagnostic graphs. + We emit an error with a pair of digraphs associated with it, + and a global digraph showing the optimization passes. */ + +#define INCLUDE_MAP +#define INCLUDE_STRING +#define INCLUDE_VECTOR +#include "gcc-plugin.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "stringpool.h" +#include "toplev.h" +#include "basic-block.h" +#include "hash-table.h" +#include "vec.h" +#include "ggc.h" +#include "basic-block.h" +#include "tree-ssa-alias.h" +#include "internal-fn.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-expr.h" +#include "is-a.h" +#include "tree.h" +#include "tree-pass.h" +#include "intl.h" +#include "plugin-version.h" +#include "diagnostic.h" +#include "context.h" +#include "gcc-rich-location.h" +#include "diagnostic-metadata.h" +#include "diagnostic-digraphs.h" +#include "pass_manager.h" + + +int plugin_is_GPL_compatible; + +const pass_data pass_data_test_graph_emission = +{ + GIMPLE_PASS, /* type */ + "test_graph_emission", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_NONE, /* tv_id */ + PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_test_graph_emission : public gimple_opt_pass +{ +public: + pass_test_graph_emission(gcc::context *ctxt) + : gimple_opt_pass(pass_data_test_graph_emission, ctxt) + {} + + /* opt_pass methods: */ + bool gate (function *) { return true; } + virtual unsigned int execute (function *); + +}; // class pass_test_graph_emission + +/* Determine if STMT is a call with NUM_ARGS arguments to a function + named FUNCNAME. + If so, return STMT as a gcall *. Otherwise return NULL. */ + +static gcall * +check_for_named_call (gimple *stmt, + const char *funcname, unsigned int num_args) +{ + gcc_assert (funcname); + + gcall *call = dyn_cast <gcall *> (stmt); + if (!call) + return NULL; + + tree fndecl = gimple_call_fndecl (call); + if (!fndecl) + return NULL; + + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname)) + return NULL; + + if (gimple_call_num_args (call) != num_args) + { + error_at (stmt->location, "expected number of args: %i (got %i)", + num_args, gimple_call_num_args (call)); + return NULL; + } + + return call; +} + +class lazy_passes_graph : public diagnostics::digraphs::lazy_digraph +{ +public: + lazy_passes_graph (const ::gcc::pass_manager &pass_manager_) + : m_pass_manager (pass_manager_) + { + } + + std::unique_ptr<diagnostics::digraphs::digraph> + create_digraph () const final override + { + auto g = std::make_unique<diagnostics::digraphs::digraph> (); + g->set_description ("Optimization Passes"); + +#define DEF_PASS_LIST(NAME) \ + add_top_level_pass_list (*g, #NAME, m_pass_manager.NAME); + + GCC_PASS_LISTS + +#undef DEF_PASS_LIST + + return g; + } + + void + add_top_level_pass_list (diagnostics::digraphs::digraph &g, + const char *pass_list_name, + const opt_pass *p) const + { + gcc_assert (p); + auto n = std::make_unique<diagnostics::digraphs::node> (g, pass_list_name); + n->set_label (pass_list_name); + add_child_pass (g, *n, *p); + g.add_node (std::move (n)); + } + + diagnostics::digraphs::node & + add_child_pass (diagnostics::digraphs::digraph &g, + diagnostics::digraphs::node &parent_node, + const opt_pass &p) const + { + std::string node_label; + std::string node_id; + if (p.static_pass_number > 0 ) + { + node_label = std::to_string (p.static_pass_number) + "_" + p.name; + node_id = node_label; + } + else + { + node_label = std::string (p.name); + pretty_printer pp; + pp_printf (&pp, "%s_%p", p.name, &p); + node_id = pp_formatted_text (&pp); + } + auto n + = std::make_unique<diagnostics::digraphs::node> (g, + std::move (node_id)); + n->set_label (node_label.c_str ()); + diagnostics::digraphs::node &result = *n; + parent_node.add_child (std::move (n)); + + // TODO: add attrs for things like type, properties_*, etc + + if (p.sub) + { + auto &other_node = add_child_pass (g, parent_node, *p.sub); + g.add_edge (nullptr, result, other_node, "sub"); + } + + if (p.next) + { + auto &other_node = add_child_pass (g, parent_node, *p.next); + g.add_edge (nullptr, result, other_node, "next"); + } + + return result; + } + +private: + const ::gcc::pass_manager &m_pass_manager; +}; + +static void +report_diag_with_graphs (location_t loc) +{ + class my_lazy_digraphs : public diagnostics::digraphs::lazy_digraphs + { + public: + using diagnostic_graph = diagnostics::digraphs::digraph; + using diagnostic_node = diagnostics::digraphs::node; + using diagnostic_edge = diagnostics::digraphs::edge; + + std::unique_ptr<std::vector<std::unique_ptr<diagnostic_graph>>> + create_digraphs () const final override + { + auto graphs + = std::make_unique<std::vector<std::unique_ptr<diagnostic_graph>>> (); + + graphs->push_back (make_test_graph ("foo")); + graphs->push_back (make_test_graph ("bar")); + + return graphs; + } + + private: + std::unique_ptr<diagnostic_graph> + make_test_graph (const char *desc) const + { + auto g = std::make_unique<diagnostic_graph> (); + 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 + auto c = std::make_unique<diagnostic_node> (*g, "c"); + c->set_label ("I am a node label"); + + auto e = std::make_unique<diagnostic_edge> (*g, "my-edge", *a, *c); + e->set_label ("I am an edge label"); + g->add_edge (std::move (e)); + + g->add_node (std::move (a)); + + b->add_child (std::move (c)); + g->add_node (std::move (b)); + + return g; + } + }; + + gcc_rich_location rich_loc (loc); + diagnostic_metadata meta; + my_lazy_digraphs ldg; + meta.set_lazy_digraphs (&ldg); + error_meta (&rich_loc, meta, + "this is a placeholder error, with graphs"); +} + +/* Exercise diagnostic graph emission. */ + +unsigned int +pass_test_graph_emission::execute (function *fun) +{ + gimple_stmt_iterator gsi; + basic_block bb; + + FOR_EACH_BB_FN (bb, fun) + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + if (gcall *call = check_for_named_call (stmt, "here", 0)) + report_diag_with_graphs (gimple_location (call)); + } + + return 0; +} + +int +plugin_init (struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + struct register_pass_info pass_info; + const char *plugin_name = plugin_info->base_name; + int argc = plugin_info->argc; + struct plugin_argument *argv = plugin_info->argv; + + if (!plugin_default_version_check (version, &gcc_version)) + return 1; + + pass_info.pass = new pass_test_graph_emission (g); + pass_info.reference_pass_name = "ssa"; + pass_info.ref_pass_instance_number = 1; + pass_info.pos_op = PASS_POS_INSERT_AFTER; + register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &pass_info); + + gcc_assert (::g->get_passes ()); + global_dc->report_global_digraph (lazy_passes_graph (*::g->get_passes ())); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index d1d7f5d..ce25c0a 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -107,6 +107,10 @@ set plugin_test_list [list \ diagnostic-test-metadata.c \ diagnostic-test-metadata-html.c \ diagnostic-test-metadata-sarif.c } \ + { diagnostic_plugin_test_graphs.cc + diagnostic-test-graphs.c \ + diagnostic-test-graphs-html.c \ + diagnostic-test-graphs-sarif.c } \ { diagnostic_plugin_test_nesting.cc \ diagnostic-test-nesting-text-plain.c \ diagnostic-test-nesting-text-indented.c \ diff --git a/gcc/testsuite/gcc.dg/pr87600-1.c b/gcc/testsuite/gcc.dg/pr87600-1.c index 3517957..9d74cad 100644 --- a/gcc/testsuite/gcc.dg/pr87600-1.c +++ b/gcc/testsuite/gcc.dg/pr87600-1.c @@ -1,5 +1,5 @@ /* PR rtl-optimization/87600 */ -/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* loongarch*-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ /* { dg-options "-O2" } */ #include "pr87600.h" diff --git a/gcc/testsuite/gcc.dg/pr87600-2.c b/gcc/testsuite/gcc.dg/pr87600-2.c index e8a9f19..808ebe1 100644 --- a/gcc/testsuite/gcc.dg/pr87600-2.c +++ b/gcc/testsuite/gcc.dg/pr87600-2.c @@ -1,5 +1,5 @@ /* PR rtl-optimization/87600 */ -/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ +/* { dg-do compile { target aarch64*-*-* arm*-*-* i?86-*-* loongarch*-*-* powerpc*-*-* s390*-*-* x86_64-*-* } } */ /* { dg-options "-O2" } */ #include "pr87600.h" diff --git a/gcc/testsuite/gcc.dg/pr87600.h b/gcc/testsuite/gcc.dg/pr87600.h index c89071eb..29f065e 100644 --- a/gcc/testsuite/gcc.dg/pr87600.h +++ b/gcc/testsuite/gcc.dg/pr87600.h @@ -7,6 +7,9 @@ #elif defined (__i386__) # define REG1 "%eax" # define REG2 "%edx" +#elif defined (__loongarch__) +# define REG1 "$t0" +# define REG2 "$t1" #elif defined (__powerpc__) || defined (__POWERPC__) || defined (__PPC__) # define REG1 "r3" # define REG2 "r4" diff --git a/gcc/testsuite/gcc.dg/vect/pr121034.c b/gcc/testsuite/gcc.dg/vect/pr121034.c new file mode 100644 index 0000000..de20781 --- /dev/null +++ b/gcc/testsuite/gcc.dg/vect/pr121034.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O3" } */ + +int b, e; +char c, d; +unsigned g; +int abs(int); +void f() { + char *a = &d; + int h; + for (; e; e++) { + h = 0; + for (; h < 16; h++) + g += __builtin_abs(a[h] - c); + a += b; + } +} diff --git a/gcc/testsuite/gcc.target/aarch64/simd/bcax_d.c b/gcc/testsuite/gcc.target/aarch64/simd/bcax_d.c new file mode 100644 index 0000000..a7640c3 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/simd/bcax_d.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include <arm_neon.h> + +#pragma GCC target "+sha3" + +#define BCAX(x,y,z) ((x) ^ ((y) & ~(z))) + +/* When the inputs come from GP regs don't form a BCAX. */ +uint64_t bcax_d_gp (uint64_t a, uint64_t b, uint64_t c) { return BCAX (a, b, c); } + +uint64x1_t bcax_d (uint64x1_t a, uint64x1_t b, uint64x1_t c) { return BCAX (a, b, c); } +uint32x2_t bcax_s (uint32x2_t a, uint32x2_t b, uint32x2_t c) { return BCAX (a, b, c); } +uint16x4_t bcax_h (uint16x4_t a, uint16x4_t b, uint16x4_t c) { return BCAX (a, b, c); } +uint8x8_t bcax_b (uint8x8_t a, uint8x8_t b, uint8x8_t c) { return BCAX (a, b, c); } + +/* { dg-final { scan-assembler-times {bcax\tv0.16b, v0.16b, v1.16b, v2.16b} 4 } } */ + diff --git a/gcc/testsuite/gcc.target/aarch64/simd/eor3_d.c b/gcc/testsuite/gcc.target/aarch64/simd/eor3_d.c new file mode 100644 index 0000000..6c9595b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/simd/eor3_d.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#include <arm_neon.h> + +#pragma GCC target "+sha3" + +#define EOR3(x,y,z) ((x) ^ (y) ^ (z)) + +/* Should not use EOR3 when inputs come from GP regs. */ +uint64_t eor3_d_gp (uint64_t a, uint64_t b, uint64_t c) { return EOR3 (a, b, c); } + +uint64x1_t eor3_d (uint64x1_t a, uint64x1_t b, uint64x1_t c) { return EOR3 (a, b, c); } +uint32x2_t bcax_s (uint32x2_t a, uint32x2_t b, uint32x2_t c) { return EOR3 (a, b, c); } +uint16x4_t bcax_h (uint16x4_t a, uint16x4_t b, uint16x4_t c) { return EOR3 (a, b, c); } +uint8x8_t bcax_b (uint8x8_t a, uint8x8_t b, uint8x8_t c) { return EOR3 (a, b, c); } + +/* { dg-final { scan-assembler-times {eor3\tv0.16b, v[0-9]+.16b, v[0-9]+.16b, v[0-9]+.16b} 4 } } */ + diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/perm_1.c b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/perm_1.c new file mode 100644 index 0000000..6b920b8 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/perm_1.c @@ -0,0 +1,14 @@ +/* { dg-options "-O2 -msve-vector-bits=256" } */ + +#include <arm_sve.h> +typedef svbfloat16_t vls_bfloat16_t __attribute__((arm_sve_vector_bits(32 * 8))); +svbfloat16_t foo(vls_bfloat16_t a, vls_bfloat16_t b) +{ + svbfloat16_t zero = svreinterpret_bf16_f32 (svdup_n_f32 (0.0f)); + return svzip2_bf16(zero, svuzp1_bf16(a,b)); +} + + +/* { dg-final { scan-assembler-times {\tuzp1\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tzip2\t} 1 } } */ +/* { dg-final { scan-assembler-not {\ttbl\t} } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr91384-1.c b/gcc/testsuite/gcc.target/i386/pr91384-1.c new file mode 100644 index 0000000..4f8823d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr91384-1.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -mapxf" } */ + +void foo (void); +void bar (void); + +int +test (int a) +{ + int r; + + if (r = -a) + foo (); + else + bar (); + + return r; +} + +/* { dg-final { scan-assembler-not "testl" } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/pr121007.c b/gcc/testsuite/gcc.target/powerpc/pr121007.c new file mode 100644 index 0000000..9e6b1be --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr121007.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mdejagnu-cpu=power9" } */ + +typedef struct { int a; } A; +unsigned char *a; +char b; +int c; +void foo (vector char, vector char, vector char); + +void +bar (long stride) +{ + vector char v0, v1, v2, v3, v5; + vector char r0 = __builtin_vec_vsx_ld (0, a); + vector char r2 = __builtin_vec_vsx_ld (2 * stride, a - 3); + vector char r3 = __builtin_vec_vsx_ld (3 * stride, a - 3); + vector char r4; + vector char r6 = __builtin_vec_vsx_ld (6 * stride, a - 3); + vector char r7 = __builtin_vec_vsx_ld (7 * stride, a - 3); + vector char r14, h, i, j; + if (b) + return; + v1 = __builtin_vec_vsx_ld (9 * stride, a); + v2 = __builtin_vec_vsx_ld (10 * stride, a - 3); + v3 = __builtin_vec_vsx_ld (11 * stride, a - 3); + r3 = __builtin_vec_mergeh (r3, v3); + v5 = __builtin_vec_mergel (r2, r6); + r14 = __builtin_vec_mergeh (r3, r7); + r4 = __builtin_vec_mergeh (v2, r14); + v0 = __builtin_vec_mergeh (r0, r4); + union { unsigned char a[16]; A b; } temp; + vector signed char k; + h = __builtin_vec_ld (0, temp.a); + i = __builtin_vec_splat (h, 1); + temp.b.a = c; + k = __builtin_vec_ld (0, (signed char *) temp.a); + j = __builtin_vec_and (i, (vector char) k); + foo (v1, v0, j); + foo (v1, v5, j); +} diff --git a/gcc/testsuite/gfortran.dg/goacc/parameter-3.f90 b/gcc/testsuite/gfortran.dg/goacc/parameter-3.f90 new file mode 100644 index 0000000..2c8aa61 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/goacc/parameter-3.f90 @@ -0,0 +1,16 @@ +! { dg-do compile } + +subroutine x + integer :: var + integer, parameter :: ilog = 0 + integer, parameter :: array(*) = [11,22,33] + !$ACC DECLARE COPYIN(ilog, array, var, array) ! { dg-error "Symbol 'array' present on multiple clauses" } +end subroutine x + +integer :: a +integer, parameter :: b = 4 +integer, parameter :: c(*) = [1,2,3] + +!$acc parallel copy(a,c,b,c) ! { dg-error "Symbol 'c' present on multiple clauses" } +!$acc end parallel +end diff --git a/gcc/testsuite/gfortran.dg/goacc/parameter-4.f90 b/gcc/testsuite/gfortran.dg/goacc/parameter-4.f90 new file mode 100644 index 0000000..aadd7cf --- /dev/null +++ b/gcc/testsuite/gfortran.dg/goacc/parameter-4.f90 @@ -0,0 +1,26 @@ +! { dg-do compile } +! { dg-additional-options "-fdump-tree-original" } + +subroutine x + integer :: var + integer, parameter :: ilog = 0 + integer, parameter :: array(*) = [11,22,33] + !$ACC DECLARE COPYIN(ilog, array, var) +end subroutine x + +integer :: a +integer, parameter :: b = 4 +integer, parameter :: c(*) = [1,2,3] + +!$acc parallel copy(a,c,b) + a = c(2) + b +!$acc end parallel + +!$acc parallel firstprivate(a,c,b) + a = c(2) + b +!$acc end parallel +end + +! { dg-final { scan-tree-dump-times "#pragma acc data map\\(to:var\\)" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma acc parallel map\\(tofrom:a\\)" 1 "original" } } +! { dg-final { scan-tree-dump-times "#pragma acc parallel firstprivate\\(a\\)" 1 "original" } } diff --git a/gcc/testsuite/gfortran.dg/goacc/parameter.f95 b/gcc/testsuite/gfortran.dg/goacc/parameter.f95 index b581338..a9bde4a 100644 --- a/gcc/testsuite/gfortran.dg/goacc/parameter.f95 +++ b/gcc/testsuite/gfortran.dg/goacc/parameter.f95 @@ -1,4 +1,5 @@ ! { dg-do compile } +! { dg-additional-options "-Wsurprising" } module test contains @@ -6,37 +7,37 @@ contains implicit none integer :: i integer, parameter :: a = 1 - !$acc declare device_resident (a) ! { dg-error "is not a variable" } - !$acc data copy (a) ! { dg-error "not a variable" } + !$acc declare device_resident (a) ! (no warning here - for semi-good reasons) + !$acc data copy (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as parameters need not be copied \\\[-Wsurprising\\\]" } !$acc end data - !$acc data deviceptr (a) ! { dg-error "not a variable" } + !$acc data deviceptr (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as parameters need not be copied \\\[-Wsurprising\\\]" } !$acc end data - !$acc parallel private (a) ! { dg-error "not a variable" } + !$acc parallel private (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } !$acc end parallel - !$acc serial private (a) ! { dg-error "not a variable" } + !$acc serial private (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } !$acc end serial - !$acc host_data use_device (a) ! { dg-error "not a variable" } + !$acc host_data use_device (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } !$acc end host_data - !$acc parallel loop reduction(+:a) ! { dg-error "not a variable" } + !$acc parallel loop reduction(+:a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } do i = 1,5 enddo !$acc end parallel loop - !$acc serial loop reduction(+:a) ! { dg-error "not a variable" } + !$acc serial loop reduction(+:a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } do i = 1,5 enddo !$acc end serial loop !$acc parallel loop do i = 1,5 - !$acc cache (a) ! { dg-error "not a variable" } + !$acc cache (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } enddo !$acc end parallel loop !$acc serial loop do i = 1,5 - !$acc cache (a) ! { dg-error "not a variable" } + !$acc cache (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as it is a parameter \\\[-Wsurprising\\\]" } enddo !$acc end serial loop - !$acc update device (a) ! { dg-error "not a variable" } - !$acc update host (a) ! { dg-error "not a variable" } - !$acc update self (a) ! { dg-error "not a variable" } + !$acc update device (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as parameters need not be copied \\\[-Wsurprising\\\]" } + !$acc update host (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as parameters need not be copied \\\[-Wsurprising\\\]" } + !$acc update self (a) ! { dg-warning "Clause for object 'a' at .1. is ignored as parameters need not be copied \\\[-Wsurprising\\\]" } end subroutine oacc1 end module test diff --git a/gcc/testsuite/gfortran.dg/import12.f90 b/gcc/testsuite/gfortran.dg/import12.f90 new file mode 100644 index 0000000..df1aae6 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/import12.f90 @@ -0,0 +1,302 @@ +! { dg-do compile } +! +! Tests the variants of IMPORT introduced in F2018 +! +! Contributed by Paul Thomas <pault@gcc.gnu.org> +! +MODULE M + import, none ! { dg-error "F2018: C897 IMPORT statement" } + IMPLICIT NONE + integer :: z +end module + +MODULE N + IMPLICIT NONE + integer :: z +end module + +! Taken from gfortran.dg/pr103312.f90. These F2008-style invocations should +! be accepted. +module example + type, abstract :: foo + integer :: i + contains + procedure(foo_size), deferred :: size + procedure(foo_func), deferred :: func + end type + abstract interface + pure integer function foo_size (this) + import :: foo + class(foo), intent(in) :: this + end function + function foo_func (this) result (string) + import :: foo + class(foo) :: this + character(this%size()) :: string + end function + end interface +end module + +block data blk + import, all ! { dg-error "F2018: C897 IMPORT statement" } + integer a(2) + common /my_common/a + data a/1,2/ +end + +subroutine extern_sub1 + import ! { dg-error "F2018: C897 IMPORT statement" } +end + +subroutine extern_sub2 (arg1, arg2, arg3) + implicit none + integer :: arg1, arg2, arg3 + arg1 = int_fcn () +contains + integer function int_fcn () + import, only : arg2, arg3 + int_fcn = arg2 * arg3 + end +end + +program p + import, all ! { dg-error "F2018: C897 IMPORT statement" } + implicit none + integer :: x, y + type :: t + integer :: i + end type + type(t) :: progtype + type, extends(t) :: s + integer :: j + end type + class(t), allocatable :: progclass +contains + +! OK because arg is just that and x is declared in scope of sub1. + subroutine sub1 (arg) + import, none + implicit none + real :: arg, x + end + +! IMPORT, ALL must be the only IMPORT statement in the scope. + subroutine sub2 (arg) + import, none + import, all ! { dg-error "F2018: C8100 IMPORT statement" } + implicit none + real :: arg, x + end + +! Error message says it all. + subroutine sub3 (arg) + import, none + implicit none + integer :: arg + print *, arg + x = 1 ! { dg-error "F2018: C8102" } + end + +! Error messages say it all. + subroutine sub4 (arg) + import, only : y + implicit none + integer :: arg + print *, arg + x = 1 ! { dg-error "F2018: C8102" } + y = 2 + print *, x ! { dg-error "F2018: C8102" } + end + +! IMPORT eos and IMPORT, ALL must be unique in the scope. + subroutine sub5a (arg) + import, all + import ! { dg-error "F2018: C8100" } + implicit none + real :: arg + real :: x ! { dg-error "F2018: C8102" } + end + + subroutine sub5b (arg) + import, only : x + implicit none + real :: arg + real :: x ! { dg-error "F2018: C8102" } + end + +! Error message says it all. + integer function func1 () + import, only : x + func1 = x * y ! { dg-error "F2018: C8102" } + end + +! Error messages say it all. + subroutine sub6 (arg) + import, only : func1 + import, only : func2 + import, only : foobar ! { dg-error "has no IMPLICIT type" } + implicit none + integer :: arg + arg = func1 () * func2 () * func3 () ! { dg-error "F2018: C8102" } + end + +! Error message says it all. + integer function func2 () + use N + import, none + implicit none + func2 = y ! { dg-error "F2018: C8102" } + end + +! OK + integer function func3 () + func3 = 42 + end + + subroutine sub7 (arg) + implicit none + integer :: arg +! OK + block + import, only : arg, func1, func2, func3 + arg = func1 () * func2 () * func3 () + end block + block + arg = func1 () + import, only : arg, func1 ! { dg-error "Unexpected IMPORT statement" } + end block + end + +! Error messages say it all. + subroutine sub8 (arg) + implicit none + integer :: arg + block + import, only : func1 + import, only : func2 + import, only : foobar ! { dg-error "has no IMPLICIT type" } + arg = func1 () * func2 () * func3 () ! { dg-error "F2018: C8102" } + end block + end + +! ASSOCIATE does not have a specification part so IMPORT cannot appear. + subroutine sub9 (arg) + implicit none + integer :: arg + associate (f3 => func3 ()) ! { dg-error "F2018: C8102" } + import, only : arg, func1 ! { dg-error "Unexpected IMPORT statement" } + arg = func1 () * func2 () * f3 ! { dg-error "F2018: C8102" } + end associate + end + +! OK + subroutine sub10 (arg) + import, only : t + implicit none + type(t) :: arg, mytype + mytype%i = 1 + arg = mytype + end + +! TYPE t does not appear in the IMPORT list + subroutine sub11 (arg) + import, only : progtype + implicit none + type(t) :: arg + progtype%i = 1 ! { dg-error "F2018: C8102" } + arg = progtype ! { dg-error "F2018: C8102" } + end + +! TYPE t is excluded by IMPORT, NONE + subroutine sub12 (arg) + import, none + implicit none + type(t) :: arg, mytype + mytype%i = 1 ! { dg-error "F2018: C8102" } + arg = mytype ! { dg-error "F2018: C8102" } + end + +! TYPE t does not appear in the IMPORT list + subroutine sub13 (arg) + import, only : progclass + implicit none + class(t) :: arg + type(t) :: ca(2) = [t(1), t(2)] ! { dg-error "F2018: C8102" } + progclass%i = t(1) ! { dg-error "F2018: C8102" } + arg = progclass ! { dg-error "F2018: C8102" } + ca = [t(1), t(2)] ! { dg-error "has no IMPLICIT type|F2018: C8102" } + arg = ca(2) ! Note: The preceeding line catches 'ca' having no implicit type. + end + +! TYPE t is excluded by IMPORT, NONE + subroutine sub14 (arg) + import, none + implicit none + class(t) :: arg + class(t), allocatable :: myclass + myclass%i = t(1) ! { dg-error "F2018: C8102" } + arg%i = myclass%i ! { dg-error "F2018: C8102" } + select type (arg) ! { dg-error "F2018: C8102" } + type is (t) + arg%i = arg%i + 1 + type is (s) + arg%j = -1 + end select + end + +! TYPE s does not appear in the IMPORT, ONLY list + subroutine sub15 (arg) + import, only : t + implicit none + class(t) :: arg + class(t), allocatable :: myclass + myclass = t(1) + arg%i = myclass%i + select type (arg) ! { dg-error "F2018: C8102" } + type is (t) + arg%i = arg%i + 1 + type is (s) + arg%j = -1 ! s is caught at the SELECT TYPE statement + end select + end + +! This is OK + subroutine sub16 (arg) + import, only : t, s + implicit none + class(t) :: arg + class(t), allocatable :: myclass + myclass = t(1) + arg%i = myclass%i + select type (arg) + type is (t) + arg%i = arg%i + 1 + type is (s) + arg%j = -1 + end select + end + + subroutine sub17 (arg) + import, only : t + implicit none + class(t) :: arg + call sub16 (arg) ! { dg-error "F2018: C8102" } + end + +! Make sure that recursive procedures do not require the procedure itself to be imported. + recursive subroutine sub18 (arg) + import, none + implicit none + integer :: arg + if (arg <= 0) call sub18 (arg) + arg = 1 + end + + recursive integer function func4 (arg) result (res) + import, none + implicit none + integer :: arg + if (arg <= 0) arg = func4 (arg) + res = 1 + end +end diff --git a/gcc/testsuite/gfortran.dg/import3.f90 b/gcc/testsuite/gfortran.dg/import3.f90 index 74cd527..9288c6b 100644 --- a/gcc/testsuite/gfortran.dg/import3.f90 +++ b/gcc/testsuite/gfortran.dg/import3.f90 @@ -1,6 +1,8 @@ ! { dg-do compile } +! { dg-options "-std=f2008" } ! { dg-shouldfail "Invalid use of IMPORT" } ! Test invalid uses of import +! Wording of some error messages change for -std>=F2018 but all are caught. ! PR fortran/29601 subroutine test() diff --git a/gcc/testsuite/lib/sarif.py b/gcc/testsuite/lib/sarif.py index 384de2f..06d05c0 100644 --- a/gcc/testsuite/lib/sarif.py +++ b/gcc/testsuite/lib/sarif.py @@ -29,10 +29,24 @@ def get_result_by_index(sarif, idx): results = run['results'] return results[idx] -def get_xml_state(events, event_idx): - xml_src = events[event_idx]['properties']['gcc/diagnostic_event/xml_state'] +def get_state_graph(events, event_idx): + graph = events[event_idx]['properties']['gcc/diagnostic_event/state_graph'] if 0: - print(xml_src) - xml = ET.fromstring(xml_src) - assert xml.tag == 'state-diagram' - return xml + print(graph) + assert graph is not None + return graph + +def get_state_node_attr(obj, attr_name): + return obj['properties']['gcc/diagnostic_state_node/%s' % attr_name] + +def get_state_node_kind(obj): + return get_state_node_attr(obj, 'kind') + +def get_state_node_name(obj): + return get_state_node_attr(obj, 'name') + +def get_state_node_type(obj): + return get_state_node_attr(obj, 'type') + +def get_state_node_value(obj): + return get_state_node_attr(obj, 'value') diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif new file mode 100644 index 0000000..a4eb9d4 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.40.2-duplicate-node-id.sarif @@ -0,0 +1,23 @@ +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": { "driver": { "name": "example" } }, + "results": [], + "graphs": [{"nodes": [{"id": "a", /* { dg-message "'a' already used as node id within graph here" } */ + "children": [{"id": "a"}]}], /* { dg-error "duplicate node id 'a' within graph \\\[SARIF v2.1.0 §3.40.2\\\]" } */ + "edges": []}]}]} + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/nodes/0/children/0/id': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 6 | "children": [{"id": "a"}]}], + | ^~~ + { dg-end-multiline-output "" } */ + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/nodes/0/id': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 5 | "graphs": [{"nodes": [{"id": "a", + | ^~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif new file mode 100644 index 0000000..b483346 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-invalid/3.41.4-unrecognized-node-id.sarif @@ -0,0 +1,16 @@ +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": { "driver": { "name": "example" } }, + "results": [], + "graphs": [{"nodes": [], + "edges": [{"id": "edge0", + "sourceNodeId": "this-does-not-exist", /* { dg-error "no node with id 'this-does-not-exist' in graph \\\[SARIF v2.1.0 §3.41.4\\\]" } */ + "targetNodeId": "neither-does-this"}]}]}]} + +/* { dg-begin-multiline-output "" } +In JSON property '/runs/0/graphs/0/edges/0/sourceNodeId': + { dg-end-multiline-output "" } */ +/* { dg-begin-multiline-output "" } + 7 | "sourceNodeId": "this-does-not-exist", + | ^~~~~~~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py new file mode 100644 index 0000000..63b80c9 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-html.py @@ -0,0 +1,46 @@ +from htmltest import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def html_tree(): + return html_tree_from_env() + +def test_result_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + diag_list = body.find('xhtml:div', ns) + assert diag_list is not None + assert diag_list.attrib['class'] == 'gcc-diagnostic-list' + + diag = diag_list.find('xhtml:div', ns) + assert diag is not None + + message = diag.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-0-message' + + assert message[0].tag == make_tag('strong') + assert message[0].tail == ' this is a placeholder error, with graphs ' + + graph = diag.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'foo' + +def test_run_graph(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + graph = body.find("./xhtml:div[@class='gcc-directed-graph']", ns) + assert graph is not None + + header = graph.find("./xhtml:h2", ns) + assert header.text == 'Optimization Passes' diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py new file mode 100644 index 0000000..4bb7535 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs-check-sarif-roundtrip.py @@ -0,0 +1,55 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_basics(sarif): + schema = sarif['$schema'] + assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json" + + version = sarif['version'] + assert version == "2.1.0" + +def test_result_graph(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + assert len(results) == 1 + + result = results[0] + assert result['level'] == 'error' + assert result['message']['text'] == "this is a placeholder error, with graphs" + + assert len(result['graphs']) == 2 + + assert result['graphs'][0]['description']['text'] == 'foo' + + assert len(result['graphs'][0]['nodes']) == 2 + assert result['graphs'][0]['nodes'][0]['id'] == 'a' + assert result['graphs'][0]['nodes'][1]['id'] == 'b' + assert result['graphs'][0]['nodes'][1]['properties']['/placeholder-prefix/color'] == 'red' + assert len(result['graphs'][0]['nodes'][1]['children']) == 1 + assert result['graphs'][0]['nodes'][1]['children'][0]['id'] == 'c' + assert result['graphs'][0]['nodes'][1]['children'][0]['label']['text'] == 'I am a node label' + + assert len(result['graphs'][0]['edges']) == 1 + result['graphs'][0]['edges'][0]['id'] == 'my-edge' + assert result['graphs'][0]['edges'][0]['label']['text'] == 'I am an edge label' + assert result['graphs'][0]['edges'][0]['sourceNodeId'] == 'a' + assert result['graphs'][0]['edges'][0]['targetNodeId'] == 'c' + + assert result['graphs'][1]['description']['text'] == 'bar' + +def test_run_graph(sarif): + runs = sarif['runs'] + run = runs[0] + + assert len(run['graphs']) == 1 + + assert run['graphs'][0]['description']['text'] == 'Optimization Passes' + assert run['graphs'][0]['nodes'][0]['id'] == 'all_lowering_passes' + assert run['graphs'][0]['edges'][0]['id'] == 'edge0' diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif new file mode 100644 index 0000000..da236ba --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/graphs.sarif @@ -0,0 +1,2445 @@ +/* Test a replay of a .sarif file generated from GCC testsuite. + + The dg directives were stripped out from the generated .sarif + to avoid confusing DejaGnu for this test. */ +/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:file=graphs.sarif.html,javascript=no" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=graphs.roundtrip.sarif" } */ + +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": {"driver": {"name": "GNU C23", + "fullName": "GNU C23 (GCC) version 16.0.0 20250702 (experimental) (x86_64-pc-linux-gnu)", + "version": "16.0.0 20250702 (experimental)", + "informationUri": "https://gcc.gnu.org/gcc-16/", + "rules": []}, + "extensions": [{"name": "diagnostic_plugin_test_graphs", + "fullName": "./diagnostic_plugin_test_graphs.so"}]}, + "invocations": [{"arguments": ["/home/david/gcc-newgit-gcc16/build/gcc/cc1", + "-quiet", + "-iprefix", + "/usr/local/lib/gcc/x86_64-pc-linux-gnu/16.0.0/", + "-isystem", + "/home/david/gcc-newgit-gcc16/build/gcc/include", + "-isystem", + "/home/david/gcc-newgit-gcc16/build/gcc/include-fixed", + "-iplugindir=/home/david/gcc-newgit-gcc16/build/gcc/plugin", + "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c", + "-iplugindir=/home/david/gcc-newgit-gcc16/build/gcc/plugin", + "-quiet", + "-dumpbase", + "diagnostic-test-graphs-sarif.c", + "-dumpbase-ext", + ".c", + "-mtune=generic", + "-march=x86-64", + "-fdiagnostics-color=never", + "-fdiagnostics-urls=never", + "-fno-diagnostics-show-caret", + "-fno-diagnostics-show-line-numbers", + "-fdiagnostics-path-format=separate-events", + "-fdiagnostics-text-art-charset=none", + "-fno-diagnostics-show-event-links", + "-fplugin=./diagnostic_plugin_test_graphs.so", + "-fdiagnostics-add-output=sarif", + "-o", + "diagnostic-test-graphs-sarif.s"], + "workingDirectory": {"uri": "/home/david/gcc-newgit-gcc16/build/gcc/testsuite/gcc"}, + "startTimeUtc": "2025-07-09T22:43:31Z", + "executionSuccessful": false, + "toolExecutionNotifications": [], + "endTimeUtc": "2025-07-09T22:43:31Z"}], + "artifacts": [{"location": {"uri": "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c"}, + "sourceLanguage": "c", + "roles": ["analysisTarget"]}], + "results": [{"ruleId": "error", + "level": "error", + "message": {"text": "this is a placeholder error, with graphs"}, + "locations": [{"physicalLocation": {"artifactLocation": {"uri": "/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 10}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " here ();"}}}, + "logicalLocations": [{"index": 0, + "fullyQualifiedName": "test_graphs"}]}], + "graphs": [{"description": {"text": "foo"}, + "nodes": [{"id": "a"}, + {"id": "b", + "properties": {"/placeholder-prefix/color": "red"}, + "children": [{"id": "c", + "label": {"text": "I am a node label"}}]}], + "edges": [{"id": "my-edge", + "label": {"text": "I am an edge label"}, + "sourceNodeId": "a", + "targetNodeId": "c"}]}, + {"description": {"text": "bar"}, + "nodes": [{"id": "a"}, + {"id": "b", + "properties": {"/placeholder-prefix/color": "red"}, + "children": [{"id": "c", + "label": {"text": "I am a node label"}}]}], + "edges": [{"id": "my-edge", + "label": {"text": "I am an edge label"}, + "sourceNodeId": "a", + "targetNodeId": "c"}]}]}], + "logicalLocations": [{"name": "test_graphs", + "fullyQualifiedName": "test_graphs", + "decoratedName": "test_graphs", + "kind": "function", + "index": 0}], + "graphs": [{"description": {"text": "Optimization Passes"}, + "nodes": [{"id": "all_lowering_passes", + "label": {"text": "all_lowering_passes"}, + "children": [{"id": "*warn_unused_result_0x101ef3d0", + "label": {"text": "*warn_unused_result"}}, + {"id": "*diagnose_omp_blocks_0x101ef430", + "label": {"text": "*diagnose_omp_blocks"}}, + {"id": "*diagnose_tm_blocks_0x101ef490", + "label": {"text": "*diagnose_tm_blocks"}}, + {"id": "13_omp_oacc_kernels_decompose", + "label": {"text": "13_omp_oacc_kernels_decompose"}}, + {"id": "14_omplower", + "label": {"text": "14_omplower"}}, + {"id": "15_lower", + "label": {"text": "15_lower"}}, + {"id": "16_tmlower", + "label": {"text": "16_tmlower"}}, + {"id": "17_ehopt", + "label": {"text": "17_ehopt"}}, + {"id": "18_eh", + "label": {"text": "18_eh"}}, + {"id": "19_coro-lower-builtins", + "label": {"text": "19_coro-lower-builtins"}}, + {"id": "20_cfg", + "label": {"text": "20_cfg"}}, + {"id": "*warn_function_return_0x101ef7f0", + "label": {"text": "*warn_function_return"}}, + {"id": "21_coro-early-expand-ifns", + "label": {"text": "21_coro-early-expand-ifns"}}, + {"id": "22_ompexp", + "label": {"text": "22_ompexp"}}, + {"id": "*build_cgraph_edges_0x101ef910", + "label": {"text": "*build_cgraph_edges"}}]}, + {"id": "all_small_ipa_passes", + "label": {"text": "all_small_ipa_passes"}, + "children": [{"id": "23_afdo_offline", + "label": {"text": "23_afdo_offline"}}, + {"id": "*free_lang_data_0x101ef9d0", + "label": {"text": "*free_lang_data"}}, + {"id": "24_visibility", + "label": {"text": "24_visibility"}}, + {"id": "25_strubm", + "label": {"text": "25_strubm"}}, + {"id": "26_build_ssa_passes", + "label": {"text": "26_build_ssa_passes"}}, + {"id": "27_fixup_cfg", + "label": {"text": "27_fixup_cfg"}}, + {"id": "28_ssa", + "label": {"text": "28_ssa"}}, + {"id": "376_test_graph_emission", + "label": {"text": "376_test_graph_emission"}}, + {"id": "29_walloca", + "label": {"text": "29_walloca"}}, + {"id": "30_warn-printf", + "label": {"text": "30_warn-printf"}}, + {"id": "*nonnullcmp_0x101efce0", + "label": {"text": "*nonnullcmp"}}, + {"id": "31_early_uninit", + "label": {"text": "31_early_uninit"}}, + {"id": "32_waccess", + "label": {"text": "32_waccess"}}, + {"id": "33_ubsan", + "label": {"text": "33_ubsan"}}, + {"id": "34_nothrow", + "label": {"text": "34_nothrow"}}, + {"id": "*rebuild_cgraph_edges_0x101f0020", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "35_opt_local_passes", + "label": {"text": "35_opt_local_passes"}}, + {"id": "36_fixup_cfg", + "label": {"text": "36_fixup_cfg"}}, + {"id": "*rebuild_cgraph_edges_0x101f0140", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "37_local-fnsummary", + "label": {"text": "37_local-fnsummary"}}, + {"id": "38_einline", + "label": {"text": "38_einline"}}, + {"id": "*infinite-recursion_0x101f0260", + "label": {"text": "*infinite-recursion"}}, + {"id": "39_early_optimizations", + "label": {"text": "39_early_optimizations"}}, + {"id": "*remove_cgraph_callee_edges_0x101f0340", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "40_early_objsz", + "label": {"text": "40_early_objsz"}}, + {"id": "41_ccp", + "label": {"text": "41_ccp"}}, + {"id": "42_forwprop", + "label": {"text": "42_forwprop"}}, + {"id": "43_ethread", + "label": {"text": "43_ethread"}}, + {"id": "44_esra", + "label": {"text": "44_esra"}}, + {"id": "45_ealias", + "label": {"text": "45_ealias"}}, + {"id": "46_phiprop", + "label": {"text": "46_phiprop"}}, + {"id": "47_fre", + "label": {"text": "47_fre"}}, + {"id": "48_evrp", + "label": {"text": "48_evrp"}}, + {"id": "49_mergephi", + "label": {"text": "49_mergephi"}}, + {"id": "50_dse", + "label": {"text": "50_dse"}}, + {"id": "51_cddce", + "label": {"text": "51_cddce"}}, + {"id": "52_phiopt", + "label": {"text": "52_phiopt"}}, + {"id": "53_tailr", + "label": {"text": "53_tailr"}}, + {"id": "54_iftoswitch", + "label": {"text": "54_iftoswitch"}}, + {"id": "55_switchconv", + "label": {"text": "55_switchconv"}}, + {"id": "56_ehcleanup", + "label": {"text": "56_ehcleanup"}}, + {"id": "57_sccopy", + "label": {"text": "57_sccopy"}}, + {"id": "58_profile_estimate", + "label": {"text": "58_profile_estimate"}}, + {"id": "59_local-pure-const", + "label": {"text": "59_local-pure-const"}}, + {"id": "60_modref", + "label": {"text": "60_modref"}}, + {"id": "61_fnsplit", + "label": {"text": "61_fnsplit"}}, + {"id": "*strip_predict_hints_0x101f0c60", + "label": {"text": "*strip_predict_hints"}}, + {"id": "62_release_ssa", + "label": {"text": "62_release_ssa"}}, + {"id": "*rebuild_cgraph_edges_0x101f0d30", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "63_local-fnsummary", + "label": {"text": "63_local-fnsummary"}}, + {"id": "64_remove_symbols", + "label": {"text": "64_remove_symbols"}}, + {"id": "65_strub", + "label": {"text": "65_strub"}}, + {"id": "66_ipa_oacc", + "label": {"text": "66_ipa_oacc"}}, + {"id": "67_pta", + "label": {"text": "67_pta"}}, + {"id": "68_ipa_oacc_kernels", + "label": {"text": "68_ipa_oacc_kernels"}}, + {"id": "69_oacc_kernels", + "label": {"text": "69_oacc_kernels"}}, + {"id": "70_ch", + "label": {"text": "70_ch"}}, + {"id": "71_fre", + "label": {"text": "71_fre"}}, + {"id": "72_lim", + "label": {"text": "72_lim"}}, + {"id": "73_dom", + "label": {"text": "73_dom"}}, + {"id": "74_dce", + "label": {"text": "74_dce"}}, + {"id": "75_parloops", + "label": {"text": "75_parloops"}}, + {"id": "76_ompexpssa", + "label": {"text": "76_ompexpssa"}}, + {"id": "*rebuild_cgraph_edges_0x101f1310", + "label": {"text": "*rebuild_cgraph_edges"}}, + {"id": "77_targetclone", + "label": {"text": "77_targetclone"}}, + {"id": "78_afdo", + "label": {"text": "78_afdo"}}, + {"id": "79_feedback_fnsplit", + "label": {"text": "79_feedback_fnsplit"}}, + {"id": "80_profile", + "label": {"text": "80_profile"}}, + {"id": "81_feedback_fnsplit", + "label": {"text": "81_feedback_fnsplit"}}, + {"id": "82_free-fnsummary", + "label": {"text": "82_free-fnsummary"}}, + {"id": "83_increase_alignment", + "label": {"text": "83_increase_alignment"}}, + {"id": "84_tmipa", + "label": {"text": "84_tmipa"}}, + {"id": "85_emutls", + "label": {"text": "85_emutls"}}]}, + {"id": "all_regular_ipa_passes", + "label": {"text": "all_regular_ipa_passes"}, + "children": [{"id": "86_analyzer", + "label": {"text": "86_analyzer"}}, + {"id": "87_odr", + "label": {"text": "87_odr"}}, + {"id": "88_whole-program", + "label": {"text": "88_whole-program"}}, + {"id": "89_profile_estimate", + "label": {"text": "89_profile_estimate"}}, + {"id": "90_icf", + "label": {"text": "90_icf"}}, + {"id": "91_devirt", + "label": {"text": "91_devirt"}}, + {"id": "92_cdtor", + "label": {"text": "92_cdtor"}}, + {"id": "93_cp", + "label": {"text": "93_cp"}}, + {"id": "94_sra", + "label": {"text": "94_sra"}}, + {"id": "95_fnsummary", + "label": {"text": "95_fnsummary"}}, + {"id": "96_inline", + "label": {"text": "96_inline"}}, + {"id": "97_locality-clone", + "label": {"text": "97_locality-clone"}}, + {"id": "98_pure-const", + "label": {"text": "98_pure-const"}}, + {"id": "99_modref", + "label": {"text": "99_modref"}}, + {"id": "100_free-fnsummary", + "label": {"text": "100_free-fnsummary"}}, + {"id": "101_static-var", + "label": {"text": "101_static-var"}}, + {"id": "102_single-use", + "label": {"text": "102_single-use"}}, + {"id": "103_comdats", + "label": {"text": "103_comdats"}}]}, + {"id": "all_late_ipa_passes", + "label": {"text": "all_late_ipa_passes"}, + "children": [{"id": "104_pta", + "label": {"text": "104_pta"}}, + {"id": "105_simdclone", + "label": {"text": "105_simdclone"}}]}, + {"id": "all_passes", + "label": {"text": "all_passes"}, + "children": [{"id": "106_fixup_cfg", + "label": {"text": "106_fixup_cfg"}}, + {"id": "107_ehdisp", + "label": {"text": "107_ehdisp"}}, + {"id": "108_oaccloops", + "label": {"text": "108_oaccloops"}}, + {"id": "109_omp_oacc_neuter_broadcast", + "label": {"text": "109_omp_oacc_neuter_broadcast"}}, + {"id": "110_oaccdevlow", + "label": {"text": "110_oaccdevlow"}}, + {"id": "111_ompdevlow", + "label": {"text": "111_ompdevlow"}}, + {"id": "112_omptargetlink", + "label": {"text": "112_omptargetlink"}}, + {"id": "113_adjust_alignment", + "label": {"text": "113_adjust_alignment"}}, + {"id": "114_hardcfr", + "label": {"text": "114_hardcfr"}}, + {"id": "*all_optimizations_0x101f2720", + "label": {"text": "*all_optimizations"}}, + {"id": "*remove_cgraph_callee_edges_0x101f2780", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "*strip_predict_hints_0x101f27e0", + "label": {"text": "*strip_predict_hints"}}, + {"id": "115_ccp", + "label": {"text": "115_ccp"}}, + {"id": "116_objsz", + "label": {"text": "116_objsz"}}, + {"id": "117_post_ipa_warn", + "label": {"text": "117_post_ipa_warn"}}, + {"id": "118_waccess", + "label": {"text": "118_waccess"}}, + {"id": "119_rebuild_frequencies", + "label": {"text": "119_rebuild_frequencies"}}, + {"id": "120_cunrolli", + "label": {"text": "120_cunrolli"}}, + {"id": "121_backprop", + "label": {"text": "121_backprop"}}, + {"id": "122_phiprop", + "label": {"text": "122_phiprop"}}, + {"id": "123_forwprop", + "label": {"text": "123_forwprop"}}, + {"id": "124_alias", + "label": {"text": "124_alias"}}, + {"id": "125_retslot", + "label": {"text": "125_retslot"}}, + {"id": "126_fre", + "label": {"text": "126_fre"}}, + {"id": "127_mergephi", + "label": {"text": "127_mergephi"}}, + {"id": "128_threadfull", + "label": {"text": "128_threadfull"}}, + {"id": "129_vrp", + "label": {"text": "129_vrp"}}, + {"id": "130_bounds", + "label": {"text": "130_bounds"}}, + {"id": "131_dse", + "label": {"text": "131_dse"}}, + {"id": "132_dce", + "label": {"text": "132_dce"}}, + {"id": "133_stdarg", + "label": {"text": "133_stdarg"}}, + {"id": "134_cdce", + "label": {"text": "134_cdce"}}, + {"id": "135_cselim", + "label": {"text": "135_cselim"}}, + {"id": "136_copyprop", + "label": {"text": "136_copyprop"}}, + {"id": "137_ifcombine", + "label": {"text": "137_ifcombine"}}, + {"id": "138_mergephi", + "label": {"text": "138_mergephi"}}, + {"id": "139_phiopt", + "label": {"text": "139_phiopt"}}, + {"id": "140_tailr", + "label": {"text": "140_tailr"}}, + {"id": "141_ch", + "label": {"text": "141_ch"}}, + {"id": "142_cplxlower", + "label": {"text": "142_cplxlower"}}, + {"id": "143_bitintlower", + "label": {"text": "143_bitintlower"}}, + {"id": "144_sra", + "label": {"text": "144_sra"}}, + {"id": "145_thread", + "label": {"text": "145_thread"}}, + {"id": "146_dom", + "label": {"text": "146_dom"}}, + {"id": "147_copyprop", + "label": {"text": "147_copyprop"}}, + {"id": "148_isolate-paths", + "label": {"text": "148_isolate-paths"}}, + {"id": "149_reassoc", + "label": {"text": "149_reassoc"}}, + {"id": "150_dce", + "label": {"text": "150_dce"}}, + {"id": "151_forwprop", + "label": {"text": "151_forwprop"}}, + {"id": "152_phiopt", + "label": {"text": "152_phiopt"}}, + {"id": "153_ccp", + "label": {"text": "153_ccp"}}, + {"id": "154_pow", + "label": {"text": "154_pow"}}, + {"id": "155_bswap", + "label": {"text": "155_bswap"}}, + {"id": "156_laddress", + "label": {"text": "156_laddress"}}, + {"id": "157_lim", + "label": {"text": "157_lim"}}, + {"id": "158_walloca", + "label": {"text": "158_walloca"}}, + {"id": "159_pre", + "label": {"text": "159_pre"}}, + {"id": "160_sink", + "label": {"text": "160_sink"}}, + {"id": "161_sancov", + "label": {"text": "161_sancov"}}, + {"id": "162_asan", + "label": {"text": "162_asan"}}, + {"id": "163_tsan", + "label": {"text": "163_tsan"}}, + {"id": "164_dse", + "label": {"text": "164_dse"}}, + {"id": "165_dce", + "label": {"text": "165_dce"}}, + {"id": "166_fix_loops", + "label": {"text": "166_fix_loops"}}, + {"id": "167_loop", + "label": {"text": "167_loop"}}, + {"id": "168_loopinit", + "label": {"text": "168_loopinit"}}, + {"id": "169_unswitch", + "label": {"text": "169_unswitch"}}, + {"id": "170_lsplit", + "label": {"text": "170_lsplit"}}, + {"id": "171_sccp", + "label": {"text": "171_sccp"}}, + {"id": "172_lversion", + "label": {"text": "172_lversion"}}, + {"id": "173_unrolljam", + "label": {"text": "173_unrolljam"}}, + {"id": "174_cddce", + "label": {"text": "174_cddce"}}, + {"id": "175_ivcanon", + "label": {"text": "175_ivcanon"}}, + {"id": "176_ldist", + "label": {"text": "176_ldist"}}, + {"id": "177_crc", + "label": {"text": "177_crc"}}, + {"id": "178_linterchange", + "label": {"text": "178_linterchange"}}, + {"id": "179_copyprop", + "label": {"text": "179_copyprop"}}, + {"id": "180_graphite0", + "label": {"text": "180_graphite0"}}, + {"id": "181_graphite", + "label": {"text": "181_graphite"}}, + {"id": "182_lim", + "label": {"text": "182_lim"}}, + {"id": "183_copyprop", + "label": {"text": "183_copyprop"}}, + {"id": "184_dce", + "label": {"text": "184_dce"}}, + {"id": "185_parloops", + "label": {"text": "185_parloops"}}, + {"id": "186_ompexpssa", + "label": {"text": "186_ompexpssa"}}, + {"id": "187_ch_vect", + "label": {"text": "187_ch_vect"}}, + {"id": "188_ifcvt", + "label": {"text": "188_ifcvt"}}, + {"id": "189_vect", + "label": {"text": "189_vect"}}, + {"id": "190_dce", + "label": {"text": "190_dce"}}, + {"id": "191_pcom", + "label": {"text": "191_pcom"}}, + {"id": "192_cunroll", + "label": {"text": "192_cunroll"}}, + {"id": "*pre_slp_scalar_cleanup_0x101f4880", + "label": {"text": "*pre_slp_scalar_cleanup"}}, + {"id": "193_fre", + "label": {"text": "193_fre"}}, + {"id": "194_dse", + "label": {"text": "194_dse"}}, + {"id": "195_slp", + "label": {"text": "195_slp"}}, + {"id": "196_aprefetch", + "label": {"text": "196_aprefetch"}}, + {"id": "197_ivopts", + "label": {"text": "197_ivopts"}}, + {"id": "198_lim", + "label": {"text": "198_lim"}}, + {"id": "199_loopdone", + "label": {"text": "199_loopdone"}}, + {"id": "200_no_loop", + "label": {"text": "200_no_loop"}}, + {"id": "201_slp", + "label": {"text": "201_slp"}}, + {"id": "202_simduid", + "label": {"text": "202_simduid"}}, + {"id": "203_veclower2", + "label": {"text": "203_veclower2"}}, + {"id": "204_switchlower", + "label": {"text": "204_switchlower"}}, + {"id": "205_sincos", + "label": {"text": "205_sincos"}}, + {"id": "206_recip", + "label": {"text": "206_recip"}}, + {"id": "207_reassoc", + "label": {"text": "207_reassoc"}}, + {"id": "208_slsr", + "label": {"text": "208_slsr"}}, + {"id": "209_split-paths", + "label": {"text": "209_split-paths"}}, + {"id": "210_tracer", + "label": {"text": "210_tracer"}}, + {"id": "211_fre", + "label": {"text": "211_fre"}}, + {"id": "212_thread", + "label": {"text": "212_thread"}}, + {"id": "213_dom", + "label": {"text": "213_dom"}}, + {"id": "214_strlen", + "label": {"text": "214_strlen"}}, + {"id": "215_threadfull", + "label": {"text": "215_threadfull"}}, + {"id": "216_vrp", + "label": {"text": "216_vrp"}}, + {"id": "217_ccp", + "label": {"text": "217_ccp"}}, + {"id": "218_wrestrict", + "label": {"text": "218_wrestrict"}}, + {"id": "219_dse", + "label": {"text": "219_dse"}}, + {"id": "220_dce", + "label": {"text": "220_dce"}}, + {"id": "221_forwprop", + "label": {"text": "221_forwprop"}}, + {"id": "222_sink", + "label": {"text": "222_sink"}}, + {"id": "223_phiopt", + "label": {"text": "223_phiopt"}}, + {"id": "224_fab", + "label": {"text": "224_fab"}}, + {"id": "225_widening_mul", + "label": {"text": "225_widening_mul"}}, + {"id": "226_store-merging", + "label": {"text": "226_store-merging"}}, + {"id": "227_cddce", + "label": {"text": "227_cddce"}}, + {"id": "228_sccopy", + "label": {"text": "228_sccopy"}}, + {"id": "229_tailc", + "label": {"text": "229_tailc"}}, + {"id": "230_crited", + "label": {"text": "230_crited"}}, + {"id": "231_uninit", + "label": {"text": "231_uninit"}}, + {"id": "232_local-pure-const", + "label": {"text": "232_local-pure-const"}}, + {"id": "233_modref", + "label": {"text": "233_modref"}}, + {"id": "234_uncprop", + "label": {"text": "234_uncprop"}}, + {"id": "*all_optimizations_g_0x101f5af0", + "label": {"text": "*all_optimizations_g"}}, + {"id": "*remove_cgraph_callee_edges_0x101f5b50", + "label": {"text": "*remove_cgraph_callee_edges"}}, + {"id": "*strip_predict_hints_0x101f5bb0", + "label": {"text": "*strip_predict_hints"}}, + {"id": "235_cplxlower", + "label": {"text": "235_cplxlower"}}, + {"id": "236_bitintlower", + "label": {"text": "236_bitintlower"}}, + {"id": "237_veclower2", + "label": {"text": "237_veclower2"}}, + {"id": "238_switchlower", + "label": {"text": "238_switchlower"}}, + {"id": "239_ccp", + "label": {"text": "239_ccp"}}, + {"id": "240_post_ipa_warn", + "label": {"text": "240_post_ipa_warn"}}, + {"id": "241_objsz", + "label": {"text": "241_objsz"}}, + {"id": "242_fab", + "label": {"text": "242_fab"}}, + {"id": "243_strlen", + "label": {"text": "243_strlen"}}, + {"id": "244_copyprop", + "label": {"text": "244_copyprop"}}, + {"id": "245_dce", + "label": {"text": "245_dce"}}, + {"id": "246_rebuild_frequencies", + "label": {"text": "246_rebuild_frequencies"}}, + {"id": "247_sancov", + "label": {"text": "247_sancov"}}, + {"id": "248_asan", + "label": {"text": "248_asan"}}, + {"id": "249_tsan", + "label": {"text": "249_tsan"}}, + {"id": "250_crited", + "label": {"text": "250_crited"}}, + {"id": "251_uninit", + "label": {"text": "251_uninit"}}, + {"id": "252_uncprop", + "label": {"text": "252_uncprop"}}, + {"id": "253_assumptions", + "label": {"text": "253_assumptions"}}, + {"id": "*tminit_0x101f6370", + "label": {"text": "*tminit"}}, + {"id": "254_tmmark", + "label": {"text": "254_tmmark"}}, + {"id": "255_tmmemopt", + "label": {"text": "255_tmmemopt"}}, + {"id": "256_tmedge", + "label": {"text": "256_tmedge"}}, + {"id": "257_simduid", + "label": {"text": "257_simduid"}}, + {"id": "258_vtable-verify", + "label": {"text": "258_vtable-verify"}}, + {"id": "259_lower_vaarg", + "label": {"text": "259_lower_vaarg"}}, + {"id": "260_veclower", + "label": {"text": "260_veclower"}}, + {"id": "261_cplxlower0", + "label": {"text": "261_cplxlower0"}}, + {"id": "262_bitintlower0", + "label": {"text": "262_bitintlower0"}}, + {"id": "263_sancov_O0", + "label": {"text": "263_sancov_O0"}}, + {"id": "264_switchlower_O0", + "label": {"text": "264_switchlower_O0"}}, + {"id": "265_asan0", + "label": {"text": "265_asan0"}}, + {"id": "266_tsan0", + "label": {"text": "266_tsan0"}}, + {"id": "267_musttail", + "label": {"text": "267_musttail"}}, + {"id": "268_sanopt", + "label": {"text": "268_sanopt"}}, + {"id": "269_ehcleanup", + "label": {"text": "269_ehcleanup"}}, + {"id": "270_resx", + "label": {"text": "270_resx"}}, + {"id": "271_nrv", + "label": {"text": "271_nrv"}}, + {"id": "272_isel", + "label": {"text": "272_isel"}}, + {"id": "273_hardcbr", + "label": {"text": "273_hardcbr"}}, + {"id": "274_hardcmp", + "label": {"text": "274_hardcmp"}}, + {"id": "275_waccess", + "label": {"text": "275_waccess"}}, + {"id": "276_optimized", + "label": {"text": "276_optimized"}}, + {"id": "*warn_function_noreturn_0x101f6dd0", + "label": {"text": "*warn_function_noreturn"}}, + {"id": "277_expand", + "label": {"text": "277_expand"}}, + {"id": "*rest_of_compilation_0x101f6e90", + "label": {"text": "*rest_of_compilation"}}, + {"id": "278_vregs", + "label": {"text": "278_vregs"}}, + {"id": "279_into_cfglayout", + "label": {"text": "279_into_cfglayout"}}, + {"id": "280_jump", + "label": {"text": "280_jump"}}, + {"id": "281_subreg1", + "label": {"text": "281_subreg1"}}, + {"id": "282_dfinit", + "label": {"text": "282_dfinit"}}, + {"id": "283_cse1", + "label": {"text": "283_cse1"}}, + {"id": "284_fwprop1", + "label": {"text": "284_fwprop1"}}, + {"id": "285_cprop", + "label": {"text": "285_cprop"}}, + {"id": "286_rtl pre", + "label": {"text": "286_rtl pre"}}, + {"id": "287_hoist", + "label": {"text": "287_hoist"}}, + {"id": "288_hardreg_pre", + "label": {"text": "288_hardreg_pre"}}, + {"id": "289_cprop", + "label": {"text": "289_cprop"}}, + {"id": "290_store_motion", + "label": {"text": "290_store_motion"}}, + {"id": "291_cse_local", + "label": {"text": "291_cse_local"}}, + {"id": "292_ce1", + "label": {"text": "292_ce1"}}, + {"id": "293_apx_nfcvt", + "label": {"text": "293_apx_nfcvt"}}, + {"id": "294_reginfo", + "label": {"text": "294_reginfo"}}, + {"id": "295_loop2", + "label": {"text": "295_loop2"}}, + {"id": "296_loop2_init", + "label": {"text": "296_loop2_init"}}, + {"id": "297_loop2_invariant", + "label": {"text": "297_loop2_invariant"}}, + {"id": "298_loop2_unroll", + "label": {"text": "298_loop2_unroll"}}, + {"id": "299_loop2_doloop", + "label": {"text": "299_loop2_doloop"}}, + {"id": "300_loop2_done", + "label": {"text": "300_loop2_done"}}, + {"id": "301_subreg2", + "label": {"text": "301_subreg2"}}, + {"id": "302_web", + "label": {"text": "302_web"}}, + {"id": "303_cprop", + "label": {"text": "303_cprop"}}, + {"id": "304_stv", + "label": {"text": "304_stv"}}, + {"id": "305_cse2", + "label": {"text": "305_cse2"}}, + {"id": "306_dse1", + "label": {"text": "306_dse1"}}, + {"id": "307_fwprop2", + "label": {"text": "307_fwprop2"}}, + {"id": "308_auto_inc_dec", + "label": {"text": "308_auto_inc_dec"}}, + {"id": "309_init-regs", + "label": {"text": "309_init-regs"}}, + {"id": "310_ud_dce", + "label": {"text": "310_ud_dce"}}, + {"id": "311_ext_dce", + "label": {"text": "311_ext_dce"}}, + {"id": "312_combine", + "label": {"text": "312_combine"}}, + {"id": "313_late_combine", + "label": {"text": "313_late_combine"}}, + {"id": "314_rpad", + "label": {"text": "314_rpad"}}, + {"id": "315_rrvl", + "label": {"text": "315_rrvl"}}, + {"id": "316_stv", + "label": {"text": "316_stv"}}, + {"id": "317_ce2", + "label": {"text": "317_ce2"}}, + {"id": "318_jump_after_combine", + "label": {"text": "318_jump_after_combine"}}, + {"id": "319_bbpart", + "label": {"text": "319_bbpart"}}, + {"id": "320_outof_cfglayout", + "label": {"text": "320_outof_cfglayout"}}, + {"id": "321_split1", + "label": {"text": "321_split1"}}, + {"id": "322_subreg3", + "label": {"text": "322_subreg3"}}, + {"id": "323_no-opt dfinit", + "label": {"text": "323_no-opt dfinit"}}, + {"id": "*stack_ptr_mod_0x101f8050", + "label": {"text": "*stack_ptr_mod"}}, + {"id": "324_mode_sw", + "label": {"text": "324_mode_sw"}}, + {"id": "325_asmcons", + "label": {"text": "325_asmcons"}}, + {"id": "326_sms", + "label": {"text": "326_sms"}}, + {"id": "327_lr_shrinkage", + "label": {"text": "327_lr_shrinkage"}}, + {"id": "328_sched1", + "label": {"text": "328_sched1"}}, + {"id": "329_avoid_store_forwarding", + "label": {"text": "329_avoid_store_forwarding"}}, + {"id": "330_early_remat", + "label": {"text": "330_early_remat"}}, + {"id": "331_ira", + "label": {"text": "331_ira"}}, + {"id": "332_reload", + "label": {"text": "332_reload"}}, + {"id": "*all-postreload_0x101f8410", + "label": {"text": "*all-postreload"}}, + {"id": "333_postreload", + "label": {"text": "333_postreload"}}, + {"id": "334_vzeroupper", + "label": {"text": "334_vzeroupper"}}, + {"id": "335_late_combine", + "label": {"text": "335_late_combine"}}, + {"id": "336_gcse2", + "label": {"text": "336_gcse2"}}, + {"id": "337_split2", + "label": {"text": "337_split2"}}, + {"id": "338_ree", + "label": {"text": "338_ree"}}, + {"id": "339_cmpelim", + "label": {"text": "339_cmpelim"}}, + {"id": "340_pro_and_epilogue", + "label": {"text": "340_pro_and_epilogue"}}, + {"id": "341_dse2", + "label": {"text": "341_dse2"}}, + {"id": "342_csa", + "label": {"text": "342_csa"}}, + {"id": "343_jump2", + "label": {"text": "343_jump2"}}, + {"id": "344_compgotos", + "label": {"text": "344_compgotos"}}, + {"id": "345_sched_fusion", + "label": {"text": "345_sched_fusion"}}, + {"id": "346_peephole2", + "label": {"text": "346_peephole2"}}, + {"id": "347_ce3", + "label": {"text": "347_ce3"}}, + {"id": "348_rnreg", + "label": {"text": "348_rnreg"}}, + {"id": "349_fold_mem_offsets", + "label": {"text": "349_fold_mem_offsets"}}, + {"id": "350_cprop_hardreg", + "label": {"text": "350_cprop_hardreg"}}, + {"id": "351_rtl_dce", + "label": {"text": "351_rtl_dce"}}, + {"id": "352_bbro", + "label": {"text": "352_bbro"}}, + {"id": "*leaf_regs_0x101f8bf0", + "label": {"text": "*leaf_regs"}}, + {"id": "353_split3", + "label": {"text": "353_split3"}}, + {"id": "354_sched2", + "label": {"text": "354_sched2"}}, + {"id": "*stack_regs_0x101f8d10", + "label": {"text": "*stack_regs"}}, + {"id": "355_split4", + "label": {"text": "355_split4"}}, + {"id": "356_stack", + "label": {"text": "356_stack"}}, + {"id": "357_late_pro_and_epilogue", + "label": {"text": "357_late_pro_and_epilogue"}}, + {"id": "*all-late_compilation_0x101f8e90", + "label": {"text": "*all-late_compilation"}}, + {"id": "358_zero_call_used_regs", + "label": {"text": "358_zero_call_used_regs"}}, + {"id": "359_alignments", + "label": {"text": "359_alignments"}}, + {"id": "360_vartrack", + "label": {"text": "360_vartrack"}}, + {"id": "*free_cfg_0x101f9010", + "label": {"text": "*free_cfg"}}, + {"id": "361_mach", + "label": {"text": "361_mach"}}, + {"id": "362_barriers", + "label": {"text": "362_barriers"}}, + {"id": "363_dbr", + "label": {"text": "363_dbr"}}, + {"id": "364_split5", + "label": {"text": "364_split5"}}, + {"id": "365_eh_ranges", + "label": {"text": "365_eh_ranges"}}, + {"id": "366_endbr_and_patchable_area", + "label": {"text": "366_endbr_and_patchable_area"}}, + {"id": "367_align_tight_loops", + "label": {"text": "367_align_tight_loops"}}, + {"id": "368_shorten", + "label": {"text": "368_shorten"}}, + {"id": "369_nothrow", + "label": {"text": "369_nothrow"}}, + {"id": "370_dwarf2", + "label": {"text": "370_dwarf2"}}, + {"id": "371_final", + "label": {"text": "371_final"}}, + {"id": "372_dfinish", + "label": {"text": "372_dfinish"}}, + {"id": "*clean_state_0x101f9500", + "label": {"text": "*clean_state"}}]}], + "edges": [{"id": "edge0", + "label": {"text": "next"}, + "sourceNodeId": "22_ompexp", + "targetNodeId": "*build_cgraph_edges_0x101ef910"}, + {"id": "edge1", + "label": {"text": "next"}, + "sourceNodeId": "21_coro-early-expand-ifns", + "targetNodeId": "22_ompexp"}, + {"id": "edge2", + "label": {"text": "next"}, + "sourceNodeId": "*warn_function_return_0x101ef7f0", + "targetNodeId": "21_coro-early-expand-ifns"}, + {"id": "edge3", + "label": {"text": "next"}, + "sourceNodeId": "20_cfg", + "targetNodeId": "*warn_function_return_0x101ef7f0"}, + {"id": "edge4", + "label": {"text": "next"}, + "sourceNodeId": "19_coro-lower-builtins", + "targetNodeId": "20_cfg"}, + {"id": "edge5", + "label": {"text": "next"}, + "sourceNodeId": "18_eh", + "targetNodeId": "19_coro-lower-builtins"}, + {"id": "edge6", + "label": {"text": "next"}, + "sourceNodeId": "17_ehopt", + "targetNodeId": "18_eh"}, + {"id": "edge7", + "label": {"text": "next"}, + "sourceNodeId": "16_tmlower", + "targetNodeId": "17_ehopt"}, + {"id": "edge8", + "label": {"text": "next"}, + "sourceNodeId": "15_lower", + "targetNodeId": "16_tmlower"}, + {"id": "edge9", + "label": {"text": "next"}, + "sourceNodeId": "14_omplower", + "targetNodeId": "15_lower"}, + {"id": "edge10", + "label": {"text": "next"}, + "sourceNodeId": "13_omp_oacc_kernels_decompose", + "targetNodeId": "14_omplower"}, + {"id": "edge11", + "label": {"text": "next"}, + "sourceNodeId": "*diagnose_tm_blocks_0x101ef490", + "targetNodeId": "13_omp_oacc_kernels_decompose"}, + {"id": "edge12", + "label": {"text": "next"}, + "sourceNodeId": "*diagnose_omp_blocks_0x101ef430", + "targetNodeId": "*diagnose_tm_blocks_0x101ef490"}, + {"id": "edge13", + "label": {"text": "next"}, + "sourceNodeId": "*warn_unused_result_0x101ef3d0", + "targetNodeId": "*diagnose_omp_blocks_0x101ef430"}, + {"id": "edge14", + "label": {"text": "next"}, + "sourceNodeId": "34_nothrow", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0020"}, + {"id": "edge15", + "label": {"text": "next"}, + "sourceNodeId": "33_ubsan", + "targetNodeId": "34_nothrow"}, + {"id": "edge16", + "label": {"text": "next"}, + "sourceNodeId": "32_waccess", + "targetNodeId": "33_ubsan"}, + {"id": "edge17", + "label": {"text": "next"}, + "sourceNodeId": "31_early_uninit", + "targetNodeId": "32_waccess"}, + {"id": "edge18", + "label": {"text": "next"}, + "sourceNodeId": "*nonnullcmp_0x101efce0", + "targetNodeId": "31_early_uninit"}, + {"id": "edge19", + "label": {"text": "next"}, + "sourceNodeId": "30_warn-printf", + "targetNodeId": "*nonnullcmp_0x101efce0"}, + {"id": "edge20", + "label": {"text": "next"}, + "sourceNodeId": "29_walloca", + "targetNodeId": "30_warn-printf"}, + {"id": "edge21", + "label": {"text": "next"}, + "sourceNodeId": "376_test_graph_emission", + "targetNodeId": "29_walloca"}, + {"id": "edge22", + "label": {"text": "next"}, + "sourceNodeId": "28_ssa", + "targetNodeId": "376_test_graph_emission"}, + {"id": "edge23", + "label": {"text": "next"}, + "sourceNodeId": "27_fixup_cfg", + "targetNodeId": "28_ssa"}, + {"id": "edge24", + "label": {"text": "sub"}, + "sourceNodeId": "26_build_ssa_passes", + "targetNodeId": "27_fixup_cfg"}, + {"id": "edge25", + "label": {"text": "next"}, + "sourceNodeId": "61_fnsplit", + "targetNodeId": "*strip_predict_hints_0x101f0c60"}, + {"id": "edge26", + "label": {"text": "next"}, + "sourceNodeId": "60_modref", + "targetNodeId": "61_fnsplit"}, + {"id": "edge27", + "label": {"text": "next"}, + "sourceNodeId": "59_local-pure-const", + "targetNodeId": "60_modref"}, + {"id": "edge28", + "label": {"text": "next"}, + "sourceNodeId": "58_profile_estimate", + "targetNodeId": "59_local-pure-const"}, + {"id": "edge29", + "label": {"text": "next"}, + "sourceNodeId": "57_sccopy", + "targetNodeId": "58_profile_estimate"}, + {"id": "edge30", + "label": {"text": "next"}, + "sourceNodeId": "56_ehcleanup", + "targetNodeId": "57_sccopy"}, + {"id": "edge31", + "label": {"text": "next"}, + "sourceNodeId": "55_switchconv", + "targetNodeId": "56_ehcleanup"}, + {"id": "edge32", + "label": {"text": "next"}, + "sourceNodeId": "54_iftoswitch", + "targetNodeId": "55_switchconv"}, + {"id": "edge33", + "label": {"text": "next"}, + "sourceNodeId": "53_tailr", + "targetNodeId": "54_iftoswitch"}, + {"id": "edge34", + "label": {"text": "next"}, + "sourceNodeId": "52_phiopt", + "targetNodeId": "53_tailr"}, + {"id": "edge35", + "label": {"text": "next"}, + "sourceNodeId": "51_cddce", + "targetNodeId": "52_phiopt"}, + {"id": "edge36", + "label": {"text": "next"}, + "sourceNodeId": "50_dse", + "targetNodeId": "51_cddce"}, + {"id": "edge37", + "label": {"text": "next"}, + "sourceNodeId": "49_mergephi", + "targetNodeId": "50_dse"}, + {"id": "edge38", + "label": {"text": "next"}, + "sourceNodeId": "48_evrp", + "targetNodeId": "49_mergephi"}, + {"id": "edge39", + "label": {"text": "next"}, + "sourceNodeId": "47_fre", + "targetNodeId": "48_evrp"}, + {"id": "edge40", + "label": {"text": "next"}, + "sourceNodeId": "46_phiprop", + "targetNodeId": "47_fre"}, + {"id": "edge41", + "label": {"text": "next"}, + "sourceNodeId": "45_ealias", + "targetNodeId": "46_phiprop"}, + {"id": "edge42", + "label": {"text": "next"}, + "sourceNodeId": "44_esra", + "targetNodeId": "45_ealias"}, + {"id": "edge43", + "label": {"text": "next"}, + "sourceNodeId": "43_ethread", + "targetNodeId": "44_esra"}, + {"id": "edge44", + "label": {"text": "next"}, + "sourceNodeId": "42_forwprop", + "targetNodeId": "43_ethread"}, + {"id": "edge45", + "label": {"text": "next"}, + "sourceNodeId": "41_ccp", + "targetNodeId": "42_forwprop"}, + {"id": "edge46", + "label": {"text": "next"}, + "sourceNodeId": "40_early_objsz", + "targetNodeId": "41_ccp"}, + {"id": "edge47", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f0340", + "targetNodeId": "40_early_objsz"}, + {"id": "edge48", + "label": {"text": "sub"}, + "sourceNodeId": "39_early_optimizations", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f0340"}, + {"id": "edge49", + "label": {"text": "next"}, + "sourceNodeId": "*rebuild_cgraph_edges_0x101f0d30", + "targetNodeId": "63_local-fnsummary"}, + {"id": "edge50", + "label": {"text": "next"}, + "sourceNodeId": "62_release_ssa", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0d30"}, + {"id": "edge51", + "label": {"text": "next"}, + "sourceNodeId": "39_early_optimizations", + "targetNodeId": "62_release_ssa"}, + {"id": "edge52", + "label": {"text": "next"}, + "sourceNodeId": "*infinite-recursion_0x101f0260", + "targetNodeId": "39_early_optimizations"}, + {"id": "edge53", + "label": {"text": "next"}, + "sourceNodeId": "38_einline", + "targetNodeId": "*infinite-recursion_0x101f0260"}, + {"id": "edge54", + "label": {"text": "next"}, + "sourceNodeId": "37_local-fnsummary", + "targetNodeId": "38_einline"}, + {"id": "edge55", + "label": {"text": "next"}, + "sourceNodeId": "*rebuild_cgraph_edges_0x101f0140", + "targetNodeId": "37_local-fnsummary"}, + {"id": "edge56", + "label": {"text": "next"}, + "sourceNodeId": "36_fixup_cfg", + "targetNodeId": "*rebuild_cgraph_edges_0x101f0140"}, + {"id": "edge57", + "label": {"text": "sub"}, + "sourceNodeId": "35_opt_local_passes", + "targetNodeId": "36_fixup_cfg"}, + {"id": "edge58", + "label": {"text": "next"}, + "sourceNodeId": "76_ompexpssa", + "targetNodeId": "*rebuild_cgraph_edges_0x101f1310"}, + {"id": "edge59", + "label": {"text": "next"}, + "sourceNodeId": "75_parloops", + "targetNodeId": "76_ompexpssa"}, + {"id": "edge60", + "label": {"text": "next"}, + "sourceNodeId": "74_dce", + "targetNodeId": "75_parloops"}, + {"id": "edge61", + "label": {"text": "next"}, + "sourceNodeId": "73_dom", + "targetNodeId": "74_dce"}, + {"id": "edge62", + "label": {"text": "next"}, + "sourceNodeId": "72_lim", + "targetNodeId": "73_dom"}, + {"id": "edge63", + "label": {"text": "next"}, + "sourceNodeId": "71_fre", + "targetNodeId": "72_lim"}, + {"id": "edge64", + "label": {"text": "next"}, + "sourceNodeId": "70_ch", + "targetNodeId": "71_fre"}, + {"id": "edge65", + "label": {"text": "sub"}, + "sourceNodeId": "69_oacc_kernels", + "targetNodeId": "70_ch"}, + {"id": "edge66", + "label": {"text": "sub"}, + "sourceNodeId": "68_ipa_oacc_kernels", + "targetNodeId": "69_oacc_kernels"}, + {"id": "edge67", + "label": {"text": "next"}, + "sourceNodeId": "67_pta", + "targetNodeId": "68_ipa_oacc_kernels"}, + {"id": "edge68", + "label": {"text": "sub"}, + "sourceNodeId": "66_ipa_oacc", + "targetNodeId": "67_pta"}, + {"id": "edge69", + "label": {"text": "sub"}, + "sourceNodeId": "78_afdo", + "targetNodeId": "79_feedback_fnsplit"}, + {"id": "edge70", + "label": {"text": "sub"}, + "sourceNodeId": "80_profile", + "targetNodeId": "81_feedback_fnsplit"}, + {"id": "edge71", + "label": {"text": "next"}, + "sourceNodeId": "84_tmipa", + "targetNodeId": "85_emutls"}, + {"id": "edge72", + "label": {"text": "next"}, + "sourceNodeId": "83_increase_alignment", + "targetNodeId": "84_tmipa"}, + {"id": "edge73", + "label": {"text": "next"}, + "sourceNodeId": "82_free-fnsummary", + "targetNodeId": "83_increase_alignment"}, + {"id": "edge74", + "label": {"text": "next"}, + "sourceNodeId": "80_profile", + "targetNodeId": "82_free-fnsummary"}, + {"id": "edge75", + "label": {"text": "next"}, + "sourceNodeId": "78_afdo", + "targetNodeId": "80_profile"}, + {"id": "edge76", + "label": {"text": "next"}, + "sourceNodeId": "77_targetclone", + "targetNodeId": "78_afdo"}, + {"id": "edge77", + "label": {"text": "next"}, + "sourceNodeId": "66_ipa_oacc", + "targetNodeId": "77_targetclone"}, + {"id": "edge78", + "label": {"text": "next"}, + "sourceNodeId": "65_strub", + "targetNodeId": "66_ipa_oacc"}, + {"id": "edge79", + "label": {"text": "next"}, + "sourceNodeId": "64_remove_symbols", + "targetNodeId": "65_strub"}, + {"id": "edge80", + "label": {"text": "next"}, + "sourceNodeId": "35_opt_local_passes", + "targetNodeId": "64_remove_symbols"}, + {"id": "edge81", + "label": {"text": "next"}, + "sourceNodeId": "26_build_ssa_passes", + "targetNodeId": "35_opt_local_passes"}, + {"id": "edge82", + "label": {"text": "next"}, + "sourceNodeId": "25_strubm", + "targetNodeId": "26_build_ssa_passes"}, + {"id": "edge83", + "label": {"text": "next"}, + "sourceNodeId": "24_visibility", + "targetNodeId": "25_strubm"}, + {"id": "edge84", + "label": {"text": "next"}, + "sourceNodeId": "*free_lang_data_0x101ef9d0", + "targetNodeId": "24_visibility"}, + {"id": "edge85", + "label": {"text": "next"}, + "sourceNodeId": "23_afdo_offline", + "targetNodeId": "*free_lang_data_0x101ef9d0"}, + {"id": "edge86", + "label": {"text": "next"}, + "sourceNodeId": "102_single-use", + "targetNodeId": "103_comdats"}, + {"id": "edge87", + "label": {"text": "next"}, + "sourceNodeId": "101_static-var", + "targetNodeId": "102_single-use"}, + {"id": "edge88", + "label": {"text": "next"}, + "sourceNodeId": "100_free-fnsummary", + "targetNodeId": "101_static-var"}, + {"id": "edge89", + "label": {"text": "next"}, + "sourceNodeId": "99_modref", + "targetNodeId": "100_free-fnsummary"}, + {"id": "edge90", + "label": {"text": "next"}, + "sourceNodeId": "98_pure-const", + "targetNodeId": "99_modref"}, + {"id": "edge91", + "label": {"text": "next"}, + "sourceNodeId": "97_locality-clone", + "targetNodeId": "98_pure-const"}, + {"id": "edge92", + "label": {"text": "next"}, + "sourceNodeId": "96_inline", + "targetNodeId": "97_locality-clone"}, + {"id": "edge93", + "label": {"text": "next"}, + "sourceNodeId": "95_fnsummary", + "targetNodeId": "96_inline"}, + {"id": "edge94", + "label": {"text": "next"}, + "sourceNodeId": "94_sra", + "targetNodeId": "95_fnsummary"}, + {"id": "edge95", + "label": {"text": "next"}, + "sourceNodeId": "93_cp", + "targetNodeId": "94_sra"}, + {"id": "edge96", + "label": {"text": "next"}, + "sourceNodeId": "92_cdtor", + "targetNodeId": "93_cp"}, + {"id": "edge97", + "label": {"text": "next"}, + "sourceNodeId": "91_devirt", + "targetNodeId": "92_cdtor"}, + {"id": "edge98", + "label": {"text": "next"}, + "sourceNodeId": "90_icf", + "targetNodeId": "91_devirt"}, + {"id": "edge99", + "label": {"text": "next"}, + "sourceNodeId": "89_profile_estimate", + "targetNodeId": "90_icf"}, + {"id": "edge100", + "label": {"text": "next"}, + "sourceNodeId": "88_whole-program", + "targetNodeId": "89_profile_estimate"}, + {"id": "edge101", + "label": {"text": "next"}, + "sourceNodeId": "87_odr", + "targetNodeId": "88_whole-program"}, + {"id": "edge102", + "label": {"text": "next"}, + "sourceNodeId": "86_analyzer", + "targetNodeId": "87_odr"}, + {"id": "edge103", + "label": {"text": "next"}, + "sourceNodeId": "104_pta", + "targetNodeId": "105_simdclone"}, + {"id": "edge104", + "label": {"text": "next"}, + "sourceNodeId": "183_copyprop", + "targetNodeId": "184_dce"}, + {"id": "edge105", + "label": {"text": "next"}, + "sourceNodeId": "182_lim", + "targetNodeId": "183_copyprop"}, + {"id": "edge106", + "label": {"text": "next"}, + "sourceNodeId": "181_graphite", + "targetNodeId": "182_lim"}, + {"id": "edge107", + "label": {"text": "sub"}, + "sourceNodeId": "180_graphite0", + "targetNodeId": "181_graphite"}, + {"id": "edge108", + "label": {"text": "sub"}, + "sourceNodeId": "189_vect", + "targetNodeId": "190_dce"}, + {"id": "edge109", + "label": {"text": "next"}, + "sourceNodeId": "193_fre", + "targetNodeId": "194_dse"}, + {"id": "edge110", + "label": {"text": "sub"}, + "sourceNodeId": "*pre_slp_scalar_cleanup_0x101f4880", + "targetNodeId": "193_fre"}, + {"id": "edge111", + "label": {"text": "next"}, + "sourceNodeId": "198_lim", + "targetNodeId": "199_loopdone"}, + {"id": "edge112", + "label": {"text": "next"}, + "sourceNodeId": "197_ivopts", + "targetNodeId": "198_lim"}, + {"id": "edge113", + "label": {"text": "next"}, + "sourceNodeId": "196_aprefetch", + "targetNodeId": "197_ivopts"}, + {"id": "edge114", + "label": {"text": "next"}, + "sourceNodeId": "195_slp", + "targetNodeId": "196_aprefetch"}, + {"id": "edge115", + "label": {"text": "next"}, + "sourceNodeId": "*pre_slp_scalar_cleanup_0x101f4880", + "targetNodeId": "195_slp"}, + {"id": "edge116", + "label": {"text": "next"}, + "sourceNodeId": "192_cunroll", + "targetNodeId": "*pre_slp_scalar_cleanup_0x101f4880"}, + {"id": "edge117", + "label": {"text": "next"}, + "sourceNodeId": "191_pcom", + "targetNodeId": "192_cunroll"}, + {"id": "edge118", + "label": {"text": "next"}, + "sourceNodeId": "189_vect", + "targetNodeId": "191_pcom"}, + {"id": "edge119", + "label": {"text": "next"}, + "sourceNodeId": "188_ifcvt", + "targetNodeId": "189_vect"}, + {"id": "edge120", + "label": {"text": "next"}, + "sourceNodeId": "187_ch_vect", + "targetNodeId": "188_ifcvt"}, + {"id": "edge121", + "label": {"text": "next"}, + "sourceNodeId": "186_ompexpssa", + "targetNodeId": "187_ch_vect"}, + {"id": "edge122", + "label": {"text": "next"}, + "sourceNodeId": "185_parloops", + "targetNodeId": "186_ompexpssa"}, + {"id": "edge123", + "label": {"text": "next"}, + "sourceNodeId": "180_graphite0", + "targetNodeId": "185_parloops"}, + {"id": "edge124", + "label": {"text": "next"}, + "sourceNodeId": "179_copyprop", + "targetNodeId": "180_graphite0"}, + {"id": "edge125", + "label": {"text": "next"}, + "sourceNodeId": "178_linterchange", + "targetNodeId": "179_copyprop"}, + {"id": "edge126", + "label": {"text": "next"}, + "sourceNodeId": "177_crc", + "targetNodeId": "178_linterchange"}, + {"id": "edge127", + "label": {"text": "next"}, + "sourceNodeId": "176_ldist", + "targetNodeId": "177_crc"}, + {"id": "edge128", + "label": {"text": "next"}, + "sourceNodeId": "175_ivcanon", + "targetNodeId": "176_ldist"}, + {"id": "edge129", + "label": {"text": "next"}, + "sourceNodeId": "174_cddce", + "targetNodeId": "175_ivcanon"}, + {"id": "edge130", + "label": {"text": "next"}, + "sourceNodeId": "173_unrolljam", + "targetNodeId": "174_cddce"}, + {"id": "edge131", + "label": {"text": "next"}, + "sourceNodeId": "172_lversion", + "targetNodeId": "173_unrolljam"}, + {"id": "edge132", + "label": {"text": "next"}, + "sourceNodeId": "171_sccp", + "targetNodeId": "172_lversion"}, + {"id": "edge133", + "label": {"text": "next"}, + "sourceNodeId": "170_lsplit", + "targetNodeId": "171_sccp"}, + {"id": "edge134", + "label": {"text": "next"}, + "sourceNodeId": "169_unswitch", + "targetNodeId": "170_lsplit"}, + {"id": "edge135", + "label": {"text": "next"}, + "sourceNodeId": "168_loopinit", + "targetNodeId": "169_unswitch"}, + {"id": "edge136", + "label": {"text": "sub"}, + "sourceNodeId": "167_loop", + "targetNodeId": "168_loopinit"}, + {"id": "edge137", + "label": {"text": "sub"}, + "sourceNodeId": "200_no_loop", + "targetNodeId": "201_slp"}, + {"id": "edge138", + "label": {"text": "next"}, + "sourceNodeId": "233_modref", + "targetNodeId": "234_uncprop"}, + {"id": "edge139", + "label": {"text": "next"}, + "sourceNodeId": "232_local-pure-const", + "targetNodeId": "233_modref"}, + {"id": "edge140", + "label": {"text": "next"}, + "sourceNodeId": "231_uninit", + "targetNodeId": "232_local-pure-const"}, + {"id": "edge141", + "label": {"text": "next"}, + "sourceNodeId": "230_crited", + "targetNodeId": "231_uninit"}, + {"id": "edge142", + "label": {"text": "next"}, + "sourceNodeId": "229_tailc", + "targetNodeId": "230_crited"}, + {"id": "edge143", + "label": {"text": "next"}, + "sourceNodeId": "228_sccopy", + "targetNodeId": "229_tailc"}, + {"id": "edge144", + "label": {"text": "next"}, + "sourceNodeId": "227_cddce", + "targetNodeId": "228_sccopy"}, + {"id": "edge145", + "label": {"text": "next"}, + "sourceNodeId": "226_store-merging", + "targetNodeId": "227_cddce"}, + {"id": "edge146", + "label": {"text": "next"}, + "sourceNodeId": "225_widening_mul", + "targetNodeId": "226_store-merging"}, + {"id": "edge147", + "label": {"text": "next"}, + "sourceNodeId": "224_fab", + "targetNodeId": "225_widening_mul"}, + {"id": "edge148", + "label": {"text": "next"}, + "sourceNodeId": "223_phiopt", + "targetNodeId": "224_fab"}, + {"id": "edge149", + "label": {"text": "next"}, + "sourceNodeId": "222_sink", + "targetNodeId": "223_phiopt"}, + {"id": "edge150", + "label": {"text": "next"}, + "sourceNodeId": "221_forwprop", + "targetNodeId": "222_sink"}, + {"id": "edge151", + "label": {"text": "next"}, + "sourceNodeId": "220_dce", + "targetNodeId": "221_forwprop"}, + {"id": "edge152", + "label": {"text": "next"}, + "sourceNodeId": "219_dse", + "targetNodeId": "220_dce"}, + {"id": "edge153", + "label": {"text": "next"}, + "sourceNodeId": "218_wrestrict", + "targetNodeId": "219_dse"}, + {"id": "edge154", + "label": {"text": "next"}, + "sourceNodeId": "217_ccp", + "targetNodeId": "218_wrestrict"}, + {"id": "edge155", + "label": {"text": "next"}, + "sourceNodeId": "216_vrp", + "targetNodeId": "217_ccp"}, + {"id": "edge156", + "label": {"text": "next"}, + "sourceNodeId": "215_threadfull", + "targetNodeId": "216_vrp"}, + {"id": "edge157", + "label": {"text": "next"}, + "sourceNodeId": "214_strlen", + "targetNodeId": "215_threadfull"}, + {"id": "edge158", + "label": {"text": "next"}, + "sourceNodeId": "213_dom", + "targetNodeId": "214_strlen"}, + {"id": "edge159", + "label": {"text": "next"}, + "sourceNodeId": "212_thread", + "targetNodeId": "213_dom"}, + {"id": "edge160", + "label": {"text": "next"}, + "sourceNodeId": "211_fre", + "targetNodeId": "212_thread"}, + {"id": "edge161", + "label": {"text": "next"}, + "sourceNodeId": "210_tracer", + "targetNodeId": "211_fre"}, + {"id": "edge162", + "label": {"text": "next"}, + "sourceNodeId": "209_split-paths", + "targetNodeId": "210_tracer"}, + {"id": "edge163", + "label": {"text": "next"}, + "sourceNodeId": "208_slsr", + "targetNodeId": "209_split-paths"}, + {"id": "edge164", + "label": {"text": "next"}, + "sourceNodeId": "207_reassoc", + "targetNodeId": "208_slsr"}, + {"id": "edge165", + "label": {"text": "next"}, + "sourceNodeId": "206_recip", + "targetNodeId": "207_reassoc"}, + {"id": "edge166", + "label": {"text": "next"}, + "sourceNodeId": "205_sincos", + "targetNodeId": "206_recip"}, + {"id": "edge167", + "label": {"text": "next"}, + "sourceNodeId": "204_switchlower", + "targetNodeId": "205_sincos"}, + {"id": "edge168", + "label": {"text": "next"}, + "sourceNodeId": "203_veclower2", + "targetNodeId": "204_switchlower"}, + {"id": "edge169", + "label": {"text": "next"}, + "sourceNodeId": "202_simduid", + "targetNodeId": "203_veclower2"}, + {"id": "edge170", + "label": {"text": "next"}, + "sourceNodeId": "200_no_loop", + "targetNodeId": "202_simduid"}, + {"id": "edge171", + "label": {"text": "next"}, + "sourceNodeId": "167_loop", + "targetNodeId": "200_no_loop"}, + {"id": "edge172", + "label": {"text": "next"}, + "sourceNodeId": "166_fix_loops", + "targetNodeId": "167_loop"}, + {"id": "edge173", + "label": {"text": "next"}, + "sourceNodeId": "165_dce", + "targetNodeId": "166_fix_loops"}, + {"id": "edge174", + "label": {"text": "next"}, + "sourceNodeId": "164_dse", + "targetNodeId": "165_dce"}, + {"id": "edge175", + "label": {"text": "next"}, + "sourceNodeId": "163_tsan", + "targetNodeId": "164_dse"}, + {"id": "edge176", + "label": {"text": "next"}, + "sourceNodeId": "162_asan", + "targetNodeId": "163_tsan"}, + {"id": "edge177", + "label": {"text": "next"}, + "sourceNodeId": "161_sancov", + "targetNodeId": "162_asan"}, + {"id": "edge178", + "label": {"text": "next"}, + "sourceNodeId": "160_sink", + "targetNodeId": "161_sancov"}, + {"id": "edge179", + "label": {"text": "next"}, + "sourceNodeId": "159_pre", + "targetNodeId": "160_sink"}, + {"id": "edge180", + "label": {"text": "next"}, + "sourceNodeId": "158_walloca", + "targetNodeId": "159_pre"}, + {"id": "edge181", + "label": {"text": "next"}, + "sourceNodeId": "157_lim", + "targetNodeId": "158_walloca"}, + {"id": "edge182", + "label": {"text": "next"}, + "sourceNodeId": "156_laddress", + "targetNodeId": "157_lim"}, + {"id": "edge183", + "label": {"text": "next"}, + "sourceNodeId": "155_bswap", + "targetNodeId": "156_laddress"}, + {"id": "edge184", + "label": {"text": "next"}, + "sourceNodeId": "154_pow", + "targetNodeId": "155_bswap"}, + {"id": "edge185", + "label": {"text": "next"}, + "sourceNodeId": "153_ccp", + "targetNodeId": "154_pow"}, + {"id": "edge186", + "label": {"text": "next"}, + "sourceNodeId": "152_phiopt", + "targetNodeId": "153_ccp"}, + {"id": "edge187", + "label": {"text": "next"}, + "sourceNodeId": "151_forwprop", + "targetNodeId": "152_phiopt"}, + {"id": "edge188", + "label": {"text": "next"}, + "sourceNodeId": "150_dce", + "targetNodeId": "151_forwprop"}, + {"id": "edge189", + "label": {"text": "next"}, + "sourceNodeId": "149_reassoc", + "targetNodeId": "150_dce"}, + {"id": "edge190", + "label": {"text": "next"}, + "sourceNodeId": "148_isolate-paths", + "targetNodeId": "149_reassoc"}, + {"id": "edge191", + "label": {"text": "next"}, + "sourceNodeId": "147_copyprop", + "targetNodeId": "148_isolate-paths"}, + {"id": "edge192", + "label": {"text": "next"}, + "sourceNodeId": "146_dom", + "targetNodeId": "147_copyprop"}, + {"id": "edge193", + "label": {"text": "next"}, + "sourceNodeId": "145_thread", + "targetNodeId": "146_dom"}, + {"id": "edge194", + "label": {"text": "next"}, + "sourceNodeId": "144_sra", + "targetNodeId": "145_thread"}, + {"id": "edge195", + "label": {"text": "next"}, + "sourceNodeId": "143_bitintlower", + "targetNodeId": "144_sra"}, + {"id": "edge196", + "label": {"text": "next"}, + "sourceNodeId": "142_cplxlower", + "targetNodeId": "143_bitintlower"}, + {"id": "edge197", + "label": {"text": "next"}, + "sourceNodeId": "141_ch", + "targetNodeId": "142_cplxlower"}, + {"id": "edge198", + "label": {"text": "next"}, + "sourceNodeId": "140_tailr", + "targetNodeId": "141_ch"}, + {"id": "edge199", + "label": {"text": "next"}, + "sourceNodeId": "139_phiopt", + "targetNodeId": "140_tailr"}, + {"id": "edge200", + "label": {"text": "next"}, + "sourceNodeId": "138_mergephi", + "targetNodeId": "139_phiopt"}, + {"id": "edge201", + "label": {"text": "next"}, + "sourceNodeId": "137_ifcombine", + "targetNodeId": "138_mergephi"}, + {"id": "edge202", + "label": {"text": "next"}, + "sourceNodeId": "136_copyprop", + "targetNodeId": "137_ifcombine"}, + {"id": "edge203", + "label": {"text": "next"}, + "sourceNodeId": "135_cselim", + "targetNodeId": "136_copyprop"}, + {"id": "edge204", + "label": {"text": "next"}, + "sourceNodeId": "134_cdce", + "targetNodeId": "135_cselim"}, + {"id": "edge205", + "label": {"text": "next"}, + "sourceNodeId": "133_stdarg", + "targetNodeId": "134_cdce"}, + {"id": "edge206", + "label": {"text": "next"}, + "sourceNodeId": "132_dce", + "targetNodeId": "133_stdarg"}, + {"id": "edge207", + "label": {"text": "next"}, + "sourceNodeId": "131_dse", + "targetNodeId": "132_dce"}, + {"id": "edge208", + "label": {"text": "next"}, + "sourceNodeId": "130_bounds", + "targetNodeId": "131_dse"}, + {"id": "edge209", + "label": {"text": "next"}, + "sourceNodeId": "129_vrp", + "targetNodeId": "130_bounds"}, + {"id": "edge210", + "label": {"text": "next"}, + "sourceNodeId": "128_threadfull", + "targetNodeId": "129_vrp"}, + {"id": "edge211", + "label": {"text": "next"}, + "sourceNodeId": "127_mergephi", + "targetNodeId": "128_threadfull"}, + {"id": "edge212", + "label": {"text": "next"}, + "sourceNodeId": "126_fre", + "targetNodeId": "127_mergephi"}, + {"id": "edge213", + "label": {"text": "next"}, + "sourceNodeId": "125_retslot", + "targetNodeId": "126_fre"}, + {"id": "edge214", + "label": {"text": "next"}, + "sourceNodeId": "124_alias", + "targetNodeId": "125_retslot"}, + {"id": "edge215", + "label": {"text": "next"}, + "sourceNodeId": "123_forwprop", + "targetNodeId": "124_alias"}, + {"id": "edge216", + "label": {"text": "next"}, + "sourceNodeId": "122_phiprop", + "targetNodeId": "123_forwprop"}, + {"id": "edge217", + "label": {"text": "next"}, + "sourceNodeId": "121_backprop", + "targetNodeId": "122_phiprop"}, + {"id": "edge218", + "label": {"text": "next"}, + "sourceNodeId": "120_cunrolli", + "targetNodeId": "121_backprop"}, + {"id": "edge219", + "label": {"text": "next"}, + "sourceNodeId": "119_rebuild_frequencies", + "targetNodeId": "120_cunrolli"}, + {"id": "edge220", + "label": {"text": "next"}, + "sourceNodeId": "118_waccess", + "targetNodeId": "119_rebuild_frequencies"}, + {"id": "edge221", + "label": {"text": "next"}, + "sourceNodeId": "117_post_ipa_warn", + "targetNodeId": "118_waccess"}, + {"id": "edge222", + "label": {"text": "next"}, + "sourceNodeId": "116_objsz", + "targetNodeId": "117_post_ipa_warn"}, + {"id": "edge223", + "label": {"text": "next"}, + "sourceNodeId": "115_ccp", + "targetNodeId": "116_objsz"}, + {"id": "edge224", + "label": {"text": "next"}, + "sourceNodeId": "*strip_predict_hints_0x101f27e0", + "targetNodeId": "115_ccp"}, + {"id": "edge225", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f2780", + "targetNodeId": "*strip_predict_hints_0x101f27e0"}, + {"id": "edge226", + "label": {"text": "sub"}, + "sourceNodeId": "*all_optimizations_0x101f2720", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f2780"}, + {"id": "edge227", + "label": {"text": "next"}, + "sourceNodeId": "251_uninit", + "targetNodeId": "252_uncprop"}, + {"id": "edge228", + "label": {"text": "next"}, + "sourceNodeId": "250_crited", + "targetNodeId": "251_uninit"}, + {"id": "edge229", + "label": {"text": "next"}, + "sourceNodeId": "249_tsan", + "targetNodeId": "250_crited"}, + {"id": "edge230", + "label": {"text": "next"}, + "sourceNodeId": "248_asan", + "targetNodeId": "249_tsan"}, + {"id": "edge231", + "label": {"text": "next"}, + "sourceNodeId": "247_sancov", + "targetNodeId": "248_asan"}, + {"id": "edge232", + "label": {"text": "next"}, + "sourceNodeId": "246_rebuild_frequencies", + "targetNodeId": "247_sancov"}, + {"id": "edge233", + "label": {"text": "next"}, + "sourceNodeId": "245_dce", + "targetNodeId": "246_rebuild_frequencies"}, + {"id": "edge234", + "label": {"text": "next"}, + "sourceNodeId": "244_copyprop", + "targetNodeId": "245_dce"}, + {"id": "edge235", + "label": {"text": "next"}, + "sourceNodeId": "243_strlen", + "targetNodeId": "244_copyprop"}, + {"id": "edge236", + "label": {"text": "next"}, + "sourceNodeId": "242_fab", + "targetNodeId": "243_strlen"}, + {"id": "edge237", + "label": {"text": "next"}, + "sourceNodeId": "241_objsz", + "targetNodeId": "242_fab"}, + {"id": "edge238", + "label": {"text": "next"}, + "sourceNodeId": "240_post_ipa_warn", + "targetNodeId": "241_objsz"}, + {"id": "edge239", + "label": {"text": "next"}, + "sourceNodeId": "239_ccp", + "targetNodeId": "240_post_ipa_warn"}, + {"id": "edge240", + "label": {"text": "next"}, + "sourceNodeId": "238_switchlower", + "targetNodeId": "239_ccp"}, + {"id": "edge241", + "label": {"text": "next"}, + "sourceNodeId": "237_veclower2", + "targetNodeId": "238_switchlower"}, + {"id": "edge242", + "label": {"text": "next"}, + "sourceNodeId": "236_bitintlower", + "targetNodeId": "237_veclower2"}, + {"id": "edge243", + "label": {"text": "next"}, + "sourceNodeId": "235_cplxlower", + "targetNodeId": "236_bitintlower"}, + {"id": "edge244", + "label": {"text": "next"}, + "sourceNodeId": "*strip_predict_hints_0x101f5bb0", + "targetNodeId": "235_cplxlower"}, + {"id": "edge245", + "label": {"text": "next"}, + "sourceNodeId": "*remove_cgraph_callee_edges_0x101f5b50", + "targetNodeId": "*strip_predict_hints_0x101f5bb0"}, + {"id": "edge246", + "label": {"text": "sub"}, + "sourceNodeId": "*all_optimizations_g_0x101f5af0", + "targetNodeId": "*remove_cgraph_callee_edges_0x101f5b50"}, + {"id": "edge247", + "label": {"text": "next"}, + "sourceNodeId": "255_tmmemopt", + "targetNodeId": "256_tmedge"}, + {"id": "edge248", + "label": {"text": "next"}, + "sourceNodeId": "254_tmmark", + "targetNodeId": "255_tmmemopt"}, + {"id": "edge249", + "label": {"text": "sub"}, + "sourceNodeId": "*tminit_0x101f6370", + "targetNodeId": "254_tmmark"}, + {"id": "edge250", + "label": {"text": "next"}, + "sourceNodeId": "299_loop2_doloop", + "targetNodeId": "300_loop2_done"}, + {"id": "edge251", + "label": {"text": "next"}, + "sourceNodeId": "298_loop2_unroll", + "targetNodeId": "299_loop2_doloop"}, + {"id": "edge252", + "label": {"text": "next"}, + "sourceNodeId": "297_loop2_invariant", + "targetNodeId": "298_loop2_unroll"}, + {"id": "edge253", + "label": {"text": "next"}, + "sourceNodeId": "296_loop2_init", + "targetNodeId": "297_loop2_invariant"}, + {"id": "edge254", + "label": {"text": "sub"}, + "sourceNodeId": "295_loop2", + "targetNodeId": "296_loop2_init"}, + {"id": "edge255", + "label": {"text": "next"}, + "sourceNodeId": "355_split4", + "targetNodeId": "356_stack"}, + {"id": "edge256", + "label": {"text": "sub"}, + "sourceNodeId": "*stack_regs_0x101f8d10", + "targetNodeId": "355_split4"}, + {"id": "edge257", + "label": {"text": "next"}, + "sourceNodeId": "354_sched2", + "targetNodeId": "*stack_regs_0x101f8d10"}, + {"id": "edge258", + "label": {"text": "next"}, + "sourceNodeId": "353_split3", + "targetNodeId": "354_sched2"}, + {"id": "edge259", + "label": {"text": "next"}, + "sourceNodeId": "*leaf_regs_0x101f8bf0", + "targetNodeId": "353_split3"}, + {"id": "edge260", + "label": {"text": "next"}, + "sourceNodeId": "352_bbro", + "targetNodeId": "*leaf_regs_0x101f8bf0"}, + {"id": "edge261", + "label": {"text": "next"}, + "sourceNodeId": "351_rtl_dce", + "targetNodeId": "352_bbro"}, + {"id": "edge262", + "label": {"text": "next"}, + "sourceNodeId": "350_cprop_hardreg", + "targetNodeId": "351_rtl_dce"}, + {"id": "edge263", + "label": {"text": "next"}, + "sourceNodeId": "349_fold_mem_offsets", + "targetNodeId": "350_cprop_hardreg"}, + {"id": "edge264", + "label": {"text": "next"}, + "sourceNodeId": "348_rnreg", + "targetNodeId": "349_fold_mem_offsets"}, + {"id": "edge265", + "label": {"text": "next"}, + "sourceNodeId": "347_ce3", + "targetNodeId": "348_rnreg"}, + {"id": "edge266", + "label": {"text": "next"}, + "sourceNodeId": "346_peephole2", + "targetNodeId": "347_ce3"}, + {"id": "edge267", + "label": {"text": "next"}, + "sourceNodeId": "345_sched_fusion", + "targetNodeId": "346_peephole2"}, + {"id": "edge268", + "label": {"text": "next"}, + "sourceNodeId": "344_compgotos", + "targetNodeId": "345_sched_fusion"}, + {"id": "edge269", + "label": {"text": "next"}, + "sourceNodeId": "343_jump2", + "targetNodeId": "344_compgotos"}, + {"id": "edge270", + "label": {"text": "next"}, + "sourceNodeId": "342_csa", + "targetNodeId": "343_jump2"}, + {"id": "edge271", + "label": {"text": "next"}, + "sourceNodeId": "341_dse2", + "targetNodeId": "342_csa"}, + {"id": "edge272", + "label": {"text": "next"}, + "sourceNodeId": "340_pro_and_epilogue", + "targetNodeId": "341_dse2"}, + {"id": "edge273", + "label": {"text": "next"}, + "sourceNodeId": "339_cmpelim", + "targetNodeId": "340_pro_and_epilogue"}, + {"id": "edge274", + "label": {"text": "next"}, + "sourceNodeId": "338_ree", + "targetNodeId": "339_cmpelim"}, + {"id": "edge275", + "label": {"text": "next"}, + "sourceNodeId": "337_split2", + "targetNodeId": "338_ree"}, + {"id": "edge276", + "label": {"text": "next"}, + "sourceNodeId": "336_gcse2", + "targetNodeId": "337_split2"}, + {"id": "edge277", + "label": {"text": "next"}, + "sourceNodeId": "335_late_combine", + "targetNodeId": "336_gcse2"}, + {"id": "edge278", + "label": {"text": "next"}, + "sourceNodeId": "334_vzeroupper", + "targetNodeId": "335_late_combine"}, + {"id": "edge279", + "label": {"text": "next"}, + "sourceNodeId": "333_postreload", + "targetNodeId": "334_vzeroupper"}, + {"id": "edge280", + "label": {"text": "sub"}, + "sourceNodeId": "*all-postreload_0x101f8410", + "targetNodeId": "333_postreload"}, + {"id": "edge281", + "label": {"text": "next"}, + "sourceNodeId": "370_dwarf2", + "targetNodeId": "371_final"}, + {"id": "edge282", + "label": {"text": "next"}, + "sourceNodeId": "369_nothrow", + "targetNodeId": "370_dwarf2"}, + {"id": "edge283", + "label": {"text": "next"}, + "sourceNodeId": "368_shorten", + "targetNodeId": "369_nothrow"}, + {"id": "edge284", + "label": {"text": "next"}, + "sourceNodeId": "367_align_tight_loops", + "targetNodeId": "368_shorten"}, + {"id": "edge285", + "label": {"text": "next"}, + "sourceNodeId": "366_endbr_and_patchable_area", + "targetNodeId": "367_align_tight_loops"}, + {"id": "edge286", + "label": {"text": "next"}, + "sourceNodeId": "365_eh_ranges", + "targetNodeId": "366_endbr_and_patchable_area"}, + {"id": "edge287", + "label": {"text": "next"}, + "sourceNodeId": "364_split5", + "targetNodeId": "365_eh_ranges"}, + {"id": "edge288", + "label": {"text": "next"}, + "sourceNodeId": "363_dbr", + "targetNodeId": "364_split5"}, + {"id": "edge289", + "label": {"text": "next"}, + "sourceNodeId": "362_barriers", + "targetNodeId": "363_dbr"}, + {"id": "edge290", + "label": {"text": "next"}, + "sourceNodeId": "361_mach", + "targetNodeId": "362_barriers"}, + {"id": "edge291", + "label": {"text": "next"}, + "sourceNodeId": "*free_cfg_0x101f9010", + "targetNodeId": "361_mach"}, + {"id": "edge292", + "label": {"text": "next"}, + "sourceNodeId": "360_vartrack", + "targetNodeId": "*free_cfg_0x101f9010"}, + {"id": "edge293", + "label": {"text": "next"}, + "sourceNodeId": "359_alignments", + "targetNodeId": "360_vartrack"}, + {"id": "edge294", + "label": {"text": "next"}, + "sourceNodeId": "358_zero_call_used_regs", + "targetNodeId": "359_alignments"}, + {"id": "edge295", + "label": {"text": "sub"}, + "sourceNodeId": "*all-late_compilation_0x101f8e90", + "targetNodeId": "358_zero_call_used_regs"}, + {"id": "edge296", + "label": {"text": "next"}, + "sourceNodeId": "*all-late_compilation_0x101f8e90", + "targetNodeId": "372_dfinish"}, + {"id": "edge297", + "label": {"text": "next"}, + "sourceNodeId": "357_late_pro_and_epilogue", + "targetNodeId": "*all-late_compilation_0x101f8e90"}, + {"id": "edge298", + "label": {"text": "next"}, + "sourceNodeId": "*all-postreload_0x101f8410", + "targetNodeId": "357_late_pro_and_epilogue"}, + {"id": "edge299", + "label": {"text": "next"}, + "sourceNodeId": "332_reload", + "targetNodeId": "*all-postreload_0x101f8410"}, + {"id": "edge300", + "label": {"text": "next"}, + "sourceNodeId": "331_ira", + "targetNodeId": "332_reload"}, + {"id": "edge301", + "label": {"text": "next"}, + "sourceNodeId": "330_early_remat", + "targetNodeId": "331_ira"}, + {"id": "edge302", + "label": {"text": "next"}, + "sourceNodeId": "329_avoid_store_forwarding", + "targetNodeId": "330_early_remat"}, + {"id": "edge303", + "label": {"text": "next"}, + "sourceNodeId": "328_sched1", + "targetNodeId": "329_avoid_store_forwarding"}, + {"id": "edge304", + "label": {"text": "next"}, + "sourceNodeId": "327_lr_shrinkage", + "targetNodeId": "328_sched1"}, + {"id": "edge305", + "label": {"text": "next"}, + "sourceNodeId": "326_sms", + "targetNodeId": "327_lr_shrinkage"}, + {"id": "edge306", + "label": {"text": "next"}, + "sourceNodeId": "325_asmcons", + "targetNodeId": "326_sms"}, + {"id": "edge307", + "label": {"text": "next"}, + "sourceNodeId": "324_mode_sw", + "targetNodeId": "325_asmcons"}, + {"id": "edge308", + "label": {"text": "next"}, + "sourceNodeId": "*stack_ptr_mod_0x101f8050", + "targetNodeId": "324_mode_sw"}, + {"id": "edge309", + "label": {"text": "next"}, + "sourceNodeId": "323_no-opt dfinit", + "targetNodeId": "*stack_ptr_mod_0x101f8050"}, + {"id": "edge310", + "label": {"text": "next"}, + "sourceNodeId": "322_subreg3", + "targetNodeId": "323_no-opt dfinit"}, + {"id": "edge311", + "label": {"text": "next"}, + "sourceNodeId": "321_split1", + "targetNodeId": "322_subreg3"}, + {"id": "edge312", + "label": {"text": "next"}, + "sourceNodeId": "320_outof_cfglayout", + "targetNodeId": "321_split1"}, + {"id": "edge313", + "label": {"text": "next"}, + "sourceNodeId": "319_bbpart", + "targetNodeId": "320_outof_cfglayout"}, + {"id": "edge314", + "label": {"text": "next"}, + "sourceNodeId": "318_jump_after_combine", + "targetNodeId": "319_bbpart"}, + {"id": "edge315", + "label": {"text": "next"}, + "sourceNodeId": "317_ce2", + "targetNodeId": "318_jump_after_combine"}, + {"id": "edge316", + "label": {"text": "next"}, + "sourceNodeId": "316_stv", + "targetNodeId": "317_ce2"}, + {"id": "edge317", + "label": {"text": "next"}, + "sourceNodeId": "315_rrvl", + "targetNodeId": "316_stv"}, + {"id": "edge318", + "label": {"text": "next"}, + "sourceNodeId": "314_rpad", + "targetNodeId": "315_rrvl"}, + {"id": "edge319", + "label": {"text": "next"}, + "sourceNodeId": "313_late_combine", + "targetNodeId": "314_rpad"}, + {"id": "edge320", + "label": {"text": "next"}, + "sourceNodeId": "312_combine", + "targetNodeId": "313_late_combine"}, + {"id": "edge321", + "label": {"text": "next"}, + "sourceNodeId": "311_ext_dce", + "targetNodeId": "312_combine"}, + {"id": "edge322", + "label": {"text": "next"}, + "sourceNodeId": "310_ud_dce", + "targetNodeId": "311_ext_dce"}, + {"id": "edge323", + "label": {"text": "next"}, + "sourceNodeId": "309_init-regs", + "targetNodeId": "310_ud_dce"}, + {"id": "edge324", + "label": {"text": "next"}, + "sourceNodeId": "308_auto_inc_dec", + "targetNodeId": "309_init-regs"}, + {"id": "edge325", + "label": {"text": "next"}, + "sourceNodeId": "307_fwprop2", + "targetNodeId": "308_auto_inc_dec"}, + {"id": "edge326", + "label": {"text": "next"}, + "sourceNodeId": "306_dse1", + "targetNodeId": "307_fwprop2"}, + {"id": "edge327", + "label": {"text": "next"}, + "sourceNodeId": "305_cse2", + "targetNodeId": "306_dse1"}, + {"id": "edge328", + "label": {"text": "next"}, + "sourceNodeId": "304_stv", + "targetNodeId": "305_cse2"}, + {"id": "edge329", + "label": {"text": "next"}, + "sourceNodeId": "303_cprop", + "targetNodeId": "304_stv"}, + {"id": "edge330", + "label": {"text": "next"}, + "sourceNodeId": "302_web", + "targetNodeId": "303_cprop"}, + {"id": "edge331", + "label": {"text": "next"}, + "sourceNodeId": "301_subreg2", + "targetNodeId": "302_web"}, + {"id": "edge332", + "label": {"text": "next"}, + "sourceNodeId": "295_loop2", + "targetNodeId": "301_subreg2"}, + {"id": "edge333", + "label": {"text": "next"}, + "sourceNodeId": "294_reginfo", + "targetNodeId": "295_loop2"}, + {"id": "edge334", + "label": {"text": "next"}, + "sourceNodeId": "293_apx_nfcvt", + "targetNodeId": "294_reginfo"}, + {"id": "edge335", + "label": {"text": "next"}, + "sourceNodeId": "292_ce1", + "targetNodeId": "293_apx_nfcvt"}, + {"id": "edge336", + "label": {"text": "next"}, + "sourceNodeId": "291_cse_local", + "targetNodeId": "292_ce1"}, + {"id": "edge337", + "label": {"text": "next"}, + "sourceNodeId": "290_store_motion", + "targetNodeId": "291_cse_local"}, + {"id": "edge338", + "label": {"text": "next"}, + "sourceNodeId": "289_cprop", + "targetNodeId": "290_store_motion"}, + {"id": "edge339", + "label": {"text": "next"}, + "sourceNodeId": "288_hardreg_pre", + "targetNodeId": "289_cprop"}, + {"id": "edge340", + "label": {"text": "next"}, + "sourceNodeId": "287_hoist", + "targetNodeId": "288_hardreg_pre"}, + {"id": "edge341", + "label": {"text": "next"}, + "sourceNodeId": "286_rtl pre", + "targetNodeId": "287_hoist"}, + {"id": "edge342", + "label": {"text": "next"}, + "sourceNodeId": "285_cprop", + "targetNodeId": "286_rtl pre"}, + {"id": "edge343", + "label": {"text": "next"}, + "sourceNodeId": "284_fwprop1", + "targetNodeId": "285_cprop"}, + {"id": "edge344", + "label": {"text": "next"}, + "sourceNodeId": "283_cse1", + "targetNodeId": "284_fwprop1"}, + {"id": "edge345", + "label": {"text": "next"}, + "sourceNodeId": "282_dfinit", + "targetNodeId": "283_cse1"}, + {"id": "edge346", + "label": {"text": "next"}, + "sourceNodeId": "281_subreg1", + "targetNodeId": "282_dfinit"}, + {"id": "edge347", + "label": {"text": "next"}, + "sourceNodeId": "280_jump", + "targetNodeId": "281_subreg1"}, + {"id": "edge348", + "label": {"text": "next"}, + "sourceNodeId": "279_into_cfglayout", + "targetNodeId": "280_jump"}, + {"id": "edge349", + "label": {"text": "next"}, + "sourceNodeId": "278_vregs", + "targetNodeId": "279_into_cfglayout"}, + {"id": "edge350", + "label": {"text": "sub"}, + "sourceNodeId": "*rest_of_compilation_0x101f6e90", + "targetNodeId": "278_vregs"}, + {"id": "edge351", + "label": {"text": "next"}, + "sourceNodeId": "*rest_of_compilation_0x101f6e90", + "targetNodeId": "*clean_state_0x101f9500"}, + {"id": "edge352", + "label": {"text": "next"}, + "sourceNodeId": "277_expand", + "targetNodeId": "*rest_of_compilation_0x101f6e90"}, + {"id": "edge353", + "label": {"text": "next"}, + "sourceNodeId": "*warn_function_noreturn_0x101f6dd0", + "targetNodeId": "277_expand"}, + {"id": "edge354", + "label": {"text": "next"}, + "sourceNodeId": "276_optimized", + "targetNodeId": "*warn_function_noreturn_0x101f6dd0"}, + {"id": "edge355", + "label": {"text": "next"}, + "sourceNodeId": "275_waccess", + "targetNodeId": "276_optimized"}, + {"id": "edge356", + "label": {"text": "next"}, + "sourceNodeId": "274_hardcmp", + "targetNodeId": "275_waccess"}, + {"id": "edge357", + "label": {"text": "next"}, + "sourceNodeId": "273_hardcbr", + "targetNodeId": "274_hardcmp"}, + {"id": "edge358", + "label": {"text": "next"}, + "sourceNodeId": "272_isel", + "targetNodeId": "273_hardcbr"}, + {"id": "edge359", + "label": {"text": "next"}, + "sourceNodeId": "271_nrv", + "targetNodeId": "272_isel"}, + {"id": "edge360", + "label": {"text": "next"}, + "sourceNodeId": "270_resx", + "targetNodeId": "271_nrv"}, + {"id": "edge361", + "label": {"text": "next"}, + "sourceNodeId": "269_ehcleanup", + "targetNodeId": "270_resx"}, + {"id": "edge362", + "label": {"text": "next"}, + "sourceNodeId": "268_sanopt", + "targetNodeId": "269_ehcleanup"}, + {"id": "edge363", + "label": {"text": "next"}, + "sourceNodeId": "267_musttail", + "targetNodeId": "268_sanopt"}, + {"id": "edge364", + "label": {"text": "next"}, + "sourceNodeId": "266_tsan0", + "targetNodeId": "267_musttail"}, + {"id": "edge365", + "label": {"text": "next"}, + "sourceNodeId": "265_asan0", + "targetNodeId": "266_tsan0"}, + {"id": "edge366", + "label": {"text": "next"}, + "sourceNodeId": "264_switchlower_O0", + "targetNodeId": "265_asan0"}, + {"id": "edge367", + "label": {"text": "next"}, + "sourceNodeId": "263_sancov_O0", + "targetNodeId": "264_switchlower_O0"}, + {"id": "edge368", + "label": {"text": "next"}, + "sourceNodeId": "262_bitintlower0", + "targetNodeId": "263_sancov_O0"}, + {"id": "edge369", + "label": {"text": "next"}, + "sourceNodeId": "261_cplxlower0", + "targetNodeId": "262_bitintlower0"}, + {"id": "edge370", + "label": {"text": "next"}, + "sourceNodeId": "260_veclower", + "targetNodeId": "261_cplxlower0"}, + {"id": "edge371", + "label": {"text": "next"}, + "sourceNodeId": "259_lower_vaarg", + "targetNodeId": "260_veclower"}, + {"id": "edge372", + "label": {"text": "next"}, + "sourceNodeId": "258_vtable-verify", + "targetNodeId": "259_lower_vaarg"}, + {"id": "edge373", + "label": {"text": "next"}, + "sourceNodeId": "257_simduid", + "targetNodeId": "258_vtable-verify"}, + {"id": "edge374", + "label": {"text": "next"}, + "sourceNodeId": "*tminit_0x101f6370", + "targetNodeId": "257_simduid"}, + {"id": "edge375", + "label": {"text": "next"}, + "sourceNodeId": "253_assumptions", + "targetNodeId": "*tminit_0x101f6370"}, + {"id": "edge376", + "label": {"text": "next"}, + "sourceNodeId": "*all_optimizations_g_0x101f5af0", + "targetNodeId": "253_assumptions"}, + {"id": "edge377", + "label": {"text": "next"}, + "sourceNodeId": "*all_optimizations_0x101f2720", + "targetNodeId": "*all_optimizations_g_0x101f5af0"}, + {"id": "edge378", + "label": {"text": "next"}, + "sourceNodeId": "114_hardcfr", + "targetNodeId": "*all_optimizations_0x101f2720"}, + {"id": "edge379", + "label": {"text": "next"}, + "sourceNodeId": "113_adjust_alignment", + "targetNodeId": "114_hardcfr"}, + {"id": "edge380", + "label": {"text": "next"}, + "sourceNodeId": "112_omptargetlink", + "targetNodeId": "113_adjust_alignment"}, + {"id": "edge381", + "label": {"text": "next"}, + "sourceNodeId": "111_ompdevlow", + "targetNodeId": "112_omptargetlink"}, + {"id": "edge382", + "label": {"text": "next"}, + "sourceNodeId": "110_oaccdevlow", + "targetNodeId": "111_ompdevlow"}, + {"id": "edge383", + "label": {"text": "next"}, + "sourceNodeId": "109_omp_oacc_neuter_broadcast", + "targetNodeId": "110_oaccdevlow"}, + {"id": "edge384", + "label": {"text": "next"}, + "sourceNodeId": "108_oaccloops", + "targetNodeId": "109_omp_oacc_neuter_broadcast"}, + {"id": "edge385", + "label": {"text": "next"}, + "sourceNodeId": "107_ehdisp", + "targetNodeId": "108_oaccloops"}, + {"id": "edge386", + "label": {"text": "next"}, + "sourceNodeId": "106_fixup_cfg", + "targetNodeId": "107_ehdisp"}]}]}]} + +/* { dg-begin-multiline-output "" } +In function 'test_graphs': +/home/david/gcc-newgit-gcc16/src/gcc/testsuite/gcc.dg/plugin/diagnostic-test-graphs-sarif.c:8:3: error: this is a placeholder error, with graphs + { dg-end-multiline-output "" } */ + +/* Use a Python script to verify various properties about the generated + .html file: + { dg-final { run-html-pytest graphs.sarif "2.1.0-valid/graphs-check-html.py" } } */ + +/* Use a Python script to verify various properties about the *generated* + .sarif file: + { dg-final { run-sarif-pytest graphs.roundtrip "2.1.0-valid/graphs-check-sarif-roundtrip.py" } } */ diff --git a/gcc/tree-cfg.cc b/gcc/tree-cfg.cc index 9a5479a..3d23f17 100644 --- a/gcc/tree-cfg.cc +++ b/gcc/tree-cfg.cc @@ -114,43 +114,6 @@ struct replace_decls_d tree to_context; }; -/* Hash table to store last discriminator assigned for each locus. */ -struct locus_discrim_map -{ - int location_line; - int discriminator; -}; - -/* Hashtable helpers. */ - -struct locus_discrim_hasher : free_ptr_hash <locus_discrim_map> -{ - static inline hashval_t hash (const locus_discrim_map *); - static inline bool equal (const locus_discrim_map *, - const locus_discrim_map *); -}; - -/* Trivial hash function for a location_t. ITEM is a pointer to - a hash table entry that maps a location_t to a discriminator. */ - -inline hashval_t -locus_discrim_hasher::hash (const locus_discrim_map *item) -{ - return item->location_line; -} - -/* Equality function for the locus-to-discriminator map. A and B - point to the two hash table entries to compare. */ - -inline bool -locus_discrim_hasher::equal (const locus_discrim_map *a, - const locus_discrim_map *b) -{ - return a->location_line == b->location_line; -} - -static hash_table<locus_discrim_hasher> *discriminator_per_locus; - /* Basic blocks and flowgraphs. */ static void make_blocks (gimple_seq); @@ -168,7 +131,6 @@ static edge gimple_try_redirect_by_replacing_jump (edge, basic_block); static inline bool stmt_starts_bb_p (gimple *, gimple *); static bool gimple_verify_flow_info (void); static void gimple_make_forwarder_block (edge); -static gimple *first_non_label_nondebug_stmt (basic_block); static bool verify_gimple_transaction (gtransaction *); static bool call_can_make_abnormal_goto (gimple *); @@ -247,12 +209,9 @@ build_gimple_cfg (gimple_seq seq) group_case_labels (); /* Create the edges of the flowgraph. */ - discriminator_per_locus = new hash_table<locus_discrim_hasher> (13); make_edges (); assign_discriminators (); cleanup_dead_labels (); - delete discriminator_per_locus; - discriminator_per_locus = NULL; } /* Look for ANNOTATE calls with loop annotation kind in BB; if found, remove @@ -1120,77 +1079,41 @@ gimple_find_sub_bbs (gimple_seq seq, gimple_stmt_iterator *gsi) return true; } -/* Find the next available discriminator value for LOCUS. The - discriminator distinguishes among several basic blocks that - share a common locus, allowing for more accurate sample-based - profiling. */ - -static int -next_discriminator_for_locus (int line) +/* Auto-profile needs discriminator to distinguish statements with same line + number (file name is ignored) which are in different basic block. This + map keeps track of current discriminator for a given line number. */ +struct discrim_entry { - struct locus_discrim_map item; - struct locus_discrim_map **slot; - - item.location_line = line; - item.discriminator = 0; - slot = discriminator_per_locus->find_slot_with_hash (&item, line, INSERT); - gcc_assert (slot); - if (*slot == HTAB_EMPTY_ENTRY) - { - *slot = XNEW (struct locus_discrim_map); - gcc_assert (*slot); - (*slot)->location_line = line; - (*slot)->discriminator = 0; - } - (*slot)->discriminator++; - return (*slot)->discriminator; -} - -/* Return TRUE if LOCUS1 and LOCUS2 refer to the same source line. */ - -static bool -same_line_p (location_t locus1, expanded_location *from, location_t locus2) -{ - expanded_location to; - - if (locus1 == locus2) - return true; - - to = expand_location (locus2); - - if (from->line != to.line) - return false; - if (from->file == to.file) - return true; - return (from->file != NULL - && to.file != NULL - && filename_cmp (from->file, to.file) == 0); -} + /* ID of basic block we saw line number last time. */ + unsigned int bb_id; + /* Discriminator we used. */ + unsigned int discrim; +}; -/* Assign a unique discriminator value to all statements in block bb that - have the same line number as locus. */ +/* Return updated LOC with discriminator for use in basic block BB_ID. + MAP keeps track of current values. */ -static void -assign_discriminator (location_t locus, basic_block bb) +location_t +assign_discriminator (location_t loc, unsigned int bb_id, + hash_map<int_hash <int64_t, -1, -2>, discrim_entry> &map) { - gimple_stmt_iterator gsi; - int discriminator; - - if (locus == UNKNOWN_LOCATION) - return; - - expanded_location locus_e = expand_location (locus); - - discriminator = next_discriminator_for_locus (locus_e.line); - - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + bool existed; + discrim_entry &e = map.get_or_insert (LOCATION_LINE (loc), &existed); + gcc_checking_assert (!has_discriminator (loc)); + if (!existed) { - gimple *stmt = gsi_stmt (gsi); - location_t stmt_locus = gimple_location (stmt); - if (same_line_p (locus, &locus_e, stmt_locus)) - gimple_set_location (stmt, - location_with_discriminator (stmt_locus, discriminator)); + e.bb_id = bb_id; + e.discrim = 0; + return loc; + } + if (e.bb_id != bb_id) + { + e.bb_id = bb_id; + e.discrim++; } + if (e.discrim) + return location_with_discriminator (loc, e.discrim); + return loc; } /* Assign discriminators to statement locations. */ @@ -1198,92 +1121,54 @@ assign_discriminator (location_t locus, basic_block bb) static void assign_discriminators (void) { + hash_map<int_hash <int64_t, -1, -2>, discrim_entry> map (13); + unsigned int bb_id = 0; basic_block bb; - FOR_EACH_BB_FN (bb, cfun) { - edge e; - edge_iterator ei; - gimple_stmt_iterator gsi; - location_t curr_locus = UNKNOWN_LOCATION; - expanded_location curr_locus_e = {}; - int curr_discr = 0; - + location_t prev_loc = UNKNOWN_LOCATION, prev_replacement = UNKNOWN_LOCATION; /* Traverse the basic block, if two function calls within a basic block are mapped to the same line, assign a new discriminator because a call stmt could be a split point of a basic block. */ - for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) { gimple *stmt = gsi_stmt (gsi); - - /* Don't allow debug stmts to affect discriminators, but - allow them to take discriminators when they're on the - same line as the preceding nondebug stmt. */ - if (is_gimple_debug (stmt)) - { - if (curr_locus != UNKNOWN_LOCATION - && same_line_p (curr_locus, &curr_locus_e, - gimple_location (stmt))) - { - location_t loc = gimple_location (stmt); - location_t dloc = location_with_discriminator (loc, - curr_discr); - gimple_set_location (stmt, dloc); - } - continue; - } - if (curr_locus == UNKNOWN_LOCATION) - { - curr_locus = gimple_location (stmt); - curr_locus_e = expand_location (curr_locus); - } - else if (!same_line_p (curr_locus, &curr_locus_e, gimple_location (stmt))) - { - curr_locus = gimple_location (stmt); - curr_locus_e = expand_location (curr_locus); - curr_discr = 0; - } - else if (curr_discr != 0) + location_t loc = gimple_location (stmt); + if (loc == UNKNOWN_LOCATION) + continue; + if (loc == prev_loc) + gimple_set_location (stmt, prev_replacement); + else { - location_t loc = gimple_location (stmt); - location_t dloc = location_with_discriminator (loc, curr_discr); - gimple_set_location (stmt, dloc); + prev_loc = loc; + prev_replacement = assign_discriminator (loc, bb_id, map); + gimple_set_location (stmt, prev_replacement); } - /* Allocate a new discriminator for CALL stmt. */ + /* Break basic blocks after each call. This is requires so each + call site has unque discriminator. + More correctly, we can break after each statement that can possibly + terinate execution of the basic block, but for auto-profile this + precision is probably not useful. */ if (gimple_code (stmt) == GIMPLE_CALL) - curr_discr = next_discriminator_for_locus (curr_locus_e.line); - } - - gimple *last = last_nondebug_stmt (bb); - location_t locus = last ? gimple_location (last) : UNKNOWN_LOCATION; - if (locus == UNKNOWN_LOCATION) - continue; - - expanded_location locus_e = expand_location (locus); - - FOR_EACH_EDGE (e, ei, bb->succs) - { - gimple *first = first_non_label_nondebug_stmt (e->dest); - gimple *last = last_nondebug_stmt (e->dest); - - gimple *stmt_on_same_line = NULL; - if (first && same_line_p (locus, &locus_e, - gimple_location (first))) - stmt_on_same_line = first; - else if (last && same_line_p (locus, &locus_e, - gimple_location (last))) - stmt_on_same_line = last; - - if (stmt_on_same_line) { - if (has_discriminator (gimple_location (stmt_on_same_line)) - && !has_discriminator (locus)) - assign_discriminator (locus, bb); - else - assign_discriminator (locus, e->dest); + prev_loc = UNKNOWN_LOCATION; + bb_id++; } } + /* IF basic block has multiple sucessors, consdier every edge as a separate + block. */ + if (!single_succ_p (bb)) + bb_id++; + for (edge e : bb->succs) + if (e->goto_locus != UNKNOWN_LOCATION) + { + e->goto_locus = assign_discriminator (e->goto_locus, bb_id, map); + bb_id++; + } + bb_id++; } + } /* Create the edges for a GIMPLE_COND starting at block BB. */ @@ -2948,16 +2833,6 @@ first_stmt (basic_block bb) return stmt; } -/* Return the first non-label/non-debug statement in basic block BB. */ - -static gimple * -first_non_label_nondebug_stmt (basic_block bb) -{ - gimple_stmt_iterator i; - i = gsi_start_nondebug_after_labels_bb (bb); - return !gsi_end_p (i) ? gsi_stmt (i) : NULL; -} - /* Return the last statement in basic block BB. */ gimple * diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc index 56f80db..734b7ef 100644 --- a/gcc/tree-vect-loop.cc +++ b/gcc/tree-vect-loop.cc @@ -7316,17 +7316,17 @@ vectorizable_reduction (loop_vec_info loop_vinfo, unsigned reduc_chain_length = 0; bool only_slp_reduc_chain = true; stmt_info = NULL; - slp_tree slp_for_stmt_info = slp_node_instance->root; + slp_tree slp_for_stmt_info = NULL; + slp_tree vdef_slp = slp_node_instance->root; /* For double-reductions we start SLP analysis at the inner loop LC PHI which is the def of the outer loop live stmt. */ if (STMT_VINFO_DEF_TYPE (reduc_info) == vect_double_reduction_def) - slp_for_stmt_info = SLP_TREE_CHILDREN (slp_for_stmt_info)[0]; + vdef_slp = SLP_TREE_CHILDREN (vdef_slp)[0]; while (reduc_def != PHI_RESULT (reduc_def_phi)) { stmt_vec_info def = loop_vinfo->lookup_def (reduc_def); stmt_vec_info vdef = vect_stmt_to_vectorize (def); int reduc_idx = STMT_VINFO_REDUC_IDX (vdef); - if (reduc_idx == -1) { if (dump_enabled_p ()) @@ -7343,14 +7343,9 @@ vectorizable_reduction (loop_vec_info loop_vinfo, the SLP node with live lane zero the other live lanes also need to be identified as part of a reduction to be able to skip code generation for them. */ - if (slp_for_stmt_info) - { - for (auto s : SLP_TREE_SCALAR_STMTS (slp_for_stmt_info)) - if (STMT_VINFO_LIVE_P (s)) - STMT_VINFO_REDUC_DEF (vect_orig_stmt (s)) = phi_info; - } - else if (STMT_VINFO_LIVE_P (vdef)) - STMT_VINFO_REDUC_DEF (def) = phi_info; + for (auto s : SLP_TREE_SCALAR_STMTS (vdef_slp)) + if (STMT_VINFO_LIVE_P (s)) + STMT_VINFO_REDUC_DEF (vect_orig_stmt (s)) = phi_info; gimple_match_op op; if (!gimple_extract_op (vdef->stmt, &op)) { @@ -7369,12 +7364,16 @@ vectorizable_reduction (loop_vec_info loop_vinfo, "conversion in the reduction chain.\n"); return false; } + vdef_slp = SLP_TREE_CHILDREN (vdef_slp)[0]; } else { /* First non-conversion stmt. */ if (!stmt_info) - stmt_info = vdef; + { + stmt_info = vdef; + slp_for_stmt_info = vdef_slp; + } if (lane_reducing_op_p (op.code)) { @@ -7382,7 +7381,7 @@ vectorizable_reduction (loop_vec_info loop_vinfo, reduction. */ gcc_assert (reduc_idx > 0 && reduc_idx == (int) op.num_ops - 1); - slp_tree op_node = SLP_TREE_CHILDREN (slp_for_stmt_info)[0]; + slp_tree op_node = SLP_TREE_CHILDREN (vdef_slp)[0]; tree vectype_op = SLP_TREE_VECTYPE (op_node); tree type_op = TREE_TYPE (op.ops[0]); if (!vectype_op) @@ -7413,14 +7412,14 @@ vectorizable_reduction (loop_vec_info loop_vinfo, < GET_MODE_SIZE (SCALAR_TYPE_MODE (type_op)))) vectype_in = vectype_op; } - else + else if (!vectype_in) vectype_in = STMT_VINFO_VECTYPE (phi_info); + if (!REDUC_GROUP_FIRST_ELEMENT (vdef)) + vdef_slp = SLP_TREE_CHILDREN (vdef_slp)[reduc_idx]; } reduc_def = op.ops[reduc_idx]; reduc_chain_length++; - if (!stmt_info) - slp_for_stmt_info = SLP_TREE_CHILDREN (slp_for_stmt_info)[0]; } /* PHIs should not participate in patterns. */ gcc_assert (!STMT_VINFO_RELATED_STMT (phi_info)); |