diff options
52 files changed, 1183 insertions, 388 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index bc607cb..dd31eed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,7 +276,7 @@ check in changes outside of the parts of the compiler they maintain. Reviewers aarch64 port Alex Coplan <alex.coplan@arm.com> -aarch64 port Andrew Pinski <pinskia@gmail.com> +aarch64 port Andrew Pinski <andrew.pinski@oss.qualcomm.com> arm port (MVE) Christophe Lyon <christophe.lyon@arm.com> callgraph Martin Jambor <mjambor@suse.cz> C front end Marek Polacek <polacek@redhat.com> @@ -734,7 +734,7 @@ Sebastian Peryt speryt <sebastian.peryt@intel.com> Johannes Pfau jpfau <johannespfau@gmail.com> Gerald Pfeifer gerald <gerald@pfeifer.com> Kaushik Phatak kaushikp <kaushik.phatak@kpitcummins.com> -Andrew Pinski pinskia <pinskia@gmail.com> +Andrew Pinski pinskia <andrew.pinski@qualcomm.com> Nicolas Pitre nico <nico@cam.org> Michael Ploujnikov plouj <michael.ploujnikov@oracle.com> Paul Pluzhnikov ppluzhnikov <ppluzhnikov@google.com> @@ -959,6 +959,7 @@ Immad Mir <mir@sourceware.org> Gaius Mulley <gaiusmod2@gmail.com> Szabolcs Nagy <nsz@gcc.gnu.org> Andrew Pinski <quic_apinski@quicinc.com> +Andrew Pinski <andrew.pinski@oss.qualcomm.com> Siddhesh Poyarekar <siddhesh@gotplt.org> Ramana Radhakrishnan <ramanara@nvidia.com> Navid Rahimi <navidr@gcc.gnu.org> diff --git a/gcc/Makefile.in b/gcc/Makefile.in index cc79595..f21d692 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1856,6 +1856,7 @@ OBJS-libcommon = \ diagnostics/color.o \ diagnostics/context.o \ diagnostics/digraphs.o \ + diagnostics/dumping.o \ diagnostics/file-cache.o \ diagnostics/output-spec.o \ diagnostics/html-sink.o \ diff --git a/gcc/c-family/c-indentation.cc b/gcc/c-family/c-indentation.cc index bb214fc..d378464 100644 --- a/gcc/c-family/c-indentation.cc +++ b/gcc/c-family/c-indentation.cc @@ -330,7 +330,7 @@ should_warn_for_misleading_indentation (const token_indent_info &guard_tinfo, if (guard_loc == body_loc || body_loc == next_stmt_loc) return false; - const unsigned int tab_width = global_dc->m_tabstop; + const unsigned int tab_width = global_dc->get_column_options ().m_tabstop; /* They must be in the same file. */ if (next_stmt_exploc.file != body_exploc.file) diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 3fb12b9..0ec30e8 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1200,7 +1200,7 @@ c_common_post_options (const char **pfilename) flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc23; cpp_opts->unsigned_utf8char = flag_char8_t ? 1 : cpp_opts->unsigned_char; - cpp_opts->cpp_tabstop = global_dc->m_tabstop; + cpp_opts->cpp_tabstop = global_dc->get_column_options ().m_tabstop; if (flag_extern_tls_init) { diff --git a/gcc/common.opt b/gcc/common.opt index bf38f60..c58ac13 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1620,6 +1620,18 @@ fdiagnostics-minimum-margin-width= Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6) Set minimum width of left margin of source code when showing source. +fdiagnostics-show-nesting +Common Var(flag_diagnostics_show_nesting) Init(1) +Use indentation to show nesting of diagnostics in text output. + +fdiagnostics-show-nesting-locations +Common Var(flag_diagnostics_show_nesting_locations) Init(1) +Show location information when showing nested diagnostics. + +fdiagnostics-show-nesting-levels +Common Var(flag_diagnostics_show_nesting_levels) Init(0) +Show nesting levels as numbers when showing nested diagnostics. + fdisable- Common Joined RejectNegative Var(common_deferred_options) Defer -fdisable-[tree|rtl|ipa]-<pass>=range1+range2 Disable an optimization pass. diff --git a/gcc/common.opt.urls b/gcc/common.opt.urls index 38dd9d3..0bc36c4 100644 --- a/gcc/common.opt.urls +++ b/gcc/common.opt.urls @@ -640,6 +640,15 @@ UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-text fdiagnostics-minimum-margin-width= UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-minimum-margin-width) +fdiagnostics-show-nesting +UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-show-nesting) + +fdiagnostics-show-nesting-locations +UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-show-nesting-locations) + +fdiagnostics-show-nesting-levels +UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-show-nesting-levels) + fdisable- UrlSuffix(gcc/Developer-Options.html#index-fdisable-) diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index c427163..cd35c7a 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -250,7 +250,7 @@ erroneous_templates_t *erroneous_templates; issue an error if we later need to instantiate the template. */ static void -cp_adjust_diagnostic_info (diagnostics::context *context, +cp_adjust_diagnostic_info (const diagnostics::context &context, diagnostics::diagnostic_info *diagnostic) { if (diagnostic->m_kind == diagnostics::kind::error) @@ -258,7 +258,7 @@ cp_adjust_diagnostic_info (diagnostics::context *context, { diagnostic->m_option_id = OPT_Wtemplate_body; - if (context->m_permissive) + if (context.m_permissive) diagnostic->m_kind = diagnostics::kind::warning; bool existed; diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 7572e04..7d73046 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -277,9 +277,6 @@ namespace diagnostics { /* Compute the number of digits in the decimal representation of an integer. */ extern int num_digits (int); -extern char *get_cwe_url (int cwe); -extern const char *maybe_line_and_column (int line, int col); - } // namespace diagnostics #endif /* ! GCC_DIAGNOSTIC_H */ diff --git a/gcc/diagnostics/buffering.cc b/gcc/diagnostics/buffering.cc index 29f039f..a7747b5 100644 --- a/gcc/diagnostics/buffering.cc +++ b/gcc/diagnostics/buffering.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "diagnostics/buffering.h" #include "diagnostics/sink.h" +#include "diagnostics/dumping.h" namespace diagnostics { @@ -122,12 +123,12 @@ void buffer::dump (FILE *out, int indent) const { m_diagnostic_counters.dump (out, indent + 2); - fprintf (out, "%*sm_per_sink_buffers:\n", indent, ""); + dumping::emit_heading (out, indent, "m_per_sink_buffers"); if (m_per_sink_buffers) for (auto per_sink_buffer_ : *m_per_sink_buffers) per_sink_buffer_->dump (out, indent + 2); else - fprintf (out, "%*s(none)\n", indent + 2, ""); + dumping::emit_none (out, indent + 2); } bool diff --git a/gcc/diagnostics/column-options.h b/gcc/diagnostics/column-options.h new file mode 100644 index 0000000..86296e9 --- /dev/null +++ b/gcc/diagnostics/column-options.h @@ -0,0 +1,44 @@ +/* Options relating to the meaning of column numbers. + Copyright (C) 2000-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 GCC_DIAGNOSTICS_COLUMN_OPTIONS_H +#define GCC_DIAGNOSTICS_COLUMN_OPTIONS_H + +namespace diagnostics { + +/* A bundle of options relating to the meaning of column numbers. */ + +struct column_options +{ + int convert_column (file_cache &fc, + expanded_location s) const; + + /* What units to use when outputting the column number. */ + enum diagnostics_column_unit m_column_unit; + + /* The origin for the column number (1-based or 0-based typically). */ + int m_column_origin; + + /* The size of the tabstop for tab expansion. */ + int m_tabstop; +}; + +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_COLUMN_OPTIONS_H */ diff --git a/gcc/diagnostics/context.cc b/gcc/diagnostics/context.cc index a1441ca..c948246 100644 --- a/gcc/diagnostics/context.cc +++ b/gcc/diagnostics/context.cc @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/logical-locations.h" #include "diagnostics/buffering.h" #include "diagnostics/file-cache.h" +#include "diagnostics/dumping.h" #ifdef HAVE_TERMIOS_H # include <termios.h> @@ -176,12 +177,18 @@ context::initialize (int n_opts) m_client_aux_data = nullptr; m_lock = 0; m_inhibit_notes_p = false; + m_source_printing.colorize_source_p = false; m_source_printing.show_labels_p = false; m_source_printing.show_line_numbers_p = false; m_source_printing.min_margin_width = 0; m_source_printing.show_ruler_p = false; m_source_printing.show_event_links_p = false; + + m_column_options.m_column_unit = DIAGNOSTICS_COLUMN_UNIT_DISPLAY; + m_column_options.m_column_origin = 1; + m_column_options.m_tabstop = 8; + m_report_bug = false; m_extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_none; if (const char *var = getenv ("GCC_EXTRA_DIAGNOSTIC_OUTPUT")) @@ -192,9 +199,6 @@ context::initialize (int n_opts) m_extra_output_kind = EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2; /* Silently ignore unrecognized values. */ } - m_column_unit = DIAGNOSTICS_COLUMN_UNIT_DISPLAY; - m_column_origin = 1; - m_tabstop = 8; m_escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE; m_fixits_change_set = nullptr; m_diagnostic_groups.m_group_nesting_depth = 0; @@ -284,6 +288,33 @@ context::urls_init (int value) (m_reference_printer->get_url_format ()); } +void +context::set_show_nesting (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_nesting (val); +} + +void +context::set_show_nesting_locations (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_locations_in_nesting (val); +} + +void +context::set_show_nesting_levels (bool val) +{ + for (auto sink_ : m_sinks) + if (sink_->follows_reference_printer_p ()) + if (auto text_sink_ = sink_->dyn_cast_text_sink ()) + text_sink_->set_show_nesting_levels (val); +} + /* Create the file_cache, if not already created, and tell it how to translate files on input. */ void @@ -355,33 +386,37 @@ context::finish () /* Dump state of this diagnostics::context to OUT, for debugging. */ void -context::dump (FILE *out) const +context::dump (FILE *outfile) const { - fprintf (out, "diagnostics::context:\n"); - m_diagnostic_counters.dump (out, 2); - fprintf (out, " reference printer:\n"); - m_reference_printer->dump (out, 4); - fprintf (out, " output sinks:\n"); + dumping::emit_heading (outfile, 0, "diagnostics::context"); + m_diagnostic_counters.dump (outfile, 2); + dumping::emit_heading (outfile, 2, "reference printer"); + m_reference_printer->dump (outfile, 4); + dumping::emit_heading (outfile, 2, "output sinks"); if (m_sinks.length () > 0) { for (unsigned i = 0; i < m_sinks.length (); ++i) { - fprintf (out, " sink %i:\n", i); - m_sinks[i]->dump (out, 4); + dumping::emit_indent (outfile, 4); + const sink *s = m_sinks[i]; + fprintf (outfile, "sink %i (", i); + s->dump_kind (outfile); + fprintf (outfile, "):\n"); + s->dump (outfile, 6); } } else - fprintf (out, " (none):\n"); - fprintf (out, " diagnostic buffer:\n"); + dumping::emit_none (outfile, 4); + dumping::emit_heading (outfile, 2, "diagnostic buffer"); if (m_diagnostic_buffer) - m_diagnostic_buffer->dump (out, 4); + m_diagnostic_buffer->dump (outfile, 4); else - fprintf (out, " (none):\n"); - fprintf (out, " file cache:\n"); + dumping::emit_none (outfile, 4); + dumping::emit_heading (outfile, 2, "file cache"); if (m_file_cache) - m_file_cache->dump (out, 4); + m_file_cache->dump (outfile, 4); else - fprintf (out, " (none):\n"); + dumping::emit_none (outfile, 4); } /* Return true if sufficiently severe diagnostics have been seen that @@ -671,11 +706,22 @@ convert_column_unit (file_cache &fc, } } +/* Given an expanded_location, convert the column (which is in 1-based bytes) + to the requested units and origin. Return -1 if the column is + invalid (<= 0). */ +int +column_options::convert_column (file_cache &fc, + expanded_location s) const +{ + int one_based_col = convert_column_unit (fc, m_column_unit, m_tabstop, s); + if (one_based_col <= 0) + return -1; + return one_based_col + (m_column_origin - 1); +} + column_policy::column_policy (const context &dc) : m_file_cache (dc.get_file_cache ()), - m_column_unit (dc.m_column_unit), - m_column_origin (dc.m_column_origin), - m_tabstop (dc.m_tabstop) + m_column_options (dc.get_column_options ()) { } @@ -685,11 +731,7 @@ column_policy::column_policy (const context &dc) int column_policy::converted_column (expanded_location s) const { - int one_based_col = convert_column_unit (m_file_cache, - m_column_unit, m_tabstop, s); - if (one_based_col <= 0) - return -1; - return one_based_col + (m_column_origin - 1); + return m_column_options.convert_column (m_file_cache, s); } /* Return a string describing a location e.g. "foo.c:42:10". */ @@ -711,7 +753,7 @@ column_policy::get_location_text (const expanded_location &s, col = converted_column (s); } - const char *line_col = maybe_line_and_column (line, col); + const char *line_col = text_sink::maybe_line_and_column (line, col); return label_text::take (build_message_string ("%s%s%s:%s", locus_cs, file, line_col, locus_ce)); } @@ -1098,7 +1140,7 @@ context::get_any_inlining_info (diagnostic_info *diagnostic) /* Retrieve the locations into which the expression about to be diagnosed has been inlined, including those of all the callers all the way down the inlining stack. */ - m_set_locations_cb (this, diagnostic); + m_set_locations_cb (*this, diagnostic); else { /* When there's no callback use just the one location provided @@ -1249,7 +1291,7 @@ context::report_diagnostic (diagnostic_info *diagnostic) } if (m_adjust_diagnostic_info) - m_adjust_diagnostic_info (this, diagnostic); + m_adjust_diagnostic_info (*this, diagnostic); if (diagnostic->m_kind == kind::pedwarn) { @@ -1385,6 +1427,8 @@ context::report_diagnostic (diagnostic_info *diagnostic) sink_->on_report_diagnostic (*diagnostic, orig_diag_kind); } + const int tabstop = get_column_options ().m_tabstop; + switch (m_extra_output_kind) { default: @@ -1393,14 +1437,14 @@ context::report_diagnostic (diagnostic_info *diagnostic) print_parseable_fixits (get_file_cache (), m_reference_printer, diagnostic->m_richloc, DIAGNOSTICS_COLUMN_UNIT_BYTE, - m_tabstop); + tabstop); pp_flush (m_reference_printer); break; case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2: print_parseable_fixits (get_file_cache (), m_reference_printer, diagnostic->m_richloc, DIAGNOSTICS_COLUMN_UNIT_DISPLAY, - m_tabstop); + tabstop); pp_flush (m_reference_printer); break; } @@ -1690,7 +1734,7 @@ context::set_nesting_level (int new_level) void sink::dump (FILE *out, int indent) const { - fprintf (out, "%*sprinter:\n", indent, ""); + dumping::emit_heading (out, indent, "printer"); m_printer->dump (out, indent + 2); } @@ -1777,19 +1821,19 @@ counters::counters () void counters::dump (FILE *out, int indent) const { - fprintf (out, "%*scounts:\n", indent, ""); + dumping::emit_heading (out, indent, "counts"); bool none = true; for (int i = 0; i < static_cast<int> (kind::last_diagnostic_kind); i++) if (m_count_for_kind[i] > 0) { - fprintf (out, "%*s%s%i\n", - indent + 2, "", + dumping::emit_indent (out, indent + 2); + fprintf (out, "%s%i\n", get_text_for_kind (static_cast<enum kind> (i)), m_count_for_kind[i]); none = false; } if (none) - fprintf (out, "%*s(none)\n", indent + 2, ""); + dumping::emit_none (out, indent + 2); } void @@ -2015,8 +2059,8 @@ assert_location_text (const char *expected_loc_text, = DIAGNOSTICS_COLUMN_UNIT_BYTE) { diagnostics::selftest::test_context dc; - dc.m_column_unit = column_unit; - dc.m_column_origin = origin; + dc.get_column_options ().m_column_unit = column_unit; + dc.get_column_options ().m_column_origin = origin; expanded_location xloc; xloc.file = filename; @@ -2052,8 +2096,8 @@ test_get_location_text () assert_location_text ("foo.c:42:", "foo.c", 42, 10, false); assert_location_text ("foo.c:", "foo.c", 0, 10, false); - diagnostics::maybe_line_and_column (INT_MAX, INT_MAX); - diagnostics::maybe_line_and_column (INT_MIN, INT_MIN); + diagnostics::text_sink::maybe_line_and_column (INT_MAX, INT_MAX); + diagnostics::text_sink::maybe_line_and_column (INT_MIN, INT_MIN); { /* In order to test display columns vs byte columns, we need to create a diff --git a/gcc/diagnostics/context.h b/gcc/diagnostics/context.h index b6ec85c..dea4588 100644 --- a/gcc/diagnostics/context.h +++ b/gcc/diagnostics/context.h @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/option-id-manager.h" #include "diagnostics/context-options.h" #include "diagnostics/source-printing-options.h" +#include "diagnostics/column-options.h" #include "diagnostics/counters.h" namespace diagnostics { @@ -99,13 +100,11 @@ public: bool show_column, bool colorize) const; - int get_tabstop () const { return m_tabstop; } + int get_tabstop () const { return m_column_options.m_tabstop; } private: file_cache &m_file_cache; - enum diagnostics_column_unit m_column_unit; - int m_column_origin; - int m_tabstop; + column_options m_column_options; }; /* A bundle of state for printing locations within diagnostics @@ -265,7 +264,7 @@ public: friend class text_sink; friend class buffer; - typedef void (*set_locations_callback_t) (context *, + typedef void (*set_locations_callback_t) (const context &, diagnostic_info *); void initialize (int n_opts); @@ -393,6 +392,9 @@ public: } void set_show_path_depths (bool val) { m_show_path_depths = val; } void set_show_option_requested (bool val) { m_show_option_requested = val; } + void set_show_nesting (bool val); + void set_show_nesting_locations (bool val); + void set_show_nesting_levels (bool val); void set_max_errors (int val) { m_max_errors = val; } void set_escape_format (enum diagnostics_escape_format val) { @@ -572,7 +574,7 @@ public: } void - set_adjust_diagnostic_info_callback (void (*cb) (context *, + set_adjust_diagnostic_info_callback (void (*cb) (const context &, diagnostic_info *)) { m_adjust_diagnostic_info = cb; @@ -592,6 +594,9 @@ public: return m_source_printing; } + column_options &get_column_options () { return m_column_options; } + const column_options &get_column_options () const { return m_column_options; } + void set_caret_max_width (int value); private: @@ -708,7 +713,7 @@ private: /* Client hook to adjust properties of the given diagnostic that we're about to issue, such as its kind. */ - void (*m_adjust_diagnostic_info)(context *, diagnostic_info *); + void (*m_adjust_diagnostic_info)(const context &, diagnostic_info *); /* Owned by the context; this would be a std::unique_ptr if context had a proper ctor. */ @@ -739,6 +744,7 @@ private: bool m_inhibit_notes_p; source_printing_options m_source_printing; + column_options m_column_options; /* True if -freport-bug option is used. */ bool m_report_bug; @@ -748,17 +754,6 @@ private: -fdiagnostics-parseable-fixits and GCC_EXTRA_DIAGNOSTIC_OUTPUT. */ enum diagnostics_extra_output_kind m_extra_output_kind; -public: - /* What units to use when outputting the column number. */ - enum diagnostics_column_unit m_column_unit; - - /* The origin for the column number (1-based or 0-based typically). */ - int m_column_origin; - - /* The size of the tabstop for tab expansion. */ - int m_tabstop; - -private: /* How should non-ASCII/non-printable bytes be escaped when a diagnostic suggests escaping the source code on output. */ enum diagnostics_escape_format m_escape_format; diff --git a/gcc/diagnostics/dumping.cc b/gcc/diagnostics/dumping.cc new file mode 100644 index 0000000..f0366a5 --- /dev/null +++ b/gcc/diagnostics/dumping.cc @@ -0,0 +1,116 @@ +/* Utilities for implementing "dump" functions for the diagnostics subsystem. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "diagnostics/dumping.h" + +namespace diagnostics { +namespace dumping { + +/* Emit indentation to OUTFILE for the start of a dump line. */ + +void +emit_indent (FILE *outfile, int indent) +{ + fprintf (outfile, "%*s", indent, ""); +} + +/* Emit an indented line to OUTFILE showing a heading. */ + +void +emit_heading (FILE *outfile, int indent, + const char *text) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s:\n", text); +} + +/* Various specializattions that emit an indented line to OUTFILE + showing "label: value". */ + +template <> +void +emit_field<const char *> (FILE *outfile, int indent, + const char *label, const char *value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %s\n", label, value); +} + +template <> +void +emit_field<char *> (FILE *outfile, int indent, + const char *label, char *value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %s\n", label, value); +} + +template <> +void +emit_field<bool> (FILE *outfile, int indent, + const char *label, bool value) +{ + emit_field<const char *> (outfile, indent, label, + value ? "true" : "false"); +} + +template <> +void +emit_field<size_t> (FILE *outfile, int indent, + const char *label, size_t value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %zi\n", label, value); +} + +template <> +void +emit_field<int> (FILE *outfile, int indent, + const char *label, int value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %i\n", label, value); +} + +template <> +void +emit_field<unsigned> (FILE *outfile, int indent, + const char *label, unsigned value) +{ + emit_indent (outfile, indent); + fprintf (outfile, "%s: %u\n", label, value); +} + +/* Emit an indented line to OUTFILE reading "(none)". */ + +void +emit_none (FILE *outfile, int indent) +{ + emit_indent (outfile, indent); + fprintf (outfile, "(none)\n"); +} + + +} // namespace dumping { +} // namespace diagnostics diff --git a/gcc/diagnostics/dumping.h b/gcc/diagnostics/dumping.h new file mode 100644 index 0000000..08c7ee4 --- /dev/null +++ b/gcc/diagnostics/dumping.h @@ -0,0 +1,43 @@ +/* Utilities for implementing "dump" functions for the diagnostics subsystem. + 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_DIAGNOSTICS_DUMP_H +#define GCC_DIAGNOSTICS_DUMP_H + +namespace diagnostics { +namespace dumping { + +extern void emit_indent (FILE *outfile, int indent); +extern void emit_heading (FILE *outfile, int indent, + const char *text); + +template <typename T> +extern void emit_field (FILE *outfile, int indent, + const char *label, T value); + +extern void emit_none (FILE *outfile, int indent); + +#define DIAGNOSTICS_DUMPING_EMIT_FIELD(FLAG) \ + dumping::emit_field (outfile, indent, #FLAG, FLAG) + +} // namespace dumping +} // namespace diagnostics + +#endif /* ! GCC_DIAGNOSTICS_DUMP_H */ diff --git a/gcc/diagnostics/file-cache.cc b/gcc/diagnostics/file-cache.cc index febeb03..9acf82e 100644 --- a/gcc/diagnostics/file-cache.cc +++ b/gcc/diagnostics/file-cache.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "cpplib.h" #include "diagnostics/file-cache.h" +#include "diagnostics/dumping.h" #include "selftest.h" #ifndef HAVE_ICONV @@ -473,7 +474,8 @@ file_cache::dump (FILE *out, int indent) const { for (size_t i = 0; i < m_num_file_slots; ++i) { - fprintf (out, "%*sslot[%i]:\n", indent, "", (int)i); + dumping::emit_indent (out, indent); + fprintf (out, "slot[%i]:\n", (int)i); m_file_slots[i].dump (out, indent + 2); } } @@ -541,27 +543,35 @@ file_cache_slot::dump (FILE *out, int indent) const { if (!m_file_path) { - fprintf (out, "%*s(unused)\n", indent, ""); + dumping::emit_indent (out, indent); + fprintf (out, "(unused)\n"); return; } - fprintf (out, "%*sfile_path: %s\n", indent, "", m_file_path); - fprintf (out, "%*sfp: %p\n", indent, "", (void *)m_fp); - fprintf (out, "%*sneeds_read_p: %i\n", indent, "", (int)needs_read_p ()); - fprintf (out, "%*sneeds_grow_p: %i\n", indent, "", (int)needs_grow_p ()); - fprintf (out, "%*suse_count: %i\n", indent, "", m_use_count); - fprintf (out, "%*ssize: %zi\n", indent, "", m_size); - fprintf (out, "%*snb_read: %zi\n", indent, "", m_nb_read); - fprintf (out, "%*sstart_line_idx: %zi\n", indent, "", m_line_start_idx); - fprintf (out, "%*sline_num: %zi\n", indent, "", m_line_num); - fprintf (out, "%*smissing_trailing_newline: %i\n", - indent, "", (int)m_missing_trailing_newline); - fprintf (out, "%*sline records (%i):\n", - indent, "", m_line_record.length ()); + dumping::emit_field (out, indent, "file_path", m_file_path); + { + dumping::emit_indent (out, indent); + fprintf (out, "fp: %p\n", (void *)m_fp); + } + dumping::emit_field (out, indent, "needs_read_p", needs_read_p ()); + dumping::emit_field (out, indent, "needs_grow_p", needs_grow_p ()); + dumping::emit_field (out, indent, "use_count", m_use_count); + dumping::emit_field (out, indent, "size", m_size); + dumping::emit_field (out, indent, "nb_read", m_nb_read); + dumping::emit_field (out, indent, "start_line_idx", m_line_start_idx); + dumping::emit_field (out, indent, "line_num", m_line_num); + dumping::emit_field (out, indent, "missing_trailing_newline", + (int)m_missing_trailing_newline); + { + dumping::emit_indent (out, indent); + fprintf (out, "line records (%i):\n", m_line_record.length ()); + } int idx = 0; for (auto &line : m_line_record) - fprintf (out, "%*s[%i]: line %zi: byte offsets: %zi-%zi\n", - indent + 2, "", - idx++, line.line_num, line.start_pos, line.end_pos); + { + dumping::emit_indent (out, indent); + fprintf (out, "[%i]: line %zi: byte offsets: %zi-%zi\n", + idx++, line.line_num, line.start_pos, line.end_pos); + } } /* Returns TRUE iff the cache would need to be filled with data coming diff --git a/gcc/diagnostics/html-sink.cc b/gcc/diagnostics/html-sink.cc index 934d8e2..448d461 100644 --- a/gcc/diagnostics/html-sink.cc +++ b/gcc/diagnostics/html-sink.cc @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/output-file.h" #include "diagnostics/buffering.h" #include "diagnostics/paths.h" +#include "diagnostics/dumping.h" #include "diagnostics/client-data-hooks.h" #include "selftest.h" #include "diagnostics/selftest-context.h" @@ -61,6 +62,16 @@ html_generation_options::html_generation_options () { } +void +html_generation_options::dump (FILE *outfile, int indent) const +{ + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_css); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_javascript); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_state_diagrams); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_state_diagrams_sarif); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_state_diagrams_dot_src); +} + class html_builder; /* Concrete buffering implementation subclass for HTML output. */ @@ -116,6 +127,8 @@ public: const line_maps *line_maps, const html_generation_options &html_gen_opts); + void dump (FILE *out, int indent) const; + void set_main_input_filename (const char *name); @@ -223,11 +236,12 @@ make_span (std::string class_) void html_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*shtml_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "html_sink_buffer"); int idx = 0; for (auto &result : m_results) { - fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx); + dumping::emit_indent (out, indent + 2); + fprintf (out, "result[%i]:\n", idx); result->dump (out); fprintf (out, "\n"); ++idx; @@ -470,6 +484,13 @@ html_builder::html_builder (context &dc, } void +html_builder::dump (FILE *out, int indent) const +{ + dumping::emit_heading (out, indent, "HTML generation options"); + m_html_gen_opts.dump (out, indent + 2); +} + +void html_builder::set_main_input_filename (const char *name) { gcc_assert (m_title_element); @@ -539,7 +560,7 @@ html_builder::on_report_diagnostic (const diagnostic_info &diagnostic, } } -// For ease of comparison with experimental-nesting-show-levels=yes +// For ease of comparison with show-nesting-levels=yes static void add_nesting_level_attr (xml::element &element, @@ -1333,8 +1354,9 @@ public: void dump (FILE *out, int indent) const override { - fprintf (out, "%*shtml_sink\n", indent, ""); sink::dump (out, indent); + dumping::emit_heading (out, indent, "html_builder"); + m_builder.dump (out, indent + 2); } void @@ -1439,12 +1461,10 @@ public: { m_builder.flush_to_file (m_output_file.get_open_file ()); } - void dump (FILE *out, int indent) const override + void dump_kind (FILE *out) const override { - fprintf (out, "%*shtml_file_sink: %s\n", - indent, "", + fprintf (out, "html_file_sink: %s", m_output_file.get_filename ()); - sink::dump (out, indent); } bool machine_readable_stderr_p () const final override { @@ -1607,6 +1627,10 @@ private: : html_sink (dc, line_maps, html_gen_opts) { } + void dump_kind (FILE *out) const final override + { + fprintf (out, "html_buffered_sink"); + } bool machine_readable_stderr_p () const final override { return true; diff --git a/gcc/diagnostics/html-sink.h b/gcc/diagnostics/html-sink.h index d86bde8..d25ceea 100644 --- a/gcc/diagnostics/html-sink.h +++ b/gcc/diagnostics/html-sink.h @@ -30,6 +30,8 @@ struct html_generation_options { html_generation_options (); + void dump (FILE *out, int indent) const; + bool m_css; bool m_javascript; diff --git a/gcc/diagnostics/metadata.h b/gcc/diagnostics/metadata.h index c28f982..39291ec 100644 --- a/gcc/diagnostics/metadata.h +++ b/gcc/diagnostics/metadata.h @@ -119,6 +119,8 @@ class metadata const lazy_digraphs *m_lazy_digraphs; }; +extern char *get_cwe_url (int cwe); + } // namespace diagnostics #endif /* ! GCC_DIAGNOSTICS_METADATA_H */ diff --git a/gcc/diagnostics/output-spec.cc b/gcc/diagnostics/output-spec.cc index 83f128c..13565f9 100644 --- a/gcc/diagnostics/output-spec.cc +++ b/gcc/diagnostics/output-spec.cc @@ -368,7 +368,7 @@ text_scheme_handler::make_sink (const context &ctxt, const scheme_name_and_params &parsed_arg) const { bool show_color = pp_show_color (dc.get_reference_printer ()); - bool show_nesting = false; + bool show_nesting = true; bool show_locations_in_nesting = true; bool show_levels = false; for (auto& iter : parsed_arg.m_kvs) @@ -381,21 +381,21 @@ text_scheme_handler::make_sink (const context &ctxt, return nullptr; continue; } - if (key == "experimental-nesting") + if (key == "show-nesting") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_nesting)) return nullptr; continue; } - if (key == "experimental-nesting-show-locations") + if (key == "show-nesting-locations") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_locations_in_nesting)) return nullptr; continue; } - if (key == "experimental-nesting-show-levels") + if (key == "show-nesting-levels") { if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_levels)) return nullptr; @@ -405,9 +405,9 @@ text_scheme_handler::make_sink (const context &ctxt, /* Key not found. */ auto_vec<const char *> known_keys; known_keys.safe_push ("color"); - known_keys.safe_push ("experimental-nesting"); - known_keys.safe_push ("experimental-nesting-show-locations"); - known_keys.safe_push ("experimental-nesting-show-levels"); + known_keys.safe_push ("show-nesting"); + known_keys.safe_push ("show-nesting-locations"); + known_keys.safe_push ("show-nesting-levels"); ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (), known_keys); return nullptr; diff --git a/gcc/diagnostics/sarif-sink.cc b/gcc/diagnostics/sarif-sink.cc index 4738ae9..0c4d77a 100644 --- a/gcc/diagnostics/sarif-sink.cc +++ b/gcc/diagnostics/sarif-sink.cc @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/paths.h" #include "diagnostics/sink.h" #include "diagnostics/buffering.h" +#include "diagnostics/dumping.h" #include "json.h" #include "cpplib.h" #include "diagnostics/logical-locations.h" @@ -704,6 +705,14 @@ sarif_serialization_format_json::write_to_file (FILE *outf, fprintf (outf, "\n"); } +void +sarif_serialization_format_json::dump (FILE *outfile, int indent) const +{ + dumping::emit_indent (outfile, indent); + fprintf (outfile, "json\n"); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_formatted); +} + /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr and -fdiagnostics-format=sarif-file). @@ -760,6 +769,8 @@ public: const sarif_generation_options &sarif_gen_opts); ~sarif_builder (); + void dump (FILE *out, int indent) const; + void set_printer (pretty_printer &printer) { m_printer = &printer; @@ -1674,7 +1685,7 @@ sarif_builder::sarif_builder (diagnostics::context &dc, (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()), m_run_graphs (std::make_unique<sarif_array_of_unique<sarif_graph>> ()), - m_tabstop (dc.m_tabstop), + m_tabstop (dc.get_column_options ().m_tabstop), m_serialization_format (std::move (serialization_format)), m_sarif_gen_opts (sarif_gen_opts), m_next_result_idx (0), @@ -1699,6 +1710,15 @@ sarif_builder::~sarif_builder () } } +void +sarif_builder::dump (FILE *out, int indent) const +{ + dumping::emit_heading (out, indent, "serialization format"); + m_serialization_format->dump (out, indent + 2); + dumping::emit_heading (out, indent, "SARIF generation options"); + m_sarif_gen_opts.dump (out, indent + 2); +} + /* Functions at which to stop the backtrace print. It's not particularly helpful to print the callers of these functions. */ @@ -3803,11 +3823,12 @@ sarif_builder::make_artifact_content_object (const char *text) const void sarif_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*ssarif_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "sarif_sink_buffer"); int idx = 0; for (auto &result : m_results) { - fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx); + dumping::emit_indent (out, indent + 2); + fprintf (out, "result[%i]:\n", idx); result->dump (out, true); fprintf (out, "\n"); ++idx; @@ -3862,8 +3883,9 @@ public: void dump (FILE *out, int indent) const override { - fprintf (out, "%*ssarif_sink\n", indent, ""); sink::dump (out, indent); + dumping::emit_heading (out, indent, "sarif_builder"); + m_builder.dump (out, indent + 2); } void @@ -3973,6 +3995,10 @@ public: { m_builder.flush_to_file (m_stream); } + void dump_kind (FILE *out) const override + { + fprintf (out, "sarif_stream_sink"); + } bool machine_readable_stderr_p () const final override { return m_stream == stderr; @@ -4001,12 +4027,10 @@ public: { m_builder.flush_to_file (m_output_file.get_open_file ()); } - void dump (FILE *out, int indent) const override + void dump_kind (FILE *out) const override { - fprintf (out, "%*ssarif_file_sink: %s\n", - indent, "", + fprintf (out, "sarif_file_sink: %s", m_output_file.get_filename ()); - sink::dump (out, indent); } bool machine_readable_stderr_p () const final override { @@ -4319,6 +4343,29 @@ sarif_generation_options::sarif_generation_options () { } +static const char * +get_dump_string_for_sarif_version (enum sarif_version version) +{ + switch (version) + { + default: + gcc_unreachable (); + case sarif_version::v2_1_0: + return "v2_1_0"; + case sarif_version::v2_2_prerelease_2024_08_08: + return "v2_2_prerelease_2024_08_08"; + } +} + +void +sarif_generation_options::dump (FILE *outfile, int indent) const +{ + dumping::emit_field (outfile, indent, + "m_version", + get_dump_string_for_sarif_version (m_version)); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_state_graph); +} + #if CHECKING_P namespace selftest { @@ -4406,10 +4453,10 @@ public: test_sarif_diagnostic_context (const char *main_input_filename, const sarif_generation_options &sarif_gen_opts) { - auto sink_ = std::make_unique<buffered_sink> (*this, - line_table, - true, - sarif_gen_opts); + auto sink_ = std::make_unique<sarif_buffered_sink> (*this, + line_table, + true, + sarif_gen_opts); m_sink = sink_.get (); // borrowed init_sarif_sink (*this, std::move (sink_)); m_sink->set_main_input_filename (main_input_filename); @@ -4424,10 +4471,10 @@ public: sarif_result &get_result (size_t idx) { return m_sink->get_result (idx); } private: - class buffered_sink : public sarif_sink + class sarif_buffered_sink : public sarif_sink { public: - buffered_sink (context &dc, + sarif_buffered_sink (context &dc, const line_maps *line_maps, bool formatted, const sarif_generation_options &sarif_gen_opts) @@ -4436,6 +4483,10 @@ private: sarif_gen_opts) { } + void dump_kind (FILE *out) const final override + { + fprintf (out, "sarif_buffered_sink"); + } bool machine_readable_stderr_p () const final override { return false; @@ -4446,7 +4497,7 @@ private: } }; - buffered_sink *m_sink; // borrowed + sarif_buffered_sink *m_sink; // borrowed }; /* Test making a sarif_location for a complex rich_location diff --git a/gcc/diagnostics/sarif-sink.h b/gcc/diagnostics/sarif-sink.h index 9f8a73f..e6f897b 100644 --- a/gcc/diagnostics/sarif-sink.h +++ b/gcc/diagnostics/sarif-sink.h @@ -73,6 +73,7 @@ public: virtual ~sarif_serialization_format () {} virtual void write_to_file (FILE *outf, const json::value &top) = 0; + virtual void dump (FILE *out, int indent) const = 0; }; /* Concrete subclass for serializing SARIF as JSON. */ @@ -85,6 +86,7 @@ public: { } void write_to_file (FILE *outf, const json::value &top) final override; + void dump (FILE *out, int indent) const final override; private: bool m_formatted; @@ -108,6 +110,8 @@ struct sarif_generation_options { sarif_generation_options (); + void dump (FILE *out, int indent) const; + enum sarif_version m_version; bool m_state_graph; }; diff --git a/gcc/diagnostics/sink.h b/gcc/diagnostics/sink.h index ac4e0fb64..24eb707 100644 --- a/gcc/diagnostics/sink.h +++ b/gcc/diagnostics/sink.h @@ -36,6 +36,9 @@ class sink public: virtual ~sink () {} + virtual text_sink *dyn_cast_text_sink () { return nullptr; } + + virtual void dump_kind (FILE *out) const = 0; virtual void dump (FILE *out, int indent) const; /* Vfunc for notifying this format what the primary input file is, diff --git a/gcc/diagnostics/source-printing.cc b/gcc/diagnostics/source-printing.cc index 94b1c2d..aeda9ad 100644 --- a/gcc/diagnostics/source-printing.cc +++ b/gcc/diagnostics/source-printing.cc @@ -4412,7 +4412,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; diagnostics::source_print_policy policy (dc); layout test_layout (policy, richloc, nullptr); colorizer col (*dc.get_reference_printer (), @@ -4436,7 +4436,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; static const int small_width = 24; auto &source_printing_opts = dc.get_source_printing_options (); source_printing_opts.max_width = small_width - 4; @@ -6833,7 +6833,7 @@ test_tab_expansion (const line_table_case &case_) everything too. */ { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; rich_location richloc (line_table, linemap_position_for_column (line_table, first_non_ws_byte_col)); @@ -6846,7 +6846,7 @@ test_tab_expansion (const line_table_case &case_) as well. */ { test_context dc; - dc.m_tabstop = tabstop; + dc.get_column_options ().m_tabstop = tabstop; rich_location richloc (line_table, linemap_position_for_column (line_table, right_quote_byte_col)); diff --git a/gcc/diagnostics/text-sink.cc b/gcc/diagnostics/text-sink.cc index bcf91cf..a7ebb30 100644 --- a/gcc/diagnostics/text-sink.cc +++ b/gcc/diagnostics/text-sink.cc @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/diagram.h" #include "diagnostics/text-sink.h" #include "diagnostics/buffering.h" +#include "diagnostics/dumping.h" #include "text-art/theme.h" /* Disable warnings about quoting issues in the pp_xxx calls below @@ -76,7 +77,7 @@ text_sink_buffer::text_sink_buffer (sink &sink_) void text_sink_buffer::dump (FILE *out, int indent) const { - fprintf (out, "%*stext_sink_buffer:\n", indent, ""); + dumping::emit_heading (out, indent, "text_sink_buffer"); m_output_buffer.dump (out, indent + 2); } @@ -156,18 +157,19 @@ text_sink::~text_sink () } void -text_sink::dump (FILE *out, int indent) const -{ - fprintf (out, "%*stext_sink\n", indent, ""); - fprintf (out, "%*sm_follows_reference_printer: %s\n", - indent, "", - m_follows_reference_printer ? "true" : "false"); - sink::dump (out, indent); - fprintf (out, "%*ssaved_output_buffer:\n", indent + 2, ""); +text_sink::dump (FILE *outfile, int indent) const +{ + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_follows_reference_printer); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_nesting); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_locations_in_nesting); + DIAGNOSTICS_DUMPING_EMIT_FIELD (m_show_nesting_levels); + + sink::dump (outfile, indent); + dumping::emit_heading (outfile, indent, "saved_output_buffer"); if (m_saved_output_buffer) - m_saved_output_buffer->dump (out, indent + 4); + m_saved_output_buffer->dump (outfile, indent + 2); else - fprintf (out, "%*s(none):\n", indent + 4, ""); + dumping::emit_none (outfile, indent + 2); } void @@ -612,7 +614,7 @@ text_sink::get_location_text (const expanded_location &s) const The result is a statically allocated buffer. */ const char * -maybe_line_and_column (int line, int col) +text_sink::maybe_line_and_column (int line, int col) { static char result[32]; diff --git a/gcc/diagnostics/text-sink.h b/gcc/diagnostics/text-sink.h index 5c60976..f280e72 100644 --- a/gcc/diagnostics/text-sink.h +++ b/gcc/diagnostics/text-sink.h @@ -51,6 +51,12 @@ public: {} ~text_sink (); + text_sink *dyn_cast_text_sink () final override { return this; } + + void dump_kind (FILE *out) const override + { + fprintf (out, "text_sink"); + } void dump (FILE *out, int indent) const override; std::unique_ptr<per_sink_buffer> @@ -127,6 +133,8 @@ public: return m_source_printing; } + static const char *maybe_line_and_column (int line, int col); + protected: void print_any_cwe (const diagnostic_info &diagnostic); void print_any_rules (const diagnostic_info &diagnostic); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 33ae98c..28466c4 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -322,6 +322,9 @@ Objective-C and Objective-C++ Dialects}. -fno-diagnostics-show-cwe -fno-diagnostics-show-rules -fno-diagnostics-show-highlight-colors +-fno-diagnostics-show-nesting +-fno-diagnostics-show-nesting-locations +-fdiagnostics-show-nesting-levels -fdiagnostics-minimum-margin-width=@var{width} -fdiagnostics-parseable-fixits -fdiagnostics-generate-patch -fdiagnostics-show-template-tree -fno-elide-type @@ -5429,7 +5432,8 @@ options: -fdiagnostics-urls=never -fdiagnostics-path-format=separate-events -fdiagnostics-text-art-charset=none --fno-diagnostics-show-event-links} +-fno-diagnostics-show-event-links +-fno-diagnostics-show-nesting} In the future, if GCC changes the default appearance of its diagnostics, the corresponding option to disable the new behavior will be added to this list. @@ -6051,6 +6055,40 @@ emoji variant of the character). The default is @samp{emoji}, except when the environment variable @env{LANG} is set to @samp{C}, in which case the default is @samp{ascii}. +@opindex fno-diagnostics-show-nesting +@opindex fdiagnostics-show-nesting +@item -fno-diagnostics-show-nesting +Some GCC diagnostics have an internal tree-like structure of nested +sub-diagnostics, such as for problems when instantiating C++ templates. + +By default GCC uses indentation and bullet points in its text output to +show the nesting structure of these diagnostics, moves location +information to separate lines to make the structure clearer, and +eliminates redundant repeated information. + +Selecting @option{-fno-diagnostics-show-nesting} suppresses this +indentation, reformatting, and elision, restoring an older `look'' for the +diagnostics. + +@opindex fno-diagnostics-show-nesting-locations +@opindex fdiagnostics-show-nesting-locations +@item -fno-diagnostics-show-nesting-locations + +When @option{fdiagnostics-show-nesting} is enabled, file names and +line- and column- numbers are displayed on separate lines from the +messages. This location information can be disabled altogether with +@option{-fno-diagnostics-show-nesting-locations}. +This option exists for use by GCC developers, for writing DejaGnu test cases. + +@opindex fdiagnostics-show-nesting-levels +@opindex fno-diagnostics-show-nesting-levels +@item -fdiagnostics-show-nesting-levels +When @option{fdiagnostics-show-nesting} is enabled, use +@option{fdiagnostics-show-nesting-levels} to also display numbers +showing the depth of the nesting. +This option exists for use by GCC developers for debugging nested +diagnostics, but may be of use to plugin authors. + @opindex fdiagnostics-format @item -fdiagnostics-format=@var{FORMAT} Select a different format for printing diagnostics. @@ -6099,18 +6137,18 @@ Supported keys are: Override colorization settings from @option{-fdiagnostics-color} for this text output. -@item experimental-nesting=@r{[}yes@r{|}no@r{]} -Enable an experimental mode that emphasizes hierarchical relationships -within diagnostics messages, displaying location information on separate -lines. +@item show-nesting=@r{[}yes@r{|}no@r{]} +Enable a mode that emphasizes hierarchical relationships +within diagnostics messages, as per @option{-fdiagnostics-show-nesting}. +Defaults to @code{yes}. -@item experimental-nesting-show-locations=@r{[}yes@r{|}no@r{]} -If @code{experimental-nesting=yes}, then by default locations are +@item show-nesting-locations=@r{[}yes@r{|}no@r{]} +If @code{show-nesting=yes}, then by default locations are shown; set this key to @code{no} to disable printing such locations. This exists for use by GCC developers, for writing DejaGnu test cases. -@item experimental-nesting-show-levels=@r{[}yes@r{|}no@r{]} -This is a debugging option for use with @code{experimental-nesting=yes}. +@item show-nesting-levels=@r{[}yes@r{|}no@r{]} +This is a debugging option for use with @code{show-nesting=yes}. Set this key to @code{yes} to print explicit nesting levels in the output. This exists for use by GCC developers. diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc index 03fca97..dbe3ad0 100644 --- a/gcc/lto-wrapper.cc +++ b/gcc/lto-wrapper.cc @@ -320,6 +320,9 @@ merge_and_complain (vec<cl_decoded_option> &decoded_options, case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_show_option: case OPT_fdiagnostics_show_location_: + case OPT_fdiagnostics_show_nesting: + case OPT_fdiagnostics_show_nesting_locations: + case OPT_fdiagnostics_show_nesting_levels: case OPT_fshow_column: case OPT_fcommon: case OPT_fgnu_tm: @@ -739,6 +742,9 @@ append_compiler_options (obstack *argv_obstack, vec<cl_decoded_option> opts) case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_show_option: case OPT_fdiagnostics_show_location_: + case OPT_fdiagnostics_show_nesting: + case OPT_fdiagnostics_show_nesting_locations: + case OPT_fdiagnostics_show_nesting_levels: case OPT_fshow_column: case OPT_fPIC: case OPT_fpic: @@ -801,6 +807,9 @@ append_diag_options (obstack *argv_obstack, vec<cl_decoded_option> opts) case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_show_option: case OPT_fdiagnostics_show_location_: + case OPT_fdiagnostics_show_nesting: + case OPT_fdiagnostics_show_nesting_locations: + case OPT_fdiagnostics_show_nesting_levels: case OPT_fshow_column: break; default: diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc index e6d7f4d..379402e 100644 --- a/gcc/opts-common.cc +++ b/gcc/opts-common.cc @@ -1096,7 +1096,8 @@ decode_cmdline_options_to_array (unsigned int argc, const char **argv, "-fdiagnostics-urls=never", "-fdiagnostics-path-format=separate-events", "-fdiagnostics-text-art-charset=none", - "-fno-diagnostics-show-event-links" + "-fno-diagnostics-show-event-links", + "-fno-diagnostics-show-nesting" /* We don't put "-fno-diagnostics-show-highlight-colors" here as -fdiagnostics-color=never makes it redundant. */ }; diff --git a/gcc/opts.cc b/gcc/opts.cc index b6d25bf..a02d017 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -3016,11 +3016,12 @@ common_handle_option (struct gcc_options *opts, break; case OPT_fdiagnostics_column_unit_: - dc->m_column_unit = (enum diagnostics_column_unit)value; + dc->get_column_options ().m_column_unit + = (enum diagnostics_column_unit)value; break; case OPT_fdiagnostics_column_origin_: - dc->m_column_origin = value; + dc->get_column_options ().m_column_origin = value; break; case OPT_fdiagnostics_escape_format_: @@ -3051,6 +3052,18 @@ common_handle_option (struct gcc_options *opts, dc->set_show_option_requested (value); break; + case OPT_fdiagnostics_show_nesting: + dc->set_show_nesting (value); + break; + + case OPT_fdiagnostics_show_nesting_locations: + dc->set_show_nesting_locations (value); + break; + + case OPT_fdiagnostics_show_nesting_levels: + dc->set_show_nesting_levels (value); + break; + case OPT_fdiagnostics_minimum_margin_width_: dc->get_source_printing_options ().min_margin_width = value; break; @@ -3408,7 +3421,7 @@ common_handle_option (struct gcc_options *opts, case OPT_ftabstop_: /* It is documented that we silently ignore silly values. */ if (value >= 1 && value <= 100) - dc->m_tabstop = value; + dc->get_column_options ().m_tabstop = value; break; case OPT_freport_bug: @@ -3879,6 +3892,9 @@ gen_command_line_string (cl_decoded_option *options, case OPT_fdiagnostics_show_line_numbers: case OPT_fdiagnostics_color_: case OPT_fdiagnostics_format_: + case OPT_fdiagnostics_show_nesting: + case OPT_fdiagnostics_show_nesting_locations: + case OPT_fdiagnostics_show_nesting_levels: case OPT_fverbose_asm: case OPT____: case OPT__sysroot_: diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C index 0cb1610..5b5e3fe 100644 --- a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C @@ -1,6 +1,6 @@ // { dg-do compile { target c++17 } } // { dg-options "-fconcepts" } -// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } +// { dg-additional-options "-fdiagnostics-set-output=text:show-nesting=yes,show-nesting-locations=no" } struct dog {}; struct cat {}; diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C index e642676..a071b55 100644 --- a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C @@ -1,6 +1,6 @@ // { dg-do compile { target c++17 } } // { dg-options "-fconcepts" } -// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } +// { dg-additional-options "-fdiagnostics-set-output=text:show-nesting=yes,show-nesting-locations=no" } // { dg-additional-options "-fconcepts-diagnostics-depth=3" } struct dog {}; diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C index cc15f11..9530bc1 100644 --- a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C @@ -1,6 +1,6 @@ // { dg-do compile { target c++17 } } // { dg-options "-fconcepts" } -// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } +// { dg-additional-options "-fdiagnostics-set-output=text:show-nesting=yes,show-nesting-locations=no" } struct dog{}; struct cat{}; diff --git a/gcc/testsuite/g++.dg/torture/noncall-eh-1.C b/gcc/testsuite/g++.dg/torture/noncall-eh-1.C new file mode 100644 index 0000000..ea8fd79 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/noncall-eh-1.C @@ -0,0 +1,26 @@ +// { dg-do compile } +// For slim LTO there's no optimized dump +// { dg-skip-if "" { *-*-* } { "-flto" } { "" } } +// { dg-additional-options "-fnon-call-exceptions -fexceptions -fdump-tree-optimized-eh" } + +// PR tree-optimization/120599 +// Copying prop for aggregates should not touch `a = *__val` since that statement +// can throw (internally) so we need to be able to keep the landing pad. + +struct RefitOption { + char subtype; + int string; +} n; +void h(RefitOption) __attribute__((nothrow)); +void k(RefitOption *__val, RefitOption a) +{ + try { + a = *__val; + RefitOption __trans_tmp_2 = a; + h(__trans_tmp_2); + } + catch(...){} +} + +// Make sure There is a landing pad for the non-call exception from the aggregate load. +// { dg-final { scan-tree-dump "LP " "optimized" } } diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-no-show-nesting.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-no-show-nesting.c new file mode 100644 index 0000000..3492899 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-no-show-nesting.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fno-diagnostics-show-nesting" } */ + +extern void foo (void); + +void test_nesting (void) +{ + foo (); /* { dg-error "top-level error" } */ +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-show-nesting.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-show-nesting.c new file mode 100644 index 0000000..8fc2edb --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-show-nesting.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-show-nesting" } */ + +extern void foo (void); + +void test_nesting (void) +{ + foo (); /* { dg-error "top-level error" } */ +} + +/* { dg-begin-multiline-output "" } + * child 0 + * grandchild 0 0 + * grandchild 0 1 + * grandchild 0 2 + * child 1 + * grandchild 1 0 + * grandchild 1 1 + * grandchild 1 2 + * child 2 + * grandchild 2 0 + * grandchild 2 1 + * grandchild 2 2 + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-show-levels.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-show-levels.c index f44c8eb..4be52fe 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-show-levels.c +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-show-levels.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-levels=yes" } */ +/* { dg-options "-fdiagnostics-set-output=text:show-nesting=yes,show-nesting-levels=yes" } */ extern void foo (void); diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-unicode.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-unicode.c index 39e29f7..c069c3f 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-unicode.c +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented-unicode.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes -fdiagnostics-text-art-charset=unicode" } */ +/* { dg-options "-fdiagnostics-set-output=text:show-nesting=yes -fdiagnostics-text-art-charset=unicode" } */ extern void foo (void); diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented.c index e103429..a35254d 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented.c +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-text-indented.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fdiagnostics-set-output=text:experimental-nesting=yes" } */ +/* { dg-options "-fdiagnostics-set-output=text:show-nesting=yes" } */ extern void foo (void); diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index 3bb6063..c7cc36c 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -112,6 +112,8 @@ set plugin_test_list [list \ diagnostic-test-graphs-html.c \ diagnostic-test-graphs-sarif.c } \ { diagnostic_plugin_test_nesting.cc \ + diagnostic-test-nesting-show-nesting.c \ + diagnostic-test-nesting-no-show-nesting.c \ diagnostic-test-nesting-text-plain.c \ diagnostic-test-nesting-text-indented.c \ diagnostic-test-nesting-text-indented-show-levels.c \ diff --git a/gcc/testsuite/gcc.dg/pr118946-1.c b/gcc/testsuite/gcc.dg/pr118946-1.c new file mode 100644 index 0000000..6cf2661 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr118946-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized -fdump-tree-forwprop1-details" } */ + +/* PR tree-optimization/118946 */ + +void f(char *a) +{ + char t[1024] = {}; + __builtin_memcpy(a, t, 10); +} + +/* We should be able to optimize the memcpy into a memset here. */ +/* { dg-final { scan-tree-dump-times "after previous" 1 "forwprop1"} } */ +/* { dg-final { scan-tree-dump-times "memset " 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-not "memcpy " "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr121422-1.c b/gcc/testsuite/gcc.dg/torture/pr121422-1.c new file mode 100644 index 0000000..136f80d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr121422-1.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ +/* PR tree-optimization/121422 */ + +struct s1 +{ + char a[4]; +}; +struct s1 b; +char t[4]; + +/* if both t and b startout zero initialized before this function, + t should end up being: + {0, 0, 1, 0} + while b.a should end up being: + {0, 0, 0, 1} +*/ +__attribute__((noipa,noinline)) +void f(void) +{ + b = (struct s1){}; + b.a[3] = 1; + /* This memcpy should stay a memcpy and not become memset. */ + __builtin_memcpy(&t[0], &b.a[1], 3*sizeof(t[0])); +} + + +int main() +{ + f(); + for(int i = 0; i < 4; i++) + { + if (t[i] != (i == 2 ? 1 : 0)) + __builtin_abort(); + } +} diff --git a/gcc/testsuite/gcc.dg/torture/pr121422-2.c b/gcc/testsuite/gcc.dg/torture/pr121422-2.c new file mode 100644 index 0000000..570559c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr121422-2.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ +/* PR tree-optimization/121422 */ + +struct s1 +{ + char a[4]; +}; +struct s1 b; +char t[4]; + +/* if both t and b startout zero initialized before this function, + t should end up being: + {0, 0, 1, 0} + while b.a should end up being: + {0, 0, 0, 1} +*/ +__attribute__((noipa,noinline)) +void f(void) +{ + __builtin_memset(&b.a[1], 0, 2); + b.a[3] = 1; + /* This memcpy should stay a memcpy and not become memset. */ + __builtin_memcpy(&t[0], &b.a[1], 3); +} + + +int main() +{ + f(); + for(int i = 0; i < 4; i++) + { + if (t[i] != (i == 2 ? 1 : 0)) + __builtin_abort(); + } +} + diff --git a/gcc/toplev.cc b/gcc/toplev.cc index d349d83..d264674 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -1094,6 +1094,12 @@ general_init (const char *argv0, bool init_signals, unique_argv original_argv) = global_options_init.x_flag_show_column; global_dc->set_show_highlight_colors (global_options_init.x_flag_diagnostics_show_highlight_colors); + global_dc->set_show_nesting + (global_options_init.x_flag_diagnostics_show_nesting); + global_dc->set_show_nesting_locations + (global_options_init.x_flag_diagnostics_show_nesting_locations); + global_dc->set_show_nesting_levels + (global_options_init.x_flag_diagnostics_show_nesting_levels); global_dc->set_internal_error_callback (internal_error_function); const unsigned lang_mask = lang_hooks.option_lang_mask (); global_dc->set_option_id_manager diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc index 3761fc0..20183c8 100644 --- a/gcc/tree-diagnostic.cc +++ b/gcc/tree-diagnostic.cc @@ -110,7 +110,7 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec, to the DIAGNOSTIC location. */ static void -set_inlining_locations (diagnostics::context *, +set_inlining_locations (const diagnostics::context &, diagnostics::diagnostic_info *diagnostic) { location_t loc = diagnostic_location (diagnostic); diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc index 43b1c9d..3d38d88 100644 --- a/gcc/tree-ssa-forwprop.cc +++ b/gcc/tree-ssa-forwprop.cc @@ -1190,117 +1190,55 @@ constant_pointer_difference (tree p1, tree p2) return NULL_TREE; } - -/* Optimize - a = {}; - b = a; - into - a = {}; - b = {}; - Similarly for memset (&a, ..., sizeof (a)); instead of a = {}; - and/or memcpy (&b, &a, sizeof (a)); instead of b = a; */ - +/* Helper function for optimize_aggr_zeroprop. + Props the zeroing (memset, VAL) that was done in DEST+OFFSET:LEN + (DEFSTMT) into the STMT. Returns true if the STMT was updated. */ static bool -optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree len) +optimize_aggr_zeroprop_1 (gimple *defstmt, gimple *stmt, + tree dest, poly_int64 offset, tree val, + poly_offset_int len) { - ao_ref read; - gimple *stmt = gsi_stmt (*gsip); - if (gimple_has_volatile_ops (stmt)) - return false; - - tree src2 = NULL_TREE, len2 = NULL_TREE; - poly_int64 offset, offset2; - tree val = integer_zero_node; - bool len_was_null = len == NULL_TREE; - if (len == NULL_TREE) - len = (TREE_CODE (src) == COMPONENT_REF - ? DECL_SIZE_UNIT (TREE_OPERAND (src, 1)) - : TYPE_SIZE_UNIT (TREE_TYPE (src))); - if (len == NULL_TREE - || !poly_int_tree_p (len)) - return false; + tree src2; + tree len2 = NULL_TREE; + poly_int64 offset2; - ao_ref_init (&read, src); - tree vuse = gimple_vuse (stmt); - gimple *defstmt; - unsigned limit = param_sccvn_max_alias_queries_per_access; - do { - /* If the vuse is the default definition, then there is no stores beforhand. */ - if (SSA_NAME_IS_DEFAULT_DEF (vuse)) - return false; - defstmt = SSA_NAME_DEF_STMT (vuse); - if (is_a <gphi*>(defstmt)) - return false; - if (limit-- == 0) - return false; - /* If the len was null, then we can use TBBA. */ - if (stmt_may_clobber_ref_p_1 (defstmt, &read, - /* tbaa_p = */ len_was_null)) - break; - vuse = gimple_vuse (defstmt); - } while (true); - - if (gimple_store_p (defstmt) - && gimple_assign_single_p (defstmt) - && TREE_CODE (gimple_assign_rhs1 (defstmt)) == STRING_CST - && !gimple_clobber_p (defstmt)) + if (gimple_call_builtin_p (stmt, BUILT_IN_MEMCPY) + && TREE_CODE (gimple_call_arg (stmt, 1)) == ADDR_EXPR + && poly_int_tree_p (gimple_call_arg (stmt, 2))) { - tree str = gimple_assign_rhs1 (defstmt); - src2 = gimple_assign_lhs (defstmt); - /* The string must contain all null char's for now. */ - for (int i = 0; i < TREE_STRING_LENGTH (str); i++) - { - if (TREE_STRING_POINTER (str)[i] != 0) - { - src2 = NULL_TREE; - break; - } - } - } - else if (gimple_store_p (defstmt) - && gimple_assign_single_p (defstmt) - && TREE_CODE (gimple_assign_rhs1 (defstmt)) == CONSTRUCTOR - && !gimple_clobber_p (defstmt)) - src2 = gimple_assign_lhs (defstmt); - else if (gimple_call_builtin_p (defstmt, BUILT_IN_MEMSET) - && TREE_CODE (gimple_call_arg (defstmt, 0)) == ADDR_EXPR - && TREE_CODE (gimple_call_arg (defstmt, 1)) == INTEGER_CST) - { - src2 = TREE_OPERAND (gimple_call_arg (defstmt, 0), 0); - len2 = gimple_call_arg (defstmt, 2); - val = gimple_call_arg (defstmt, 1); - /* For non-0 val, we'd have to transform stmt from assignment - into memset (only if dest is addressable). */ - if (!integer_zerop (val) && is_gimple_assign (stmt)) - src2 = NULL_TREE; + src2 = TREE_OPERAND (gimple_call_arg (stmt, 1), 0); + len2 = gimple_call_arg (stmt, 2); } + else if (gimple_assign_load_p (stmt) && gimple_store_p (stmt)) + { + src2 = gimple_assign_rhs1 (stmt); + len2 = (TREE_CODE (src2) == COMPONENT_REF + ? DECL_SIZE_UNIT (TREE_OPERAND (src2, 1)) + : TYPE_SIZE_UNIT (TREE_TYPE (src2))); + /* Can only handle zero memsets. */ + if (!integer_zerop (val)) + return false; + } + else + return false; - if (src2 == NULL_TREE) - return false; - - if (len2 == NULL_TREE) - len2 = (TREE_CODE (src2) == COMPONENT_REF - ? DECL_SIZE_UNIT (TREE_OPERAND (src2, 1)) - : TYPE_SIZE_UNIT (TREE_TYPE (src2))); if (len2 == NULL_TREE || !poly_int_tree_p (len2)) return false; - src = get_addr_base_and_unit_offset (src, &offset); src2 = get_addr_base_and_unit_offset (src2, &offset2); - if (src == NULL_TREE - || src2 == NULL_TREE - || maybe_lt (offset, offset2)) + if (src2 == NULL_TREE + || maybe_lt (offset2, offset)) return false; - if (!operand_equal_p (src, src2, 0)) + if (!operand_equal_p (dest, src2, 0)) return false; - /* [ src + offset2, src + offset2 + len2 - 1 ] is set to val. + /* [ dest + offset, dest + offset + len - 1 ] is set to val. Make sure that - [ src + offset, src + offset + len - 1 ] is a subset of that. */ - if (maybe_gt (wi::to_poly_offset (len) + (offset - offset2), - wi::to_poly_offset (len2))) + [ dest + offset2, dest + offset2 + len2 - 1 ] is a subset of that. */ + if (maybe_gt (wi::to_poly_offset (len2) + (offset2 - offset), + len)) return false; if (dump_file && (dump_flags & TDF_DETAILS)) @@ -1310,7 +1248,7 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree fprintf (dump_file, "after previous\n "); print_gimple_stmt (dump_file, defstmt, 0, dump_flags); } - + gimple *orig_stmt = stmt; /* For simplicity, don't change the kind of the stmt, turn dest = src; into dest = {}; and memcpy (&dest, &src, len); into memset (&dest, val, len); @@ -1320,8 +1258,10 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree of dest, dest isn't volatile. */ if (is_gimple_assign (stmt)) { - tree ctor = build_constructor (TREE_TYPE (dest), NULL); - gimple_assign_set_rhs_from_tree (gsip, ctor); + tree ctor_type = TREE_TYPE (gimple_assign_lhs (stmt)); + tree ctor = build_constructor (ctor_type, NULL); + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + gimple_assign_set_rhs_from_tree (&gsi, ctor); update_stmt (stmt); statistics_counter_event (cfun, "copy zeroing propagation of aggregate", 1); } @@ -1341,89 +1281,210 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree fprintf (dump_file, "into\n "); print_gimple_stmt (dump_file, stmt, 0, dump_flags); } + + /* Mark the bb for eh cleanup if needed. */ + if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt)) + bitmap_set_bit (to_purge, gimple_bb (stmt)->index); + return true; } -/* Optimizes - a = c; + +/* Optimize + a = {}; // DEST = value ;; LEN(nullptr) b = a; into - a = c; - b = c; - GSIP is the second statement and SRC is the common - between the statements. -*/ + a = {}; + b = {}; + Similarly for memset (&a, ..., sizeof (a)); instead of a = {}; + and/or memcpy (&b, &a, sizeof (a)); instead of b = a; */ + static bool -optimize_agr_copyprop (gimple_stmt_iterator *gsip) +optimize_aggr_zeroprop (gimple_stmt_iterator *gsip) { + ao_ref read; gimple *stmt = gsi_stmt (*gsip); if (gimple_has_volatile_ops (stmt)) return false; - tree dest = gimple_assign_lhs (stmt); - tree src = gimple_assign_rhs1 (stmt); - /* If the statement is `src = src;` then ignore it. */ - if (operand_equal_p (dest, src, 0)) - return false; + tree dest = NULL_TREE; + tree val = integer_zero_node; + tree len = NULL_TREE; + bool can_use_tbba = true; + bool changed = false; + + if (gimple_call_builtin_p (stmt, BUILT_IN_MEMSET) + && TREE_CODE (gimple_call_arg (stmt, 0)) == ADDR_EXPR + && TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST + && poly_int_tree_p (gimple_call_arg (stmt, 2))) + { + dest = TREE_OPERAND (gimple_call_arg (stmt, 0), 0); + len = gimple_call_arg (stmt, 2); + val = gimple_call_arg (stmt, 1); + ao_ref_init_from_ptr_and_size (&read, gimple_call_arg (stmt, 0), len); + can_use_tbba = false; + } + else if (gimple_store_p (stmt) + && gimple_assign_single_p (stmt) + && TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST) + { + tree str = gimple_assign_rhs1 (stmt); + dest = gimple_assign_lhs (stmt); + ao_ref_init (&read, dest); + /* The string must contain all null char's for now. */ + for (int i = 0; i < TREE_STRING_LENGTH (str); i++) + { + if (TREE_STRING_POINTER (str)[i] != 0) + { + dest = NULL_TREE; + break; + } + } + } + else if (gimple_store_p (stmt) + && gimple_assign_single_p (stmt) + && TREE_CODE (gimple_assign_rhs1 (stmt)) == CONSTRUCTOR + && !gimple_clobber_p (stmt)) + { + dest = gimple_assign_lhs (stmt); + ao_ref_init (&read, dest); + } - tree vuse = gimple_vuse (stmt); - /* If the vuse is the default definition, then there is no store beforehand. */ - if (SSA_NAME_IS_DEFAULT_DEF (vuse)) + if (dest == NULL_TREE) return false; - gimple *defstmt = SSA_NAME_DEF_STMT (vuse); - if (!gimple_assign_load_p (defstmt) - || !gimple_store_p (defstmt)) + + if (len == NULL_TREE) + len = (TREE_CODE (dest) == COMPONENT_REF + ? DECL_SIZE_UNIT (TREE_OPERAND (dest, 1)) + : TYPE_SIZE_UNIT (TREE_TYPE (dest))); + if (len == NULL_TREE + || !poly_int_tree_p (len)) return false; - if (gimple_has_volatile_ops (defstmt)) + + /* This store needs to be on the byte boundary and pointing to an object. */ + poly_int64 offset; + tree dest_base = get_addr_base_and_unit_offset (dest, &offset); + if (dest_base == NULL_TREE) return false; - tree dest2 = gimple_assign_lhs (defstmt); - tree src2 = gimple_assign_rhs1 (defstmt); + /* Setup the worklist. */ + auto_vec<std::pair<tree, unsigned>> worklist; + unsigned limit = param_sccvn_max_alias_queries_per_access; + worklist.safe_push (std::make_pair (gimple_vdef (stmt), limit)); - /* If the original store is `src2 = src2;` skip over it. */ - if (operand_equal_p (src2, dest2, 0)) - return false; - if (!operand_equal_p (src, dest2, 0)) + while (!worklist.is_empty ()) + { + std::pair<tree, unsigned> top = worklist.pop (); + tree vdef = top.first; + limit = top.second; + gimple *use_stmt; + imm_use_iterator iter; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, vdef) + { + /* Handling PHI nodes might not be worth it so don't. */ + if (is_a <gphi*> (use_stmt)) + continue; + + /* If this statement does not clobber add the vdef stmt to the + worklist. */ + if (gimple_vdef (use_stmt) + && !stmt_may_clobber_ref_p_1 (use_stmt, &read, + /* tbaa_p = */ can_use_tbba) + && limit != 0) + worklist.safe_push (std::make_pair (gimple_vdef (use_stmt), + limit - 1)); + + if (optimize_aggr_zeroprop_1 (stmt, use_stmt, dest_base, offset, + val, wi::to_poly_offset (len))) + changed = true; + } + } + + return changed; +} +/* Optimizes + DEST = SRC; + DEST2 = DEST; # DEST2 = SRC2; + into + DEST = SRC; + DEST2 = SRC; + GSIP is the first statement and SRC is the common + between the statements. +*/ +static bool +optimize_agr_copyprop (gimple_stmt_iterator *gsip) +{ + gimple *stmt = gsi_stmt (*gsip); + if (gimple_has_volatile_ops (stmt)) return false; + /* Can't prop if the statement could throw. */ + if (stmt_could_throw_p (cfun, stmt)) + return false; - /* For 2 memory refences and using a temporary to do the copy, - don't remove the temporary as the 2 memory references might overlap. - Note t does not need to be decl as it could be field. - See PR 22237 for full details. - E.g. - t = *a; - *b = t; - Cannot be convert into - t = *a; - *b = *a; - Though the following is allowed to be done: - t = *a; - *a = t; - And convert it into: - t = *a; - *a = *a; - */ - if (!operand_equal_p (src2, dest, 0) - && !DECL_P (dest) && !DECL_P (src2)) + tree dest = gimple_assign_lhs (stmt); + tree src = gimple_assign_rhs1 (stmt); + /* If the statement is `src = src;` then ignore it. */ + if (operand_equal_p (dest, src, 0)) return false; - if (dump_file && (dump_flags & TDF_DETAILS)) + tree vdef = gimple_vdef (stmt); + imm_use_iterator iter; + gimple *use_stmt; + bool changed = false; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, vdef) { - fprintf (dump_file, "Simplified\n "); - print_gimple_stmt (dump_file, stmt, 0, dump_flags); - fprintf (dump_file, "after previous\n "); - print_gimple_stmt (dump_file, defstmt, 0, dump_flags); - } - gimple_assign_set_rhs_from_tree (gsip, unshare_expr (src2)); - update_stmt (stmt); + if (!gimple_assign_load_p (use_stmt) + || !gimple_store_p (use_stmt)) + continue; + if (gimple_has_volatile_ops (use_stmt)) + continue; + tree dest2 = gimple_assign_lhs (use_stmt); + tree src2 = gimple_assign_rhs1 (use_stmt); + /* If the new store is `src2 = src2;` skip over it. */ + if (operand_equal_p (src2, dest2, 0)) + continue; + if (!operand_equal_p (dest, src2, 0)) + continue; + /* For 2 memory refences and using a temporary to do the copy, + don't remove the temporary as the 2 memory references might overlap. + Note t does not need to be decl as it could be field. + See PR 22237 for full details. + E.g. + t = *a; #DEST = SRC; + *b = t; #DEST2 = SRC2; + Cannot be convert into + t = *a; + *b = *a; + Though the following is allowed to be done: + t = *a; + *a = t; + And convert it into: + t = *a; + *a = *a; + */ + if (!operand_equal_p (dest2, src, 0) + && !DECL_P (dest2) && !DECL_P (src)) + continue; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Simplified\n "); + print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); + fprintf (dump_file, "after previous\n "); + print_gimple_stmt (dump_file, stmt, 0, dump_flags); + } + gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); + gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (src)); + update_stmt (use_stmt); - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "into\n "); - print_gimple_stmt (dump_file, stmt, 0, dump_flags); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "into\n "); + print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); + } + statistics_counter_event (cfun, "copy prop for aggregate", 1); + changed = true; } - statistics_counter_event (cfun, "copy prop for aggregate", 1); - return true; + return changed; } /* *GSI_P is a GIMPLE_CALL to a builtin function. @@ -1463,22 +1524,6 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2) switch (DECL_FUNCTION_CODE (callee2)) { - case BUILT_IN_MEMCPY: - if (gimple_call_num_args (stmt2) == 3) - { - tree dest = gimple_call_arg (stmt2, 0); - tree src = gimple_call_arg (stmt2, 1); - tree len = gimple_call_arg (stmt2, 2); - /* Try to optimize the memcpy to memset if src - and dest are addresses. */ - if (TREE_CODE (dest) == ADDR_EXPR - && TREE_CODE (src) == ADDR_EXPR - && TREE_CODE (len) == INTEGER_CST - && optimize_memcpy_to_memset (gsi_p, TREE_OPERAND (dest, 0), - TREE_OPERAND (src, 0), len)) - return true; - } - break; case BUILT_IN_MEMCHR: if (gimple_call_num_args (stmt2) == 3 && (res = gimple_call_lhs (stmt2)) != nullptr @@ -1540,6 +1585,13 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2) break; case BUILT_IN_MEMSET: + if (gimple_call_num_args (stmt2) == 3) + { + /* Try to prop the zeroing/value of the memset to memcpy + if the dest is an address and the value is a constant. */ + if (optimize_aggr_zeroprop (gsi_p)) + return true; + } if (gimple_call_num_args (stmt2) != 3 || gimple_call_lhs (stmt2) || CHAR_BIT != 8 @@ -4858,21 +4910,16 @@ pass_forwprop::execute (function *fun) { tree rhs1 = gimple_assign_rhs1 (stmt); enum tree_code code = gimple_assign_rhs_code (stmt); - if (gimple_assign_load_p (stmt) && gimple_store_p (stmt)) + if (gimple_store_p (stmt) && optimize_aggr_zeroprop (&gsi)) { - if (optimize_memcpy_to_memset (&gsi, - gimple_assign_lhs (stmt), - gimple_assign_rhs1 (stmt), - /* len = */NULL_TREE)) - { - changed = true; - break; - } - if (optimize_agr_copyprop (&gsi)) - { - changed = true; - break; - } + changed = true; + break; + } + if (gimple_assign_load_p (stmt) && gimple_store_p (stmt) + && optimize_agr_copyprop (&gsi)) + { + changed = true; + break; } if (TREE_CODE_CLASS (code) == tcc_comparison) diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index f7b48dc..e258f94 100644 --- a/libgcc/Makefile.in +++ b/libgcc/Makefile.in @@ -87,6 +87,7 @@ CC = @CC@ CFLAGS = @CFLAGS@ RANLIB = @RANLIB@ LN_S = @LN_S@ +WERROR = @WERROR@ PWD_COMMAND = $${PWDCMD-pwd} diff --git a/libgcc/config/aarch64/t-aarch64 b/libgcc/config/aarch64/t-aarch64 index d4c5922..c7d83c7 100644 --- a/libgcc/config/aarch64/t-aarch64 +++ b/libgcc/config/aarch64/t-aarch64 @@ -30,4 +30,4 @@ LIB2ADDEH += \ $(srcdir)/config/aarch64/__arm_za_disable.S SHLIB_MAPFILES += $(srcdir)/config/aarch64/libgcc-sme.ver -LIBGCC2_CFLAGS += -Werror -Wno-prio-ctor-dtor +LIBGCC2_CFLAGS += $(WERROR) -Wno-prio-ctor-dtor diff --git a/libgcc/configure b/libgcc/configure index 1841833..d5e80d2 100755 --- a/libgcc/configure +++ b/libgcc/configure @@ -586,6 +586,7 @@ ac_unique_file="static-object.mk" ac_includes_default='/* none */' ac_subst_vars='LTLIBOBJS LIBOBJS +WERROR md_unwind_header md_unwind_def_header unwind_header @@ -720,6 +721,7 @@ enable_tm_clone_registry with_glibc_version enable_tls with_gcc_major_version_only +enable_werror ' ac_precious_vars='build_alias host_alias @@ -1362,6 +1364,7 @@ Optional Features: installations without PT_GNU_EH_FRAME support --disable-tm-clone-registry disable TM clone registry --enable-tls Use thread-local storage [default=yes] + --enable-werror build with -Werror for selected targets Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -5789,6 +5792,22 @@ fi +# Check whether --enable-werror was given. +if test "${enable_werror+set}" = set; then : + enableval=$enable_werror; +case ${enable_werror} in + no) WERROR="" ;; + *) WERROR="-Werror" ;; +esac + +else + +WERROR="-Werror" + +fi + + + # We need multilib support. ac_config_files="$ac_config_files Makefile" diff --git a/libgcc/configure.ac b/libgcc/configure.ac index 85e4f1b..65cd3c6 100644 --- a/libgcc/configure.ac +++ b/libgcc/configure.ac @@ -733,6 +733,20 @@ AC_SUBST(md_unwind_header) AC_SUBST(sfp_machine_header) AC_SUBST(thread_header) +AC_ARG_ENABLE(werror, +[AS_HELP_STRING([--enable-werror], + [build with -Werror for selected targets])], +[ +case ${enable_werror} in + no) WERROR="" ;; + *) WERROR="-Werror" ;; +esac +], +[ +WERROR="-Werror" +]) +AC_SUBST(WERROR) + # We need multilib support. AC_CONFIG_FILES([Makefile]) AC_CONFIG_COMMANDS([default], diff --git a/libgcobol/libgcobol.cc b/libgcobol/libgcobol.cc index a7b4b55..eac6e31 100644 --- a/libgcobol/libgcobol.cc +++ b/libgcobol/libgcobol.cc @@ -1719,34 +1719,27 @@ int128_to_field(cblc_field_t *var, case FldPacked: { - static const unsigned char bin2pd[100] = - { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - } ; - // Convert the binary value to packed decimal. + int digits = var->digits; - // Set the destination bytes to zero - memset(location, 0, length); + // Assume for the moment that the res unsigned char sign_nybble = 0; - if( !(var->attr & packed_no_sign_e) ) + if( var->attr & packed_no_sign_e ) + { + // This is COMP-6 packed decimal, with no sign nybble + sign_nybble = 0; + } + else { // This is COMP-3 packed decimal, so we need to make room to the // right of the final decimal digit for the sign nybble: value *= 10; + digits += 1; // Figure out what the sign nybble is going to be, and make the // the value positive: if(var->attr & signable_e) { + // It is signable, so 0xD for negative, and 0xC for positive if(value < 0) { sign_nybble = 0x0D; @@ -1759,6 +1752,7 @@ int128_to_field(cblc_field_t *var, } else { + // The value is not signable, so the sign nybble is 0xF sign_nybble = 0x0F; if(value < 0) { @@ -1766,43 +1760,25 @@ int128_to_field(cblc_field_t *var, } } } - // ploc points to the current rightmost byte of the location: - unsigned char *ploc = location + length -1 ; - // Build the target from right to left, so that the result is - // big-endian: - while( value && ploc >= location ) - { - *ploc-- = bin2pd[value%100]; - value /= 100; - } + /* We need to check if the value is too big, in case our caller + wants to check for the error condition. In any event, we need + to make sure the value actually fits, because otherwise the + result might have a bad high-place digit for a value with an + odd number of places. */ + __int128 mask = __gg__power_of_ten(digits); + size_error = !!(value / mask); + value %= mask; + + // We are now set up to do the conversion: + __gg__binary_to_packed(location, digits, value); + // We can put the sign nybble into place at this point. Note that // for COMP-6 numbers the sign_nybble value is zero, so the next // operation is harmless. location[length -1] |= sign_nybble; - // If we still have value left, we have a size error - if( value ) - { - size_error = true; - } - else - { - if( ( sign_nybble && !(var->digits&1) ) - || ( !sign_nybble && (var->digits&1) ) ) - { - // This is either - // comp-3 with an even number of digits, or - // comp-6 with an odd number of digits. - // Either way, the first byte of the target has to have a high - // nybble of zero. If it's non-zero, then we have a size error: - if( location[0] & 0xF0 ) - { - size_error = true; - } - } - } // And we're done. break; } diff --git a/libgcobol/stringbin.cc b/libgcobol/stringbin.cc index d35ea82..2cc229e 100644 --- a/libgcobol/stringbin.cc +++ b/libgcobol/stringbin.cc @@ -328,3 +328,150 @@ __gg__binary_to_string_internal(char *result, int digits, __int128 value) return retval; } + +static +void +packed_from_combined(COMBINED &combined) + { + /* The combined.value must be positive at this point. + + The combined.run value has to be the number of places needed to hold + combined.value. The proper calculation is (digits+1)/2. + + For a signable value, the caller had to multiple the original value by + ten to create room on the right for the sign nybble. */ + + static const unsigned char bin2pd[100] = + { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + } ; + + COMBINED left; + COMBINED right; + + switch(combined.run) + { + case 1: + // We know that val8 has two digits. + combined_string[combined.start] = bin2pd[combined.val8]; + break; + + case 2: + // We know that val16 has four digits. + combined_string[combined.start ] = bin2pd[combined.val16/100]; + combined_string[combined.start+1] = bin2pd[combined.val16%100]; + break; + + case 3: + case 4: + // We know that val32 can hold up to eight digits. Break it in half. + left.start = combined.start; + left.run = combined.run - 2; + left.val16 = combined.val32 / 10000; + + right.start = combined.start+left.run; + right.run = 2; + right.val16 = combined.val32 % 10000; + + packed_from_combined(left); + packed_from_combined(right); + break; + + case 5: + case 6: + case 7: + case 8: + // We know that val64 is holding up to 18 digits. Break it into two + // eight-digit places that can each go into a val23 + left.start = combined.start; + left.run = combined.run - 4; + left.val32 = combined.val64 / 100000000; + + right.start = combined.start+left.run; + right.run = 4; + right.val32 = combined.val64 % 100000000; + + packed_from_combined(left); + packed_from_combined(right); + break; + + case 9: + // We know that val64 is holding 17 or 18 digits. Break off the + // bottom eight. + left.start = combined.start; + left.run = combined.run - 4; + left.val64 = combined.val64 / 100000000; + + right.start = combined.start+left.run; + right.run = 4; + right.val32 = combined.val64 % 100000000; + + packed_from_combined(left); + packed_from_combined(right); + break; + + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + // We know that val64 is holding between 18 and 36 digits. Break it + // two val64: + + left.start = combined.start; + left.run = combined.run - 9; + left.val64 = combined.val128 / 1000000000000000000ULL; + + right.start = combined.start+left.run; + right.run = 9; + right.val64 = combined.val128 % 1000000000000000000ULL; + + packed_from_combined(left); + packed_from_combined(right); + break; + + default: + // For twenty or more digits we peel eighteen digits at a time off the + // right side: + left.start = combined.start; + left.run = combined.run - 9; + left.val128 = combined.val128 / 1000000000000000000ULL; + + right.start = combined.start+left.run; + right.run = 9; + right.val64 = combined.val128 % 1000000000000000000ULL; + + packed_from_combined(left); + packed_from_combined(right); + break; + } + } + +extern "C" +void +__gg__binary_to_packed( unsigned char *result, + int digits, + __int128 value) + { + size_t length = (digits+1)/2; + + COMBINED combined; + combined.start = 0; + combined.run = length; + combined.val128 = value; + packed_from_combined(combined); + memcpy(result, combined_string, length); + } diff --git a/libgcobol/stringbin.h b/libgcobol/stringbin.h index 0276704..5ddb441 100644 --- a/libgcobol/stringbin.h +++ b/libgcobol/stringbin.h @@ -39,4 +39,9 @@ bool __gg__binary_to_string_internal( char *result, int digits, __int128 value); +extern "C" +void __gg__binary_to_packed( unsigned char *result, + int digits, + __int128 value); + #endif |