From 9d691ac64981ef0f9f6fd9830972e4277018b4e2 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 6 May 2025 12:43:10 -0400
Subject: [PATCH] Initial proof-of-concept of writing sarif to asm (plus CBOR
 and JSON5)

This is on top of trunk: b5f48e7872db30b8f174cb2c497868a358bf75d6
---
 gcc/Makefile.in                          |   2 +-
 gcc/analyzer/engine.cc                   |   2 +-
 gcc/diagnostic-format-sarif.cc           | 238 ++++-------
 gcc/diagnostic-format-sarif.h            |  53 +--
 gcc/diagnostic-format.h                  |   5 +-
 gcc/gcc.cc                               |  17 +
 gcc/gcov.cc                              |   2 +-
 gcc/json-cbor.cc                         | 519 +++++++++++++++++++++++
 gcc/json-cbor.h                          |  65 +++
 gcc/json-parsing.cc                      |   2 +-
 gcc/json-parsing.h                       |  14 -
 gcc/json-serialization.cc                | 130 ++++++
 gcc/json-serialization.h                 | 112 +++++
 gcc/json.cc                              | 134 +++++-
 gcc/json.h                               |  39 +-
 gcc/libgdiagnostics.cc                   |  30 +-
 gcc/libsarifreplay.cc                    |  35 +-
 gcc/lto-wrapper.cc                       |  15 +
 gcc/optinfo-emit-json.cc                 |   4 +-
 gcc/opts-diagnostic.cc                   |  84 +++-
 gcc/pretty-print.cc                      |   1 +
 gcc/selftest-run-tests.cc                |   1 +
 gcc/selftest.h                           |   1 +
 gcc/toplev.cc                            |   1 +
 gcc/tree-diagnostic-client-data-hooks.cc |   1 +
 gcc/varasm.cc                            | 224 ++++++++++
 26 files changed, 1472 insertions(+), 259 deletions(-)
 create mode 100644 gcc/json-cbor.cc
 create mode 100644 gcc/json-cbor.h
 create mode 100644 gcc/json-serialization.cc
 create mode 100644 gcc/json-serialization.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e3af923e0e04..8b22b8d4ce74 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1860,7 +1860,7 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
 	diagnostic-show-locus.o \
 	edit-context.o \
 	pretty-print.o intl.o \
-	json.o json-parsing.o \
+	json.o json-cbor.o json-parsing.o json-serialization.o \
 	sbitmap.o \
 	vec.o input.o hash-table.o ggc-none.o memory-block.o \
 	selftest.o selftest-diagnostic.o sort.o \
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index c3e4800f70ad..3a22d7665a35 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -6459,7 +6459,7 @@ dump_analyzer_json (const supergraph &sg,
   toplev_obj->set ("egraph", eg.to_json ());
 
   pretty_printer pp;
-  toplev_obj->print (&pp, flag_diagnostics_json_formatting);
+  toplev_obj->print (&pp, flag_diagnostics_json_formatting, false);
   pp_formatted_text (&pp);
 
   if (gzputs (output, pp_formatted_text (&pp)) == EOF
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 454eaae4d905..ff510b852924 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "pretty-print-urlifier.h"
 #include "demangle.h"
 #include "backtrace.h"
+#include "json-cbor.h"
 
 /* A json::array where the values are "unique" as per
    SARIF v2.1.0 section 3.7.3 ("Array properties with unique values").  */
@@ -687,18 +688,6 @@ private:
   std::vector<std::unique_ptr<sarif_result>> m_results;
 };
 
-/* Classes for abstracting away JSON vs other serialization formats.  */
-
-// class sarif_serialization_format_json : public sarif_serialization_format
-
-void
-sarif_serialization_format_json::write_to_file (FILE *outf,
-						const json::value &top)
-{
-  top.dump (outf, m_formatted);
-  fprintf (outf, "\n");
-}
-
 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
    and -fdiagnostics-format=sarif-file).
 
@@ -752,7 +741,6 @@ public:
 		 pretty_printer &printer,
 		 const line_maps *line_maps,
 		 const char *main_input_filename_,
-		 std::unique_ptr<sarif_serialization_format> serialization_format,
 		 const sarif_generation_options &sarif_gen_opts);
   ~sarif_builder ();
 
@@ -779,7 +767,6 @@ public:
   }
 
   std::unique_ptr<sarif_log> flush_to_object ();
-  void flush_to_file (FILE *outf);
 
   std::unique_ptr<json::array>
   make_locations_arr (sarif_location_manager &loc_mgr,
@@ -973,7 +960,6 @@ private:
 
   int m_tabstop;
 
-  std::unique_ptr<sarif_serialization_format> m_serialization_format;
   const sarif_generation_options m_sarif_gen_opts;
 
   unsigned m_next_result_idx;
@@ -1644,7 +1630,6 @@ sarif_builder::sarif_builder (diagnostic_context &context,
 			      pretty_printer &printer,
 			      const line_maps *line_maps,
 			      const char *main_input_filename_,
-			      std::unique_ptr<sarif_serialization_format> serialization_format,
 			      const sarif_generation_options &sarif_gen_opts)
 : m_context (context),
   m_printer (&printer),
@@ -1662,13 +1647,11 @@ sarif_builder::sarif_builder (diagnostic_context &context,
   m_cached_logical_locs
     (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()),
   m_tabstop (context.m_tabstop),
-  m_serialization_format (std::move (serialization_format)),
   m_sarif_gen_opts (sarif_gen_opts),
   m_next_result_idx (0),
   m_current_code_flow (nullptr)
 {
   gcc_assert (m_line_maps);
-  gcc_assert (m_serialization_format);
 
   if (auto client_data_hooks = context.get_client_data_hooks ())
     m_logical_loc_mgr = client_data_hooks->get_logical_location_manager ();
@@ -1904,18 +1887,6 @@ sarif_builder::flush_to_object ()
   return top;
 }
 
-/* Create a top-level object, and add it to all the results
-   (and other entities) we've seen so far.
-
-   Flush it all to OUTF.  */
-
-void
-sarif_builder::flush_to_file (FILE *outf)
-{
-  std::unique_ptr<sarif_log> top = flush_to_object ();
-  m_serialization_format->write_to_file (outf, *top);
-}
-
 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
    property (SARIF v2.1.0 section 3.27.10).
 
@@ -3641,6 +3612,23 @@ diagnostic_sarif_format_buffer::flush ()
 class sarif_output_format : public diagnostic_output_format
 {
 public:
+  sarif_output_format (diagnostic_context &context,
+		       const line_maps *line_maps,
+		       const char *main_input_filename_,
+		       const sarif_generation_options &sarif_gen_opts,
+		       std::unique_ptr<json::serializer> serializer,
+		       std::unique_ptr<json::byte_sink> sink)
+  : diagnostic_output_format (context),
+    m_builder (context, *get_printer (), line_maps, main_input_filename_,
+	       sarif_gen_opts),
+    m_serializer (std::move (serializer)),
+    m_sink (std::move (sink)),
+    m_buffer (nullptr)
+  {
+    gcc_assert (m_serializer);
+    gcc_assert (m_sink);
+  }
+
   ~sarif_output_format ()
   {
     /* Any sarifResult objects should have been handled by now.
@@ -3649,6 +3637,11 @@ public:
     std::unique_ptr<sarif_result> pending_result
       = m_builder.take_current_result ();
     gcc_assert (!pending_result);
+
+    auto obj = m_builder.flush_to_object ();
+    gcc_assert (obj);
+    auto bytes = m_serializer->to_bytes (*obj);
+    m_sink->write_bytes (bytes);
   }
 
   void dump (FILE *out, int indent) const override
@@ -3714,89 +3707,23 @@ public:
     /* No-op.  */
   }
 
+  bool machine_readable_stderr_p () const final override
+  {
+    return m_sink->machine_readable_stderr_p ();
+  }
+
   sarif_builder &get_builder () { return m_builder; }
 
   size_t num_results () const { return m_builder.num_results (); }
   sarif_result &get_result (size_t idx) { return m_builder.get_result (idx); }
 
-protected:
-  sarif_output_format (diagnostic_context &context,
-		       const line_maps *line_maps,
-		       const char *main_input_filename_,
-		       std::unique_ptr<sarif_serialization_format> serialization_format,
-		       const sarif_generation_options &sarif_gen_opts)
-  : diagnostic_output_format (context),
-    m_builder (context, *get_printer (), line_maps, main_input_filename_,
-	       std::move (serialization_format), sarif_gen_opts),
-    m_buffer (nullptr)
-  {}
-
+private:
   sarif_builder m_builder;
+  std::unique_ptr<json::serializer> m_serializer;
+  std::unique_ptr<json::byte_sink> m_sink;
   diagnostic_sarif_format_buffer *m_buffer;
 };
 
-class sarif_stream_output_format : public sarif_output_format
-{
-public:
-  sarif_stream_output_format (diagnostic_context &context,
-			      const line_maps *line_maps,
-			      const char *main_input_filename_,
-			      std::unique_ptr<sarif_serialization_format> serialization_format,
-			      const sarif_generation_options &sarif_gen_opts,
-			      FILE *stream)
-  : sarif_output_format (context, line_maps, main_input_filename_,
-			 std::move (serialization_format), sarif_gen_opts),
-    m_stream (stream)
-  {
-  }
-  ~sarif_stream_output_format ()
-  {
-    m_builder.flush_to_file (m_stream);
-  }
-  bool machine_readable_stderr_p () const final override
-  {
-    return m_stream == stderr;
-  }
-private:
-  FILE *m_stream;
-};
-
-class sarif_file_output_format : public sarif_output_format
-{
-public:
-  sarif_file_output_format (diagnostic_context &context,
-			    const line_maps *line_maps,
-			    const char *main_input_filename_,
-			    std::unique_ptr<sarif_serialization_format> serialization_format,
-			    const sarif_generation_options &sarif_gen_opts,
-			    diagnostic_output_file output_file)
-  : sarif_output_format (context, line_maps, main_input_filename_,
-			 std::move (serialization_format), sarif_gen_opts),
-    m_output_file (std::move (output_file))
-  {
-    gcc_assert (m_output_file.get_open_file ());
-    gcc_assert (m_output_file.get_filename ());
-  }
-  ~sarif_file_output_format ()
-  {
-    m_builder.flush_to_file (m_output_file.get_open_file ());
-  }
-  void dump (FILE *out, int indent) const override
-  {
-    fprintf (out, "%*ssarif_file_output_format: %s\n",
-	     indent, "",
-	     m_output_file.get_filename ());
-    diagnostic_output_format::dump (out, indent);
-  }
-  bool machine_readable_stderr_p () const final override
-  {
-    return false;
-  }
-
-private:
-  diagnostic_output_file m_output_file;
-};
-
 /* Print the start of an embedded link to PP, as per 3.11.6.  */
 
 static void
@@ -3935,16 +3862,20 @@ diagnostic_output_format_init_sarif_stderr (diagnostic_context &context,
 {
   gcc_assert (line_maps);
   const sarif_generation_options sarif_gen_opts;
-  auto serialization
-    = std::make_unique<sarif_serialization_format_json> (formatted);
+  auto serializer = std::make_unique<json::standard_serializer> (formatted);
+  auto output_file = diagnostic_output_file (stderr,
+					     false,
+					     label_text::borrow ("<stderr>"));
+  auto sink
+    = std::make_unique<json::output_file_byte_sink> (std::move (output_file));
   diagnostic_output_format_init_sarif
     (context,
-     std::make_unique<sarif_stream_output_format> (context,
-						   line_maps,
-						   main_input_filename_,
-						   std::move (serialization),
-						   sarif_gen_opts,
-						   stderr));
+     std::make_unique<sarif_output_format> (context,
+					    line_maps,
+					    main_input_filename_,
+					    sarif_gen_opts,
+					    std::move (serializer),
+					    std::move (sink)));
 }
 
 /* Attempt to open "BASE_FILE_NAME""EXTENSION" for writing.
@@ -4008,6 +3939,14 @@ diagnostic_output_format_open_sarif_file (diagnostic_context &context,
       suffix = ".sarif";
       is_binary = false;
       break;
+    case sarif_serialization_kind::json5:
+      suffix = ".sarif.json5";
+      is_binary = false;
+      break;
+    case sarif_serialization_kind::cbor:
+      suffix = ".sarif.cbor";
+      is_binary = true;
+      break;
     }
 
   return diagnostic_output_file::try_to_open (context,
@@ -4034,18 +3973,19 @@ diagnostic_output_format_init_sarif_file (diagnostic_context &context,
 						line_maps,
 						base_file_name,
 						sarif_serialization_kind::json);
-  auto serialization
-    = std::make_unique<sarif_serialization_format_json> (formatted);
+  auto serializer = std::make_unique<json::standard_serializer> (formatted);
+  auto sink
+    = std::make_unique<json::output_file_byte_sink> (std::move (output_file));
 
   const sarif_generation_options sarif_gen_opts;
   diagnostic_output_format_init_sarif
     (context,
-     std::make_unique<sarif_file_output_format> (context,
-						 line_maps,
-						 main_input_filename_,
-						 std::move (serialization),
-						 sarif_gen_opts,
-						 std::move (output_file)));
+     std::make_unique<sarif_output_format> (context,
+					    line_maps,
+					    main_input_filename_,
+					    sarif_gen_opts,
+					    std::move (serializer),
+					    std::move (sink)));
 }
 
 /* Populate CONTEXT in preparation for SARIF output to STREAM.  */
@@ -4059,35 +3999,37 @@ diagnostic_output_format_init_sarif_stream (diagnostic_context &context,
 {
   gcc_assert (line_maps);
   const sarif_generation_options sarif_gen_opts;
-  auto serialization
-    = std::make_unique<sarif_serialization_format_json> (formatted);
+  auto jv_serializer = std::make_unique<json::standard_serializer> (formatted);
+  auto output_file = diagnostic_output_file (stream, false, label_text ());
+  auto byte_sink
+    = std::make_unique<json::output_file_byte_sink> (std::move (output_file));
   diagnostic_output_format_init_sarif
     (context,
-     std::make_unique<sarif_stream_output_format> (context,
-						   line_maps,
-						   main_input_filename_,
-						   std::move (serialization),
-						   sarif_gen_opts,
-						   stream));
+     std::make_unique<sarif_output_format> (context,
+					    line_maps,
+					    main_input_filename_,
+					    sarif_gen_opts,
+					    std::move (jv_serializer),
+					    std::move (byte_sink)));
 }
 
 std::unique_ptr<diagnostic_output_format>
-make_sarif_sink (diagnostic_context &context,
-		 const line_maps &line_maps,
-		 const char *main_input_filename_,
-		 std::unique_ptr<sarif_serialization_format> serialization,
-		 const sarif_generation_options &sarif_gen_opts,
-		 diagnostic_output_file output_file)
-{
-  auto sink
-    = std::make_unique<sarif_file_output_format> (context,
-						  &line_maps,
-						  main_input_filename_,
-						  std::move (serialization),
-						  sarif_gen_opts,
-						  std::move (output_file));
-  sink->update_printer ();
-  return sink;
+make_sarif_output (diagnostic_context &context,
+		   const line_maps &line_maps,
+		   const char *main_input_filename_,
+		   const sarif_generation_options &sarif_gen_opts,
+		   std::unique_ptr<json::serializer> jv_serializer,
+		   std::unique_ptr<json::byte_sink> byte_sink)
+{
+  auto output
+    = std::make_unique<sarif_output_format> (context,
+					     &line_maps,
+					     main_input_filename_,
+					     sarif_gen_opts,
+					     std::move (jv_serializer),
+					     std::move (byte_sink));
+  output->update_printer ();
+  return output;
 }
 
 // struct sarif_generation_options
@@ -4101,6 +4043,8 @@ sarif_generation_options::sarif_generation_options ()
 
 namespace selftest {
 
+#if 0
+
 static void
 test_sarif_array_of_unique_1 ()
 {
@@ -4199,6 +4143,7 @@ public:
   sarif_result &get_result (size_t idx) { return m_format->get_result (idx); }
 
 private:
+#if 0
   class buffered_output_format : public sarif_output_format
   {
   public:
@@ -4224,6 +4169,7 @@ private:
   };
 
   buffered_output_format *m_format; // borrowed
+#endif
 };
 
 /* Test making a sarif_location for a complex rich_location
@@ -4831,11 +4777,14 @@ run_line_table_case_tests_per_version (const line_table_case &case_)
     (test_simple_log_2, case_);
 }
 
+#endif
+
 /* Run all of the selftests within this file.  */
 
 void
 diagnostic_format_sarif_cc_tests ()
 {
+#if 0
   test_sarif_array_of_unique_1 ();
   test_sarif_array_of_unique_2 ();
 
@@ -4846,6 +4795,7 @@ diagnostic_format_sarif_cc_tests ()
 
   /* Run tests per (SARIF gen-option, line-table-case) pair.  */
   for_each_line_table_case (run_line_table_case_tests_per_version);
+#endif
 }
 
 } // namespace selftest
diff --git a/gcc/diagnostic-format-sarif.h b/gcc/diagnostic-format-sarif.h
index 4e6a525d64c1..9e061a443056 100644
--- a/gcc/diagnostic-format-sarif.h
+++ b/gcc/diagnostic-format-sarif.h
@@ -25,16 +25,21 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-format.h"
 #include "diagnostic-output-file.h"
 #include "logical-location.h"
+#include "json-serialization.h"
 
 /* Enum for choosing what format to serializing the generated SARIF into.  */
 
 enum class sarif_serialization_kind
 {
    json,
+   json5,
+   cbor,
 
    num_values
 };
 
+class sarif_sink;
+
 extern diagnostic_output_file
 diagnostic_output_format_open_sarif_file (diagnostic_context &context,
 					  line_maps *line_maps,
@@ -59,33 +64,7 @@ diagnostic_output_format_init_sarif_stream (diagnostic_context &context,
 					    bool formatted,
 					    FILE *stream);
 
-/* Abstract base class for handling JSON output vs other kinds of
-   serialization of the json tree.  */
-
-class sarif_serialization_format
-{
-public:
-  virtual ~sarif_serialization_format () {}
-  virtual void write_to_file (FILE *outf,
-			      const json::value &top) = 0;
-};
-
-/* Concrete subclass for serializing SARIF as JSON.  */
-
-class sarif_serialization_format_json : public sarif_serialization_format
-{
-public:
-  sarif_serialization_format_json (bool formatted)
-  : m_formatted (formatted)
-  {
-  }
-  void write_to_file (FILE *outf, const json::value &top) final override;
-
-private:
-  bool m_formatted;
-};
-
-/* Control of SARIF generation.  */
+/* Control of generation of the SARIF json::value tree.  */
 
 enum class sarif_version
 {
@@ -107,12 +86,20 @@ struct sarif_generation_options
 };
 
 extern std::unique_ptr<diagnostic_output_format>
-make_sarif_sink (diagnostic_context &context,
-		 const line_maps &line_maps,
-		 const char *main_input_filename_,
-		 std::unique_ptr<sarif_serialization_format> serialization_format,
-		 const sarif_generation_options &sarif_gen_opts,
-		 diagnostic_output_file output_file);
+make_sarif_output (diagnostic_context &context,
+		   const line_maps &line_maps,
+		   const char *main_input_filename_,
+		   const sarif_generation_options &sarif_gen_opts,
+		   std::unique_ptr<json::serializer> serializer,
+		   std::unique_ptr<json::byte_sink> byte_sink);
+
+extern std::unique_ptr<diagnostic_output_format>
+maybe_make_sarif_asm_output (diagnostic_context &context,
+			     const line_maps &line_maps,
+			     const char *main_input_filename_,
+			     const sarif_generation_options &sarif_gen_opts,
+			     std::unique_ptr<json::serializer> serializer,
+			     label_text section_name);
 
 class sarif_builder;
 
diff --git a/gcc/diagnostic-format.h b/gcc/diagnostic-format.h
index f274a00f6e0d..51526905150f 100644
--- a/gcc/diagnostic-format.h
+++ b/gcc/diagnostic-format.h
@@ -69,7 +69,7 @@ public:
   virtual void update_printer () = 0;
 
   diagnostic_context &get_context () const { return m_context; }
-  pretty_printer *get_printer () const { return m_printer.get (); }
+  virtual pretty_printer *get_printer () const { return m_printer.get (); }
 
   text_art::theme *get_diagram_theme () const
   {
@@ -103,4 +103,7 @@ diagnostic_output_format_init_json_file (diagnostic_context &context,
 					 bool formatted,
 					 const char *base_file_name);
 
+extern std::unique_ptr<diagnostic_output_format>
+make_asm_output (std::unique_ptr<diagnostic_output_format> inner);
+
 #endif /* ! GCC_DIAGNOSTIC_FORMAT_H */
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 4fd87f2c4a13..af98cbd7add7 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -28,6 +28,7 @@ Once it knows which kind of compilation to perform, the procedure for
 compilation is specified by a string called a "spec".  */
 
 #define INCLUDE_STRING
+#define INCLUDE_VECTOR
 #include "config.h"
 #include "system.h"
 #ifdef HOST_HAS_PERSONALITY_ADDR_NO_RANDOMIZE
@@ -53,6 +54,7 @@ compilation is specified by a string called a "spec".  */
 #include "common/common-target.h"
 #include "gcc-urlifier.h"
 #include "opts-diagnostic.h"
+#include "diagnostic-format-sarif.h"
 
 #ifndef MATH_LIBRARY
 #define MATH_LIBRARY "m"
@@ -11224,6 +11226,21 @@ quote_spec_arg (char *orig)
   return quote_spec (orig);
 }
 
+/* Implementation of this for the driver, so that we can make 
+   -fdiagnostics-add-output=sarif:section=NAME be a no-op in
+   the driver, where we don't have varasm.o.  */
+
+std::unique_ptr<diagnostic_output_format>
+maybe_make_sarif_asm_output (diagnostic_context &context,
+			     const line_maps &line_maps,
+			     const char *main_input_filename_,
+			     const sarif_generation_options &sarif_gen_opts,
+			     std::unique_ptr<json::serializer> serializer,
+			     label_text section_name)
+{
+  return nullptr;
+}
+
 /* Restore all state within gcc.cc to the initial state, so that the driver
    code can be safely re-run in-process.
 
diff --git a/gcc/gcov.cc b/gcc/gcov.cc
index 0dfe513723c1..df07616f320e 100644
--- a/gcc/gcov.cc
+++ b/gcc/gcov.cc
@@ -1987,7 +1987,7 @@ generate_results (const char *file_name)
       else
 	{
 	  pretty_printer pp;
-	  root->print (&pp, false);
+	  root->print (&pp, false, false);
 	  pp_formatted_text (&pp);
 
 	  fnotice (stdout, "Creating '%s'\n",
diff --git a/gcc/json-cbor.cc b/gcc/json-cbor.cc
new file mode 100644
index 000000000000..31c71ca1a9b2
--- /dev/null
+++ b/gcc/json-cbor.cc
@@ -0,0 +1,519 @@
+/* JSON to CBOR and vice-versa.
+   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"
+#define INCLUDE_STRING
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "json-cbor.h"
+#include "pretty-print.h"
+#include "math.h"
+#include "selftest.h"
+
+/* Encode JV as CBOR.  */
+
+namespace cbor {
+
+class sink
+{
+public:
+  sink (std::vector<uint8_t> &buffer)
+  : m_buffer (buffer)
+  {}
+
+  void append_byte (uint8_t val)
+  {
+    m_buffer.push_back (val);
+  }
+
+  void append_uint16_t (uint16_t val)
+  {
+    m_buffer.push_back (val >> 8);
+    m_buffer.push_back (val & 0xff);
+  }
+
+private:
+  std::vector<uint8_t> &m_buffer;
+};
+
+} // namespace cbor
+
+// FIXME: this only handles a subset of possible JSON values
+
+static void
+append_json_value_to_cbor (cbor::sink &sink, const json::value &jv);
+
+static void
+append_utf8_string_to_cbor (cbor::sink &sink,
+			    const char *str,
+			    size_t len)
+{
+  gcc_assert (str);
+
+  if (len <= 0x17)
+    {
+      // 0x60..0x77: UTF-8 string (0x00..0x17 bytes follow)
+      const uint8_t init_byte = 0x60 + len;
+      sink.append_byte (init_byte);
+      while (*str)
+	sink.append_byte (*str++);
+    }
+  else if (len <= 0xff)
+    {
+      // 0x78: UTF-8 string (one-byte uint8_t for n, and then n bytes follow)
+      sink.append_byte (0x78);
+      sink.append_byte (len);
+      while (*str)
+	sink.append_byte (*str++);
+    }
+  else if (len <= 0xffff)
+    {
+      // 0x79: UTF-8 string (two-byte uint16_t for n, and then n bytes follow)
+      sink.append_byte (0x79);
+      sink.append_uint16_t (len);
+      while (*str)
+	sink.append_byte (*str++);
+    }
+  else
+    gcc_unreachable (); // TODO
+}
+
+static void
+append_utf8_string_to_cbor (cbor::sink &sink, const char *str)
+{
+  return append_utf8_string_to_cbor (sink, str, strlen (str));
+}
+
+static void
+append_json_object_to_cbor (cbor::sink &sink,
+			    const json::object &obj)
+{
+  if (obj.size () <= 0x17)
+    {
+      // 0xa0..0xb7: map (0x00..0x17 pairs of data items follow)
+      const uint8_t init_byte = 0xa0 + obj.size ();
+      sink.append_byte (init_byte);
+      unsigned i;
+      const char *key;
+      FOR_EACH_VEC_ELT (obj.m_keys, i, key)
+	{
+	  gcc_assert (key);
+	  append_utf8_string_to_cbor (sink, key);
+
+	  json::object::map_t &mut_map
+	    = const_cast<json::object::map_t &> (obj.m_map);
+	  const json::value *value = *mut_map.get (key);
+	  gcc_assert (value);
+	  append_json_value_to_cbor (sink, *value);
+	}
+    }
+  else
+    gcc_unreachable (); // TODO
+}
+
+static void
+append_json_array_to_cbor (cbor::sink &sink,
+			   const json::array &arr)
+{
+  if (arr.size () <= 0x17)
+    {
+      // 0x80..0x97: array (0x00..0x17 data items follow)
+      const uint8_t init_byte = 0x80 + arr.size ();
+      sink.append_byte (init_byte);
+      for (auto iter : arr)
+	append_json_value_to_cbor (sink, *iter);
+    }
+  else if (arr.size () <= 0xff)
+    {
+      // 0x98: array (one-byte uint8_t for n, and then n data items follow)
+      sink.append_byte (0x98);
+      sink.append_byte (arr.size ());
+      for (auto iter : arr)
+	append_json_value_to_cbor (sink, *iter);
+    }
+  else
+    gcc_unreachable (); // TODO
+}
+
+static void
+append_json_value_to_cbor (cbor::sink &sink, const json::value &jv)
+{
+  switch (jv.get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+    case json::JSON_OBJECT:
+      {
+	const json::object &obj = (const json::object &)jv;
+	append_json_object_to_cbor (sink, obj);
+      }
+      break;
+
+    case json::JSON_ARRAY:
+      {
+	const json::array &arr = (const json::array &)jv;
+	append_json_array_to_cbor (sink, arr);
+      }
+      break;
+
+    case json::JSON_INTEGER:
+      {
+	const json::integer_number &num = (const json::integer_number &)jv;
+	long long_val = num.get ();
+	if (long_val >= 0)
+	  {
+	    if (long_val <= 0x17)
+	      {
+		// 0x00..0x17: unsigned integer 0x00..0x17 (0..23)
+		sink.append_byte (long_val);
+	      }
+	    else if (long_val <= 0xff)
+	      {
+		// 0x18: unsigned integer (one-byte uint8_t follows)
+		sink.append_byte (0x18);
+		sink.append_byte (long_val);
+	      }
+	    else if (long_val <= 0xffff)
+	      {
+		// 0x19: unsigned integer (two-byte uint16_t follows)
+		sink.append_byte (0x19);
+		sink.append_uint16_t (long_val);
+	      }
+	    else
+	      gcc_unreachable (); // TODO
+	  }
+	else
+	  gcc_unreachable (); // TODO
+	// TODO
+      }
+      break;
+
+    case json::JSON_FLOAT:
+      {
+	const json::float_number &num = (const json::float_number &)jv;
+	double double_val = num.get ();
+
+	gcc_unreachable (); // TODO
+	// TODO
+      }
+      break;
+
+    case json::JSON_STRING:
+      {
+	const json::string &str = (const json::string &)jv;
+	append_utf8_string_to_cbor (sink,
+				    str.get_string (),
+				    str.get_length ());
+      }
+      break;
+
+    case json::JSON_TRUE:
+      // 0xf5: true
+      sink.append_byte (0xf5);
+      break;
+    case json::JSON_FALSE:
+      // 0xf4: false
+      sink.append_byte (0xf4);
+      break;
+    case json::JSON_NULL:
+      // 0xf6: null
+      sink.append_byte (0xf6);
+      break;
+    }
+}
+
+/* Encode JV as CBOR.  */
+
+std::vector<uint8_t>
+json::to_cbor (const json::value &jv)
+{
+  std::vector<uint8_t> result;
+  cbor::sink sink (result);
+  append_json_value_to_cbor (sink, jv);
+  return result;
+}
+
+namespace json
+{
+
+static std::unique_ptr<json::cbor_decoder::error>
+make_error (const json::cbor_stream &s, const char *fmt, ...);
+
+class cbor_stream
+{
+public:
+  cbor_stream (const json::cbor_buffer_t &buffer)
+  : m_buffer (buffer),
+    m_offset (0)
+  {
+  }
+
+  json::result<uint8_t, std::unique_ptr<json::cbor_decoder::error>>
+  get_uint8_t ()
+  {
+    if (m_offset < m_buffer.size ())
+      return m_buffer[m_offset++];
+    return out_of_data ();
+  }
+
+  json::result<uint16_t, std::unique_ptr<json::cbor_decoder::error>>
+  get_uint16_t ()
+  {
+    auto maybe_msb = get_uint8_t ();
+    if (maybe_msb.m_err)
+      return std::move (maybe_msb.m_err);
+    auto maybe_lsb = get_uint8_t ();
+    if (maybe_lsb.m_err)
+      return std::move (maybe_lsb.m_err);
+    return (maybe_msb.m_val << 8) + maybe_lsb.m_val;
+  }
+
+  json::result<const uint8_t *, std::unique_ptr<json::cbor_decoder::error>>
+  consume_bytes (size_t size)
+  {
+    if (m_offset + size <= m_buffer.size ())
+      {
+	const uint8_t *ptr = &m_buffer[m_offset];
+	m_offset += size;
+	return ptr;
+      }
+    return out_of_data ();
+  }
+
+  std::unique_ptr<json::cbor_decoder::error>
+  out_of_data ()
+  {
+    return make_error (*this, "out of data");
+  }
+
+  const json::cbor_buffer_t &m_buffer;
+  size_t m_offset;
+};
+
+static std::unique_ptr<json::cbor_decoder::error>
+make_error (const json::cbor_stream &s, const char *fmt, ...)
+{
+  gcc_unreachable (); // TODO
+}
+
+} // namespace json
+
+/* class cbor_decoder.  */
+
+json::cbor_decoder::result_t
+json::cbor_decoder::decode_to_json (const cbor_buffer_t &buffer)
+{
+  cbor_stream s (buffer);
+  return decode_to_json (s);
+}
+
+json::cbor_decoder::result_t
+json::cbor_decoder::decode_utf8_string (cbor_stream &s, size_t size)
+{
+  auto maybe_bytes = s.consume_bytes (size);
+  if (maybe_bytes.m_err)
+    return std::move (maybe_bytes.m_err);
+  const char *utf8 = (const char *)maybe_bytes.m_val;
+  auto str = std::make_unique<json::string> (utf8, size);
+  return result_t (std::move (str));
+}
+
+json::cbor_decoder::result_t
+json::cbor_decoder::decode_array (cbor_stream &s, size_t size)
+{
+  auto arr = std::make_unique<json::array> ();
+  for (size_t idx = 0; idx < size; idx++)
+    {
+      auto maybe_element = decode_to_json (s);
+      if (maybe_element.m_err)
+	return maybe_element;
+      arr->append<json::value> (std::move (maybe_element.m_val));
+    }
+  return result_t (std::move (arr));
+}
+
+json::cbor_decoder::result_t
+json::cbor_decoder::decode_map (cbor_stream &s, size_t size)
+{
+  auto obj = std::make_unique<json::object> ();
+  for (size_t idx = 0; idx < size; idx++)
+    {
+      auto maybe_key = decode_to_json (s);
+      if (maybe_key.m_err)
+	return std::move (maybe_key.m_err);
+      if (maybe_key.m_val->get_kind () != json::JSON_STRING)
+	return make_error (s, "map key is not a string");
+      const json::string *str_key
+	= (const json::string *)maybe_key.m_val.get ();
+      std::string key (str_key->get_string (), str_key->get_length ());
+      auto maybe_val = decode_to_json (s);
+      if (maybe_val.m_err)
+	return std::move (maybe_val.m_err);
+      obj->set (key.c_str (), std::move (maybe_val.m_val));
+    }
+  return result_t (std::move (obj));
+}
+
+json::cbor_decoder::result_t
+json::cbor_decoder::decode_to_json (cbor_stream &s)
+{
+  auto maybe_byte = s.get_uint8_t ();
+  if (maybe_byte.m_err)
+    return std::move (maybe_byte.m_err);
+
+  const uint8_t val = maybe_byte.m_val;
+  switch (val)
+    {
+    default:
+      // Unhandled code
+      return make_error (s, "unhandled code: %x", val);
+
+    // FIXME: ranged case labels isn't portable
+
+    case 0x00 ... 0x17: // unsigned integer 0x00..0x17 (0..23)
+      return result_t (std::make_unique <json::integer_number> (val));
+    case 0x18: // unsigned integer (one-byte uint8_t follows)
+      {
+	auto maybe_val = s.get_uint8_t ();
+	if (maybe_val.m_err)
+	  return std::move (maybe_val.m_err);
+	return result_t
+	  (std::make_unique <json::integer_number> (maybe_val.m_val));
+      }
+      break;
+    case 0x19: // unsigned integer (two-byte uint16_t follows)
+      {
+	auto maybe_val = s.get_uint16_t ();
+	if (maybe_val.m_err)
+	  return std::move (maybe_val.m_err);
+	return result_t
+	  (std::make_unique <json::integer_number> (maybe_val.m_val));
+      }
+      break;
+    case 0x20 ... 0x37: // negative integer -1-0x00..-1-0x17 (-1..-24)
+      return result_t (std::make_unique <json::integer_number>
+			   (-1 - (val - 0x20)));
+
+    case 0x60 ... 0x77: // UTF-8 string (0x00..0x17 bytes follow)
+      {
+	const size_t size = val - 0x60;
+	return decode_utf8_string (s, size);
+      }
+      break;
+    case 0x78: // UTF-8 string (one-byte uint8_t for n, and then n bytes follow)
+      {
+	auto maybe_size = s.get_uint8_t ();
+	if (maybe_size.m_err)
+	  return std::move (maybe_size.m_err);
+	return decode_utf8_string (s, maybe_size.m_val);
+      }
+      break;
+    case 0x79: // UTF-8 string (two-byte uint16_t for n, and then n bytes follow)
+      {
+	auto maybe_size = s.get_uint16_t ();
+	if (maybe_size.m_err)
+	  return std::move (maybe_size.m_err);
+	return decode_utf8_string (s, maybe_size.m_val);
+      }
+      break;
+
+    case 0x80 ... 0x97: // array (0x00..0x17 data items follow)
+      {
+	const size_t size = val - 0x80;
+	return decode_array (s, size);
+      }
+      break;
+    case 0x98: // array (one-byte uint8_t for n, and then n data items follow)
+      {
+	auto maybe_size = s.get_uint8_t ();
+	if (maybe_size.m_err)
+	  return std::move (maybe_size.m_err);
+	return decode_array (s, maybe_size.m_val);
+      }
+
+    case 0xa0 ... 0xb7: // map (0x00..0x17 pairs of data items follow)
+      {
+	const size_t size = val - 0xa0;
+	return decode_map (s, size);
+      }
+      break;
+
+    case 0xf4: // false
+      return result_t (std::make_unique <json::literal> (json::JSON_FALSE));
+    case 0xf5: // true
+      return result_t (std::make_unique <json::literal> (json::JSON_TRUE));
+    case 0xf6: // null
+      return result_t (std::make_unique <json::literal> (json::JSON_NULL));
+    }
+}
+
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+static void
+test_encoding_rfc8949_examples ()
+{
+  // TODO: Appendix A
+  // https://www.rfc-editor.org/rfc/rfc8949.html#name-examples-of-encoded-cbor-da
+
+  /* Example from "E.5. Conciseness on the Wire".
+     [1, [2, 3]] should encode to 82 01 82 02 03.  */
+  json::array arr;
+  arr.append (std::make_unique<json::integer_number> (1));
+  auto inner_arr = std::make_unique<json::array> ();
+  inner_arr->append (std::make_unique<json::integer_number> (2));
+  inner_arr->append (std::make_unique<json::integer_number> (3));
+  arr.append (std::move (inner_arr));
+  std::vector<uint8_t> buf = json::to_cbor (arr);
+  ASSERT_EQ (buf.size (), 5);
+  ASSERT_EQ (buf[0], 0x82);
+  ASSERT_EQ (buf[1], 0x01);
+  ASSERT_EQ (buf[2], 0x82);
+  ASSERT_EQ (buf[3], 0x02);
+  ASSERT_EQ (buf[4], 0x03);
+
+  // FIXME: use JSON parser for ease of maintenance
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_cbor_cc_tests ()
+{
+  test_encoding_rfc8949_examples ();
+#if 0
+  test_writing_objects ();
+  test_writing_arrays ();
+  test_writing_float_numbers ();
+  test_writing_integer_numbers ();
+  test_writing_strings ();
+  test_writing_literals ();
+#endif
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json-cbor.h b/gcc/json-cbor.h
new file mode 100644
index 000000000000..c5dd1f98aeb7
--- /dev/null
+++ b/gcc/json-cbor.h
@@ -0,0 +1,65 @@
+/* JSON to CBOR and vice-versa.
+   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_JSON_CBOR_H
+#define GCC_JSON_CBOR_H
+
+/* Implementation of CBOR, 
+
+   See https://cbor.io/
+   https://www.rfc-editor.org/rfc/rfc8949
+
+   Supports parsing CBOR files into json::value and writing
+   out json::value as CBOR.  */
+
+namespace json
+{
+
+typedef std::vector<uint8_t> cbor_buffer_t;
+
+extern cbor_buffer_t to_cbor (const json::value &jv);
+
+class cbor_stream;
+
+class cbor_decoder
+{
+ public:
+  class error
+  {
+  public:
+  };
+
+  typedef result<std::unique_ptr<json::value>, std::unique_ptr<error>> result_t;
+
+  result_t
+  decode_to_json (const cbor_buffer_t &buffer);
+
+  result_t
+  decode_to_json (cbor_stream &s);
+
+ private:
+  result_t decode_utf8_string (cbor_stream &s, size_t size);
+  result_t decode_array (cbor_stream &s, size_t size);
+  result_t decode_map (cbor_stream &s, size_t size);
+};
+
+} // namespace json
+
+#endif  /* GCC_JSON_CBOR_H  */
diff --git a/gcc/json-parsing.cc b/gcc/json-parsing.cc
index fc78500b2472..764aa527edcb 100644
--- a/gcc/json-parsing.cc
+++ b/gcc/json-parsing.cc
@@ -1420,7 +1420,7 @@ namespace selftest {
 /* Selftests.  */
 
 #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON)	\
-  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
+  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, false, EXPECTED_JSON)
 
 /* Implementation detail of ASSERT_RANGE_EQ.  */
 
diff --git a/gcc/json-parsing.h b/gcc/json-parsing.h
index 1a0fd982819a..dbac314716ec 100644
--- a/gcc/json-parsing.h
+++ b/gcc/json-parsing.h
@@ -77,20 +77,6 @@ private:
   char *m_msg;
 };
 
-/* Class for the result of an operation: either a value or an error
-   (or both null for the case of "successful nullptr").
-   The types must be default-constructible.  */
-
-template <typename ValueType, typename ErrorType>
-struct result
-{
-  result (ValueType val) : m_val (std::move (val)), m_err () {}
-  result (ErrorType err) : m_val (), m_err (std::move (err)) {}
-
-  ValueType m_val;
-  ErrorType m_err;
-};
-
 /* Typedef for the result of parsing JSON: ownership of either a
    json::value * or of a json::error *.  */
 typedef result<std::unique_ptr<value>,
diff --git a/gcc/json-serialization.cc b/gcc/json-serialization.cc
new file mode 100644
index 000000000000..148c771e1246
--- /dev/null
+++ b/gcc/json-serialization.cc
@@ -0,0 +1,130 @@
+/* Classes for json::value serialization.
+   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"
+#define INCLUDE_VECTOR
+#include "system.h"
+#include "coretypes.h"
+#include "json.h"
+#include "label-text.h"
+#include "json-serialization.h"
+#include "json-cbor.h"
+#include "pretty-print.h"
+
+using namespace json;
+
+/* Classes for abstracting away JSON vs other serialization formats.  */
+
+// TODO
+#if 0
+// class sarif_serialization_format_json : public sarif_serialization_format
+
+void
+sarif_serialization_format_json::write_to_sink (sarif_sink &sink,
+						const json::value &top)
+{
+  gcc_unreachable (); // TODO
+#if 0
+  sink
+  top.dump (outf, m_formatted);
+  fprintf (outf, "\n");
+#endif
+}
+
+// class sarif_serialization_format_json5 : public sarif_serialization_format
+
+void
+sarif_serialization_format_json5::write_to_file (FILE *outf,
+						 const json::value &top)
+{
+  top.dump (outf, m_formatted, true);
+  fprintf (outf, "\n");
+}
+
+// class sarif_serialization_format_cbor : public sarif_serialization_format
+
+void
+sarif_serialization_format_cbor::write_to_file (FILE *outf,
+						const json::value &top)
+{
+  std::vector<uint8_t> content = json::to_cbor (top);
+  fwrite (content.data (), content.size (), 1, outf);
+  // TODO: error checking
+}
+
+#endif
+
+
+// class standard_serializer : public serializer
+
+byte_buffer
+standard_serializer::to_bytes (const json::value &top)
+{
+  pretty_printer pp;
+  top.print (&pp, m_formatted, false);
+  pp_newline (&pp);
+
+  const char *buf = pp_formatted_text (&pp);
+  size_t sz = strlen (buf);
+  byte_buffer result (sz);
+  memcpy (result.data (), buf, sz);
+  return result;
+}
+
+// class json5_serializer : public serializer
+
+byte_buffer
+json5_serializer::to_bytes (const json::value &top)
+{
+  pretty_printer pp;
+  top.print (&pp, m_formatted, true);
+  pp_newline (&pp);
+
+  const char *buf = pp_formatted_text (&pp);
+  size_t sz = strlen (buf);
+  byte_buffer result (sz);
+  memcpy (result.data (), buf, sz);
+  return result;
+}
+
+// class cbor_serializer : public serializer
+
+byte_buffer
+cbor_serializer::to_bytes (const json::value &top)
+{
+  return json::to_cbor (top);
+}
+
+// class output_file_byte_sink : public byte_sink
+
+void
+output_file_byte_sink::write_bytes (const byte_buffer &buf)
+{
+  fwrite (buf.data (), buf.size (), 1,
+	  m_output_file.get_open_file ());
+  // TODO: error checking
+}
+
+bool
+output_file_byte_sink::machine_readable_stderr_p () const
+{
+  gcc_unreachable (); // TODO
+}
diff --git a/gcc/json-serialization.h b/gcc/json-serialization.h
new file mode 100644
index 000000000000..37d736126820
--- /dev/null
+++ b/gcc/json-serialization.h
@@ -0,0 +1,112 @@
+/* Classes for json::value serialization.
+   Copyright (C) 2023-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_JSON_SERIALIZATION_H
+#define GCC_JSON_SERIALIZATION_H
+
+#include "diagnostic-output-file.h"
+
+namespace json {
+
+class value;
+
+// TODO
+typedef std::vector<uint8_t> byte_buffer;
+
+/* Abstract base class for determining how to convert a json::value tree
+   to bytes.  */
+
+class serializer
+{
+public:
+  virtual ~serializer () {}
+  virtual byte_buffer to_bytes (const value &top) = 0;
+};
+
+/* Concrete subclass for serializing json::value as JSON.  */
+
+class standard_serializer : public serializer
+{
+public:
+  standard_serializer (bool formatted)
+  : m_formatted (formatted)
+  {
+  }
+
+  byte_buffer to_bytes (const json::value &top) final override;
+
+private:
+  bool m_formatted;
+};
+
+/* Concrete subclass for serializing SARIF as JSON5.  */
+
+class json5_serializer : public serializer
+{
+public:
+  json5_serializer (bool formatted)
+  : m_formatted (formatted)
+  {
+  }
+
+  byte_buffer to_bytes (const json::value &top) final override;
+
+private:
+  bool m_formatted;
+};
+
+/* Concrete subclass for serializing SARIF as CBOR.  */
+
+class cbor_serializer : public serializer
+{
+public:
+  byte_buffer to_bytes (const json::value &top) final override;
+};
+
+/* Abstract base class for where to send the serialized bytes.  */
+
+class byte_sink
+{
+public:
+  virtual ~byte_sink () {}
+  virtual void write_bytes (const byte_buffer &buf) = 0;
+  virtual bool machine_readable_stderr_p () const = 0;
+};
+
+// TODO:
+
+class output_file_byte_sink : public byte_sink
+{
+public:
+  output_file_byte_sink (diagnostic_output_file output_file)
+  : m_output_file (std::move (output_file))
+  {
+  }
+
+  void write_bytes (const byte_buffer &buf) final override;
+  bool machine_readable_stderr_p () const final override;
+
+private:
+  diagnostic_output_file m_output_file;
+};
+
+} // namespace json
+
+#endif /* ! GCC_JSON_SERIALIZATION_H */
diff --git a/gcc/json.cc b/gcc/json.cc
index f3f364598569..ace6869647da 100644
--- a/gcc/json.cc
+++ b/gcc/json.cc
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 
 using namespace json;
 
+// FIXME:
 /* Print a JSON string to PP, escaping '"', control characters,
    and embedded null bytes.
    The string is required to be UTF-8 encoded.  */
@@ -35,16 +36,22 @@ using namespace json;
 static void
 print_escaped_json_string (pretty_printer *pp,
 			   const char *utf8_str,
-			   size_t len)
+			   size_t len,
+			   char quote_char,
+			   bool allow_line_continuations)
 {
-  pp_character (pp, '"');
+  gcc_assert (quote_char == '"' || quote_char == '\'');
+  pp_character (pp, quote_char);
   for (size_t i = 0; i != len; ++i)
     {
       char ch = utf8_str[i];
       switch (ch)
 	{
 	case '"':
-	  pp_string (pp, "\\\"");
+	case '\'':
+	  if (ch == quote_char)
+	    pp_character (pp, '\\');
+	  pp_character (pp, ch);
 	  break;
 	case '\\':
 	  pp_string (pp, "\\\\");
@@ -57,6 +64,14 @@ print_escaped_json_string (pretty_printer *pp,
 	  break;
 	case '\n':
 	  pp_string (pp, "\\n");
+	  if (allow_line_continuations
+	      && i != (len - 1))
+	    {
+	      // FIXME: selftest coverage for this
+	      // FIXME: and don't do it for last char in string
+	      pp_character (pp, '\\');
+	      pp_newline (pp);
+	    }
 	  break;
 	case '\r':
 	  pp_string (pp, "\\r");
@@ -71,7 +86,28 @@ print_escaped_json_string (pretty_printer *pp,
 	  pp_character (pp, ch);
 	}
     }
-  pp_character (pp, '"');
+  pp_character (pp, quote_char);
+}
+
+// FIXME
+
+static bool
+is_json5_identifier (const char *utf8_str,
+		     size_t len)
+{
+  if (len == 0)
+    return false;
+
+  /* A-Za-z_ */
+  if (!ISIDST (utf8_str[0]))
+    return false;
+
+  for (size_t i = 1; i != len; ++i)
+    /* A-Za-z0-9_ */
+    if (!ISIDNUM (utf8_str[i]))
+      return false;
+
+  return true;
 }
 
 /* class pointer::token.  */
@@ -128,11 +164,11 @@ pointer::token::operator= (pointer::token &&other)
    in which the keys were originally inserted.  */
 
 void
-value::dump (FILE *outf, bool formatted) const
+value::dump (FILE *outf, bool formatted, bool json5) const
 {
   pretty_printer pp;
   pp_buffer (&pp)->m_stream = outf;
-  print (&pp, formatted);
+  print (&pp, formatted, json5);
   pp_flush (&pp);
 }
 
@@ -142,7 +178,7 @@ value::dump (FILE *outf, bool formatted) const
 void
 value::dump () const
 {
-  dump (stderr, true);
+  dump (stderr, true, false);
   fprintf (stderr, "\n");
 }
 
@@ -251,7 +287,7 @@ object::~object ()
 /* Implementation of json::value::print for json::object.  */
 
 void
-object::print (pretty_printer *pp, bool formatted) const
+object::print (pretty_printer *pp, bool formatted, bool json5) const
 {
   pp_character (pp, '{');
   if (formatted)
@@ -275,12 +311,22 @@ object::print (pretty_printer *pp, bool formatted) const
 	}
       map_t &mut_map = const_cast<map_t &> (m_map);
       value *value = *mut_map.get (key);
-      print_escaped_json_string (pp, key, strlen (key));
+      size_t key_len = strlen (key);
+      int indent;
+      if (json5 && is_json5_identifier (key, key_len))
+	{
+	  pp_string_n (pp, key, key_len);
+	  indent = key_len + 2;
+	}
+      else
+	{
+	  print_escaped_json_string (pp, key, key_len, '"', false);
+	  indent = key_len + 4;
+	}
       pp_string (pp, ": ");
-      const int indent = strlen (key) + 4;
       if (formatted)
 	pp_indentation (pp) += indent;
-      value->print (pp, formatted);
+      value->print (pp, formatted, json5);
       if (formatted)
 	pp_indentation (pp) -= indent;
     }
@@ -416,7 +462,7 @@ array::~array ()
 /* Implementation of json::value::print for json::array.  */
 
 void
-array::print (pretty_printer *pp, bool formatted) const
+array::print (pretty_printer *pp, bool formatted, bool json5) const
 {
   pp_character (pp, '[');
   if (formatted)
@@ -436,7 +482,7 @@ array::print (pretty_printer *pp, bool formatted) const
 	  else
 	    pp_space (pp);
 	}
-      v->print (pp, formatted);
+      v->print (pp, formatted, json5);
     }
   if (formatted)
     pp_indentation (pp) -= 1;
@@ -466,7 +512,8 @@ array::append_string (const char *utf8_value)
 
 void
 float_number::print (pretty_printer *pp,
-		     bool formatted ATTRIBUTE_UNUSED) const
+		     bool formatted ATTRIBUTE_UNUSED,
+		     bool json5 ATTRIBUTE_UNUSED) const
 {
   char tmp[1024];
   snprintf (tmp, sizeof (tmp), "%g", m_value);
@@ -479,7 +526,8 @@ float_number::print (pretty_printer *pp,
 
 void
 integer_number::print (pretty_printer *pp,
-		       bool formatted ATTRIBUTE_UNUSED) const
+		       bool formatted ATTRIBUTE_UNUSED,
+		       bool json5 ATTRIBUTE_UNUSED) const
 {
   char tmp[1024];
   snprintf (tmp, sizeof (tmp), "%ld", m_value);
@@ -501,18 +549,44 @@ 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.  */
 
 void
 string::print (pretty_printer *pp,
-	       bool formatted ATTRIBUTE_UNUSED) const
+	       bool formatted ATTRIBUTE_UNUSED,
+	       bool json5) const
 {
-  print_escaped_json_string (pp, m_utf8, m_len);
+  if (json5)
+    {
+      size_t num_single_quotes = 0;
+      size_t num_double_quotes = 0;
+      for (size_t i = 0; i < m_len; i++)
+	{
+	  switch (m_utf8[i])
+	    {
+	    default:
+	      break;
+	    case '\'':
+	      ++num_single_quotes;
+	      break;
+	    case '"':
+	      ++num_double_quotes;
+	      break;
+	    }
+	}
+      if (num_double_quotes > num_single_quotes)
+	{
+	  print_escaped_json_string (pp, m_utf8, m_len, '\'', json5);
+	  return;
+	}
+    }
+  print_escaped_json_string (pp, m_utf8, m_len, '"', json5);
 }
 
 /* class json::literal, a subclass of json::value.  */
@@ -521,7 +595,8 @@ string::print (pretty_printer *pp,
 
 void
 literal::print (pretty_printer *pp,
-		bool formatted ATTRIBUTE_UNUSED) const
+		bool formatted ATTRIBUTE_UNUSED,
+		bool json5 ATTRIBUTE_UNUSED) const
 {
   switch (m_kind)
     {
@@ -552,15 +627,19 @@ void
 assert_print_eq (const location &loc,
 		 const json::value &jv,
 		 bool formatted,
+		 bool json5,
 		 const char *expected_json)
 {
   pretty_printer pp;
-  jv.print (&pp, formatted);
+  jv.print (&pp, formatted, json5);
   ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
 }
 
 #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON)	\
-  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
+  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, false, EXPECTED_JSON)
+
+#define ASSERT_PRINT5_EQ(JV, FORMATTED, EXPECTED_JSON)	\
+  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, true, EXPECTED_JSON)
 
 /* Verify that object::get works as expected.  */
 
@@ -590,6 +669,10 @@ test_writing_objects ()
 		   "{\"foo\": \"bar\",\n"
 		   " \"baz\": \"quux\",\n"
 		   " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
+  ASSERT_PRINT5_EQ (obj, true,
+		   "{foo: \"bar\",\n"
+		   " baz: \"quux\",\n"
+		   " \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
   ASSERT_PRINT_EQ (obj, false,
 		   "{\"foo\": \"bar\", \"baz\": \"quux\""
 		   ", \"\\\"\\\\\\b\\f\\n\\r\\t\": \"value for awkward key\"}");
@@ -645,6 +728,7 @@ test_writing_strings ()
 
   string contains_quotes ("before \"quoted\" after");
   ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
+  ASSERT_PRINT5_EQ (contains_quotes, true, "'before \"quoted\" after'");
 
   const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
   string not_terminated (data, 3);
@@ -701,6 +785,14 @@ test_formatting ()
 		    "                          \"int\": 1066},\n"
 		    "           \"int\": 1776},\n"
 		    " \"int\": 42}"));
+  ASSERT_PRINT5_EQ (obj, true,
+		    ("{str: \"bar\",\n"
+		     " child: {grandchild: {arr: [0,\n"
+		     "                            1,\n"
+		     "                            2],\n"
+		     "                      int: 1066},\n"
+		     "         int: 1776},\n"
+		     " int: 42}"));
   ASSERT_PRINT_EQ (obj, false,
 		   ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
 		    " {\"arr\": [0, 1, 2], \"int\": 1066},"
diff --git a/gcc/json.h b/gcc/json.h
index da4da852a1ed..93cccdfdb97d 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -123,9 +123,11 @@ class value
  public:
   virtual ~value () {}
   virtual enum kind get_kind () const = 0;
-  virtual void print (pretty_printer *pp, bool formatted) const = 0;
+  virtual void print (pretty_printer *pp,
+		      bool formatted,
+		      bool json5) const = 0;
 
-  void dump (FILE *, bool formatted) const;
+  void dump (FILE *, bool formatted, bool json5 = false) const;
   void DEBUG_FUNCTION dump () const;
 
   virtual object *dyn_cast_object () { return nullptr; }
@@ -149,11 +151,12 @@ class object : public value
   ~object ();
 
   enum kind get_kind () const final override { return JSON_OBJECT; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
   object *dyn_cast_object () final override { return this; }
 
   bool is_empty () const { return m_map.is_empty (); }
+  size_t size () const { return m_map.elements (); }
 
   void set (const char *key, value *v);
 
@@ -182,7 +185,8 @@ class object : public value
 
   static int compare (const json::object &obj_a, const json::object &obj_b);
 
- private:
+  // FIXME:
+  //private:
   typedef hash_map <char *, value *,
     simple_hashmap_traits<nofree_string_hash, value *> > map_t;
   map_t m_map;
@@ -199,7 +203,7 @@ class array : public value
   ~array ();
 
   enum kind get_kind () const final override { return JSON_ARRAY; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
   void append (value *v);
   void append_string (const char *utf8_value);
@@ -240,7 +244,7 @@ class float_number : public value
   float_number (double value) : m_value (value) {}
 
   enum kind get_kind () const final override { return JSON_FLOAT; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
   double get () const { return m_value; }
 
@@ -256,7 +260,7 @@ class integer_number : public value
   integer_number (long value) : m_value (value) {}
 
   enum kind get_kind () const final override { return JSON_INTEGER; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
   long get () const { return m_value; }
 
@@ -275,13 +279,13 @@ class string : public value
   ~string () { free (m_utf8); }
 
   enum kind get_kind () const final override { return JSON_STRING; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
   const char *get_string () const { return m_utf8; }
   size_t get_length () const { return m_len; }
 
  private:
-  char *m_utf8;
+  char *m_utf8; // not necessarily null-terminated; could have embedded nulls
   size_t m_len;
 };
 
@@ -297,12 +301,26 @@ class literal : public value
   literal (bool value): m_kind (value ? JSON_TRUE : JSON_FALSE) {}
 
   enum kind get_kind () const final override { return m_kind; }
-  void print (pretty_printer *pp, bool formatted) const final override;
+  void print (pretty_printer *pp, bool formatted, bool json5) const final override;
 
  private:
   enum kind m_kind;
 };
 
+/* Class for the result of an operation: either a value or an error
+   (or both null for the case of "successful nullptr").
+   The types must be default-constructible.  */
+
+template <typename ValueType, typename ErrorType>
+struct result
+{
+  result (ValueType val) : m_val (std::move (val)), m_err () {}
+  result (ErrorType err) : m_val (), m_err (std::move (err)) {}
+
+  ValueType m_val;
+  ErrorType m_err;
+};
+
 } // namespace json
 
 template <>
@@ -410,6 +428,7 @@ class location;
 extern void assert_print_eq (const location &loc,
 			     const json::value &jv,
 			     bool formatted,
+			     bool json5,
 			     const char *expected_json);
 
 } // namespace selftest
diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc
index 70b0f36e0438..a6d634783098 100644
--- a/gcc/libgdiagnostics.cc
+++ b/gcc/libgdiagnostics.cc
@@ -241,6 +241,8 @@ private:
   diagnostic_source_printing_options m_source_printing;
 };
 
+namespace libgdiagnostics_impl {
+
 class sarif_sink : public sink
 {
 public:
@@ -250,6 +252,8 @@ public:
 	      const sarif_generation_options &sarif_gen_opts);
 };
 
+} // namespace libgdiagnostics_impl
+
 /* Helper for the linemap code.  */
 
 static size_t
@@ -1180,6 +1184,7 @@ diagnostic_text_sink::text_starter (diagnostic_text_output_format &text_output,
 
 /* class sarif_sink : public sink.  */
 
+libgdiagnostics_impl::
 sarif_sink::sarif_sink (diagnostic_manager &mgr,
 			FILE *dst_stream,
 			const diagnostic_file *main_input_file,
@@ -1188,13 +1193,15 @@ sarif_sink::sarif_sink (diagnostic_manager &mgr,
 {
   diagnostic_output_file output_file (dst_stream, false,
 				      label_text::borrow ("sarif_sink"));
-  auto serialization = std::make_unique<sarif_serialization_format_json> (true);
-  auto inner_sink = make_sarif_sink (mgr.get_dc (),
-				     *mgr.get_line_table (),
-				     main_input_file->get_name (),
-				     std::move (serialization),
-				     sarif_gen_opts,
-				     std::move (output_file));
+  auto js_serializer = std::make_unique<json::standard_serializer> (true);
+  auto byte_sink
+    = std::make_unique<json::output_file_byte_sink> (std::move (output_file));
+  auto inner_sink = make_sarif_output (mgr.get_dc (),
+				       *mgr.get_line_table (),
+				       main_input_file->get_name (),
+				       sarif_gen_opts,
+				       std::move (js_serializer),
+				       std::move (byte_sink));
   mgr.get_dc ().add_sink (std::move (inner_sink));
 }
 
@@ -1402,10 +1409,11 @@ diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr,
       break;
     }
 
-  diag_mgr->add_sink (std::make_unique<sarif_sink> (*diag_mgr,
-						    dst_stream,
-						    main_input_file,
-						    sarif_gen_opts));
+  diag_mgr->add_sink
+    (std::make_unique<libgdiagnostics_impl::sarif_sink> (*diag_mgr,
+							 dst_stream,
+							 main_input_file,
+							 sarif_gen_opts));
 }
 
 /* Public entrypoint.  */
diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index 6c797624f828..c4376c437d60 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -31,13 +31,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "sarif-spec-urls.def"
 #include "libsarifreplay.h"
 #include "label-text.h"
+#include "json-cbor.h"
 
 namespace {
 
 /* Read the contents of PATH into memory.
    Issue an error to MGR and return nullptr if there are any problems.  */
 
-static std::unique_ptr<std::vector<char>>
+static std::unique_ptr<std::vector<uint8_t>>
 read_file (const char *path, libgdiagnostics::manager &mgr)
 {
   FILE *f_in = fopen (path, "r");
@@ -50,7 +51,7 @@ read_file (const char *path, libgdiagnostics::manager &mgr)
     }
 
   /* Read content, allocating a buffer for it.  */
-  auto result = std::make_unique<std::vector<char>> ();
+  auto result = std::make_unique<std::vector<uint8_t>> ();
   char buf[4096];
   size_t iter_sz_in;
 
@@ -187,6 +188,7 @@ enum class status
   ok,
   err_reading_file,
   err_malformed_json,
+  err_malformed_cbor,
   err_invalid_sarif,
   err_unhandled_sarif
 };
@@ -753,11 +755,23 @@ report_bad_property_kind<json::object> (const json::object &,
 			describe_kind (propval));
 }
 
+static bool
+ends_with_p (const char *str,
+	     const char *suffix)
+{
+  size_t str_len = strlen (str);
+  size_t suffix_len = strlen (suffix);
+  if (str_len < suffix_len)
+    return false;
+  return !strcmp (str + str_len - suffix_len, suffix);
+}
+
 enum status
 sarif_replayer::replay_file (const char *filename,
 			     const replay_options &replay_opts)
 {
-  std::unique_ptr<std::vector<char>> buf = read_file (filename, m_control_mgr);
+  std::unique_ptr<std::vector<uint8_t>> buf
+    = read_file (filename, m_control_mgr);
   if (!buf)
     return status::err_reading_file;
 
@@ -774,6 +788,21 @@ sarif_replayer::replay_file (const char *filename,
 	fputc ((*buf)[i], stderr);
     }
 
+  if (ends_with_p (filename, ".cbor"))
+    {
+      json::cbor_decoder d;
+      json::cbor_decoder::result_t result (d.decode_to_json (*buf));
+      if (auto err = result.m_err.get ())
+	{
+	  gcc_assert (!result.m_val.get ());
+	  // TODO: complain
+	  return status::err_malformed_cbor;
+	}
+
+      gcc_assert (result.m_val.get ());
+      return emit_sarif_as_diagnostics (*result.m_val.get ());
+    }
+
   json::parser_result_t result
     (json::parse_utf8_string (buf->size (),
 			      (const char *)buf->data (),
diff --git a/gcc/lto-wrapper.cc b/gcc/lto-wrapper.cc
index 27405e8092eb..50a79f963de0 100644
--- a/gcc/lto-wrapper.cc
+++ b/gcc/lto-wrapper.cc
@@ -56,6 +56,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "opt-suggestions.h"
 #include "opts-jobserver.h"
 #include "lto-ltrans-cache.h"
+#include "diagnostic-format-sarif.h"
 
 /* Environment variable, used for passing the names of offload targets from GCC
    driver to lto-wrapper.  */
@@ -2286,6 +2287,20 @@ public:
   }
 };
 
+/* Implementation of this for lto-wrapper.  Currently a no-op.  */
+
+std::unique_ptr<diagnostic_output_format>
+maybe_make_sarif_asm_output (diagnostic_context &context,
+			     const line_maps &line_maps,
+			     const char *main_input_filename_,
+			     const sarif_generation_options &sarif_gen_opts,
+			     std::unique_ptr<json::serializer> serializer,
+			     label_text section_name)
+{
+  return nullptr;
+}
+
+
 /* Entry point.  */
 
 int
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index 28c8a98db302..77ce9149ed1e 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -103,7 +103,7 @@ void
 optrecord_json_writer::write () const
 {
   pretty_printer pp;
-  m_root_tuple->print (&pp, false);
+  m_root_tuple->print (&pp, false, false);
 
   bool emitted_error = false;
   char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
@@ -466,7 +466,7 @@ test_building_json_from_dump_calls ()
 
   /* Verify that the json is sane.  */
   pretty_printer pp;
-  json_obj->print (&pp, false);
+  json_obj->print (&pp, false, false);
   const char *json_str = pp_formatted_text (&pp);
   ASSERT_STR_CONTAINS (json_str, "impl_location");
   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
diff --git a/gcc/opts-diagnostic.cc b/gcc/opts-diagnostic.cc
index 34c390695b95..93dd18fd69a5 100644
--- a/gcc/opts-diagnostic.cc
+++ b/gcc/opts-diagnostic.cc
@@ -216,6 +216,13 @@ public:
   make_sink (const context &ctxt,
 	     const char *unparsed_arg,
 	     const scheme_name_and_params &parsed_arg) const final override;
+
+private:
+  static sarif_generation_options
+  make_sarif_gen_opts (enum sarif_version version);
+
+  static std::unique_ptr<json::serializer>
+  make_js_serializer (enum sarif_serialization_kind);
 };
 
 class html_scheme_handler : public output_factory::scheme_handler
@@ -439,6 +446,14 @@ context::open_output_file (label_text &&filename) const
   return diagnostic_output_file (outf, true, std::move (filename));
 }
 
+// FIXME:
+std::unique_ptr<json::byte_sink>
+make_js_sink_from_output_file (diagnostic_output_file output_file)
+{
+  return std::make_unique<json::output_file_byte_sink>
+    (std::move (output_file));
+}
+
 /* class sarif_scheme_handler : public output_factory::scheme_handler.  */
 
 std::unique_ptr<diagnostic_output_format>
@@ -447,6 +462,7 @@ sarif_scheme_handler::make_sink (const context &ctxt,
 				 const scheme_name_and_params &parsed_arg) const
 {
   label_text filename;
+  label_text section;
   enum sarif_serialization_kind serialization_kind
     = sarif_serialization_kind::json;
   enum sarif_version version = sarif_version::v2_1_0;
@@ -459,11 +475,18 @@ sarif_scheme_handler::make_sink (const context &ctxt,
 	  filename = label_text::take (xstrdup (value.c_str ()));
 	  continue;
 	}
+      if (key == "section")
+	{
+	  section = label_text::take (xstrdup (value.c_str ()));
+	  continue;
+	}
       if (key == "serialization")
 	{
 	  static const std::array<std::pair<const char *, enum sarif_serialization_kind>,
 				  (size_t)sarif_serialization_kind::num_values> value_names
-	    {{{"json", sarif_serialization_kind::json}}};
+	    {{{"json", sarif_serialization_kind::json},
+	      {"json5", sarif_serialization_kind::json5},
+	      {"cbor", sarif_serialization_kind::cbor}}};
 
 	  if (!parse_enum_value<enum sarif_serialization_kind>
 		 (ctxt, unparsed_arg,
@@ -491,6 +514,7 @@ sarif_scheme_handler::make_sink (const context &ctxt,
       /* Key not found.  */
       auto_vec<const char *> known_keys;
       known_keys.safe_push ("file");
+      known_keys.safe_push ("section");
       known_keys.safe_push ("serialization");
       known_keys.safe_push ("version");
       ctxt.report_unknown_key (unparsed_arg, key, get_scheme_name (),
@@ -498,6 +522,18 @@ sarif_scheme_handler::make_sink (const context &ctxt,
       return nullptr;
     }
 
+  if (section.get ())
+    {
+      auto sarif_gen_opts = make_sarif_gen_opts (version);
+      auto serialization_obj = make_js_serializer (serialization_kind);
+      return maybe_make_sarif_asm_output (ctxt.m_dc,
+					  *line_table,
+					  ctxt.m_opts.x_main_input_filename,
+					  sarif_gen_opts,
+					  std::move (serialization_obj),
+					  std::move (section));
+    }
+
   diagnostic_output_file output_file;
   if (filename.get ())
     output_file = ctxt.open_output_file (std::move (filename));
@@ -515,27 +551,43 @@ sarif_scheme_handler::make_sink (const context &ctxt,
   if (!output_file)
     return nullptr;
 
-  sarif_generation_options sarif_gen_opts;
-  sarif_gen_opts.m_version = version;
+  auto sarif_gen_opts = make_sarif_gen_opts (version);
+  auto js_serializer = make_js_serializer (serialization_kind);
+  auto js_sink = make_js_sink_from_output_file (std::move (output_file));
 
-  std::unique_ptr<sarif_serialization_format> serialization_obj;
-  switch (serialization_kind)
+  return make_sarif_output (ctxt.m_dc,
+			    *line_table,
+			    ctxt.m_opts.x_main_input_filename,
+			    sarif_gen_opts,
+			    std::move (js_serializer),
+			    std::move (js_sink));
+}
+
+sarif_generation_options
+sarif_scheme_handler::make_sarif_gen_opts (enum sarif_version version)
+{
+  sarif_generation_options opts;
+  opts.m_version = version;
+  return opts;
+}
+
+std::unique_ptr<json::serializer>
+sarif_scheme_handler::make_js_serializer (enum sarif_serialization_kind kind)
+{
+  switch (kind)
     {
     default:
       gcc_unreachable ();
+
     case sarif_serialization_kind::json:
-      serialization_obj
-	= std::make_unique<sarif_serialization_format_json> (true);
-      break;
-    }
+      return std::make_unique<json::standard_serializer> (true);
 
-  auto sink = make_sarif_sink (ctxt.m_dc,
-			       *line_table,
-			       ctxt.m_opts.x_main_input_filename,
-			       std::move (serialization_obj),
-			       sarif_gen_opts,
-			       std::move (output_file));
-  return sink;
+    case sarif_serialization_kind::json5:
+      return std::make_unique<json::json5_serializer> (true);
+
+    case sarif_serialization_kind::cbor:
+      return std::make_unique<json::cbor_serializer> ();
+    }
 }
 
 /* class html_scheme_handler : public output_factory::scheme_handler.  */
diff --git a/gcc/pretty-print.cc b/gcc/pretty-print.cc
index 1f38702b6117..0f8cfe190a8a 100644
--- a/gcc/pretty-print.cc
+++ b/gcc/pretty-print.cc
@@ -2225,6 +2225,7 @@ pp_output_formatted_text (pretty_printer *pp,
   gcc_assert (buffer->m_obstack == &buffer->m_formatted_obstack);
 
   pp_formatted_chunks *chunk_array = buffer->m_cur_formatted_chunks;
+  gcc_assert (chunk_array);
   pp_token_list * const *token_lists = chunk_array->get_token_lists ();
 
   {
diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc
index 959e5d0a0f8d..5106c0370986 100644
--- a/gcc/selftest-run-tests.cc
+++ b/gcc/selftest-run-tests.cc
@@ -76,6 +76,7 @@ selftest::run_tests ()
   opts_cc_tests ();
   json_cc_tests ();
   json_parser_cc_tests ();
+  json_cbor_cc_tests ();
   cgraph_cc_tests ();
   optinfo_emit_json_cc_tests ();
   ordered_hash_map_tests_cc_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 7e1d94cd39ef..c7752c5ffcfd 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -241,6 +241,7 @@ extern void hash_map_tests_cc_tests ();
 extern void hash_set_tests_cc_tests ();
 extern void input_cc_tests ();
 extern void json_cc_tests ();
+extern void json_cbor_cc_tests ();
 extern void lazy_diagnostic_path_cc_tests ();
 extern void json_parser_cc_tests ();
 extern void optinfo_emit_json_cc_tests ();
diff --git a/gcc/toplev.cc b/gcc/toplev.cc
index 7e457b5168b4..59f0dc24909c 100644
--- a/gcc/toplev.cc
+++ b/gcc/toplev.cc
@@ -2061,6 +2061,7 @@ finalize ()
 
   if (asm_out_file)
     {
+      // FIXME: extra info into asm file?
       if (ferror (asm_out_file) != 0)
 	fatal_error (input_location, "error writing to %s: %m", asm_file_name);
       if (fclose (asm_out_file) != 0)
diff --git a/gcc/tree-diagnostic-client-data-hooks.cc b/gcc/tree-diagnostic-client-data-hooks.cc
index 06a089425f00..a13eb61e0daf 100644
--- a/gcc/tree-diagnostic-client-data-hooks.cc
+++ b/gcc/tree-diagnostic-client-data-hooks.cc
@@ -19,6 +19,7 @@ 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_VECTOR
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
diff --git a/gcc/varasm.cc b/gcc/varasm.cc
index 10c1d2e31371..c3d1a035b2a7 100644
--- a/gcc/varasm.cc
+++ b/gcc/varasm.cc
@@ -26,6 +26,7 @@ along with GCC; see the file COPYING3.  If not see
    and are responsible for combining constants with the same value.  */
 
 #include "config.h"
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
@@ -64,6 +65,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "recog.h"
 #include "gimple-expr.h"
+#include "diagnostic-format.h"
+#include "diagnostic-buffer.h"
+#include "json-serialization.h"
+#include "plugin.h"
+#include "diagnostic-format-sarif.h"
 
 /* The (assembler) name of the first globally-visible object output.  */
 extern GTY(()) const char *first_global_object_name;
@@ -9016,6 +9022,224 @@ handle_vtv_comdat_section (section *sect, const_tree decl ATTRIBUTE_UNUSED)
   switch_to_comdat_section(sect, DECL_NAME (decl));
 }
 
+// FIXME
+
+class diagnostic_output_format_decorator : public diagnostic_output_format
+{
+public:
+  diagnostic_output_format_decorator (std::unique_ptr<diagnostic_output_format> inner)
+  : diagnostic_output_format (inner->get_context ()),
+    m_inner (std::move (inner))
+  {
+  }
+
+  void dump (FILE *out, int indent) const override
+  {
+    fprintf (out, "%*sdiagnostic_output_format_decorator\n", indent, "");
+    if (m_inner)
+      m_inner->dump (out, indent + 2);
+    else
+      fprintf (out, "%*(null)\n", indent + 2, "");
+  }
+
+  std::unique_ptr<diagnostic_per_format_buffer>
+  make_per_format_buffer () override
+  {
+    if (m_inner)
+      return m_inner->make_per_format_buffer ();
+    else
+      return nullptr;
+  }
+
+  void set_buffer (diagnostic_per_format_buffer *buf) override
+  {
+    if (m_inner)
+      m_inner->set_buffer (buf);
+  }
+
+  void on_begin_group () override
+  {
+    if (m_inner)
+      m_inner->on_begin_group ();
+  }
+  void on_end_group () override
+  {
+    if (m_inner)
+      m_inner->on_end_group ();
+  }
+ 
+  void on_report_diagnostic (const diagnostic_info &diag,
+			     diagnostic_t orig_diag_kind) override
+  {
+    /* FIXME: phase 1 and 2 uses output printer,
+       but phase 3 uses inner printer.
+       Fix by making get_printer virtual.  */
+#if 0
+    std::unique_ptr<pretty_printer> old_inner_printer
+      = m_inner->get_printer ();
+    m_inner->m_printer = m_printer.get ();
+#endif
+    if (m_inner)
+      m_inner->on_report_diagnostic (diag, orig_diag_kind);
+#if 0
+    m_inner->m_printer = old_inner_printer;
+#endif
+  }
+
+  void on_diagram (const diagnostic_diagram &diagram) override
+  {
+    if (m_inner)
+      m_inner->on_diagram (diagram);
+  }
+  void after_diagnostic (const diagnostic_info &diag) override
+  {
+    if (m_inner)
+      m_inner->after_diagnostic (diag);
+  }
+  bool machine_readable_stderr_p () const override
+  {
+    if (m_inner)
+      return m_inner->machine_readable_stderr_p ();
+    else
+      return true;
+  }
+  bool follows_reference_printer_p () const override
+  {
+    if (m_inner)
+      return m_inner->follows_reference_printer_p ();
+    else
+      return false;
+  }
+  void update_printer () override
+  {
+    if (m_inner)
+      m_inner->update_printer ();
+  }
+
+  pretty_printer *get_printer () const override
+  {
+    if (m_inner)
+      return m_inner->get_printer ();
+    else
+      return m_printer.get ();
+  }
+
+protected:
+  std::unique_ptr<diagnostic_output_format> m_inner;
+};
+
+/* FIXME.  */
+
+class diagnostic_output_format_varasm : public diagnostic_output_format_decorator
+{
+public:
+  diagnostic_output_format_varasm (std::unique_ptr<diagnostic_output_format> inner)
+  : diagnostic_output_format_decorator (std::move (inner))
+  {
+  }
+
+  void on_finish_unit ()
+  {
+    // Clear the inner output, which will flush it immediately
+    m_inner = nullptr;
+
+    /* Any diagnostics reported from now on will be ignored by this sink
+       (but the asm out file has been finalized, so there's nowhere for
+       them to go.  */
+  }
+};
+
+/* Subclass that writes the generated bytes into a section of the asm file.  */
+
+class asm_section_byte_sink : public json::byte_sink
+{
+public:
+  asm_section_byte_sink (label_text section_name)
+  : m_section_name (std::move (section_name))
+  {
+  }
+
+  void write_bytes (const json::byte_buffer &buf) final override;
+  bool machine_readable_stderr_p () const final override;
+
+private:
+  label_text m_section_name;
+};
+
+// TODO
+
+std::unique_ptr<diagnostic_output_format>
+make_asm_output (std::unique_ptr<diagnostic_output_format> inner)
+{
+  auto outer
+    = std::make_unique<diagnostic_output_format_varasm> (std::move (inner));
+  // TODO: register "plugin" callback:
+  flag_plugin_added = true;
+  register_callback
+    ("not-really-a-plugin", // FIXME
+     PLUGIN_FINISH_UNIT,
+     [] (void *gcc_data, void *user_data)
+     {
+       diagnostic_output_format_varasm *output
+	 = static_cast<diagnostic_output_format_varasm *>
+	     (user_data);
+       output->on_finish_unit ();
+     },
+     outer.get ());
+  return outer;
+}
+
+// class asm_section_byte_sink : public byte_sink
+
+void
+asm_section_byte_sink::write_bytes (const json::byte_buffer &buf)
+{
+  if (!asm_out_file)
+    /* Either the asm file hasn't been opened yet, or has
+       already been closed.  */
+    return;
+
+  section *s = get_section (m_section_name.get (),
+			    0, // unsigned int flags,
+			    NULL_TREE, // tree decl,
+			    true);
+  switch_to_section (s);
+
+  // FIXME: these are arbitrary bytes, not necessarily ASCII
+  ASM_OUTPUT_ASCII (asm_out_file,
+		    (const char *)buf.data (),
+		    buf.size ());
+}
+
+bool
+asm_section_byte_sink::machine_readable_stderr_p () const
+{
+  return false;
+}
+
+/* Make a diagnosticoutput_format for writing SARIF into the asm
+   to create a section named SECTION_NAME.  */
+
+std::unique_ptr<diagnostic_output_format>
+maybe_make_sarif_asm_output (diagnostic_context &context,
+			     const line_maps &line_maps,
+			     const char *main_input_filename_,
+			     const sarif_generation_options &sarif_gen_opts,
+			     std::unique_ptr<json::serializer> serializer,
+			     label_text section_name)
+{
+  auto sink
+    = std::make_unique<asm_section_byte_sink> (std::move (section_name));
+  auto inner = make_sarif_output (context,
+				  line_maps,
+				  main_input_filename,
+				  sarif_gen_opts,
+				  std::move (serializer),
+				  std::move (sink));
+  gcc_assert (inner);
+  return make_asm_output (std::move (inner));
+}
+
 void
 varasm_cc_finalize ()
 {
-- 
2.26.3

