public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Add new metadata interface to write directly to file
@ 2022-07-15 21:38 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-07-15 21:38 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2d1378f7310be651e2a538f192d385b136b3d697

commit 2d1378f7310be651e2a538f192d385b136b3d697
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Fri Jul 15 11:56:19 2022 +0100

    Add new metadata interface to write directly to file
    
    This interface will allow us to write the metadata directly to a file.
    MacOs does not support metadata embeded directly into a .section of
    object files and archives. This allows us to seperate this mechanism so
    that we can follow rustc and write this directly to a seperate file.
    
    This patch adds two new options to control metadata outputs
    
     -frust-embed-metadata this toggles to embed the metadata into .rust_export
     section of the target asm output
    
     -frust-metadata-output= specifies the path to directly write the metadata
     to file
    
    We need these options as embeding the metadata does not seem to be
    supported for all platforms like MacOs so writing directly to file is a
    platform agnostic solution.
    
    There is an implicit naming convetion to metadata export files. They need
    to be of crate_name.rox. This patch adds some simple validation by checking
    the basename of the specified path.

Diff:
---
 gcc/rust/lang.opt                         |   8 +
 gcc/rust/metadata/rust-export-metadata.cc | 352 ++++++++++++++++++++----------
 gcc/rust/metadata/rust-export-metadata.h  |  37 +++-
 gcc/rust/rust-object-export.cc            |   6 -
 gcc/rust/rust-session-manager.cc          |  24 +-
 gcc/rust/rust-session-manager.h           |  17 ++
 6 files changed, 324 insertions(+), 120 deletions(-)

diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
index 58ec9a75e9a..1f6855ede1d 100644
--- a/gcc/rust/lang.opt
+++ b/gcc/rust/lang.opt
@@ -103,6 +103,14 @@ Enum(frust_edition) String(2018) Value(1)
 EnumValue
 Enum(frust_edition) String(2021) Value(2)
 
+frust-embed-metadata
+Rust Var(flag_rust_embed_metadata)
+Flag to enable embeding metadata directly into object files
+
+frust-metadata-output=
+Rust Joined RejectNegative
+-frust-metadata-output=<path.rox>  Path to output crate metadata
+
 o
 Rust Joined Separate
 ; Documented in common.opt
diff --git a/gcc/rust/metadata/rust-export-metadata.cc b/gcc/rust/metadata/rust-export-metadata.cc
index ff909386f88..4856bc26149 100644
--- a/gcc/rust/metadata/rust-export-metadata.cc
+++ b/gcc/rust/metadata/rust-export-metadata.cc
@@ -29,127 +29,126 @@
 namespace Rust {
 namespace Metadata {
 
-class ExportContext
-{
-public:
-  ExportContext () : mappings (Analysis::Mappings::get ()) {}
+static const std::string extension_path = ".rox";
 
-  ~ExportContext () {}
+ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {}
 
-  void push_module_scope (const HIR::Module &module)
-  {
-    module_stack.push_back (module);
-  }
+ExportContext::~ExportContext () {}
 
-  const HIR::Module &pop_module_scope ()
-  {
-    rust_assert (!module_stack.empty ());
-
-    const HIR::Module &poped = module_stack.back ();
-    module_stack.pop_back ();
-    return poped;
-  }
+void
+ExportContext::push_module_scope (const HIR::Module &module)
+{
+  module_stack.push_back (module);
+}
 
-  void emit_trait (const HIR::Trait &trait)
-  {
-    // lookup the AST node for this
-    AST::Item *item = nullptr;
-    bool ok
-      = mappings->lookup_ast_item (trait.get_mappings ().get_nodeid (), &item);
-    rust_assert (ok);
+const HIR::Module &
+ExportContext::pop_module_scope ()
+{
+  rust_assert (!module_stack.empty ());
+  const HIR::Module &poped = module_stack.back ();
+  module_stack.pop_back ();
+  return poped;
+}
 
-    std::stringstream oss;
-    AST::Dump dumper (oss);
-    dumper.go (*item);
+void
+ExportContext::emit_trait (const HIR::Trait &trait)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok
+    = mappings->lookup_ast_item (trait.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
 
-    public_interface_buffer += oss.str ();
-  }
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  dumper.go (*item);
 
-  void emit_function (const HIR::Function &fn)
-  {
-    // lookup the AST node for this
-    AST::Item *item = nullptr;
-    bool ok
-      = mappings->lookup_ast_item (fn.get_mappings ().get_nodeid (), &item);
-    rust_assert (ok);
+  public_interface_buffer += oss.str ();
+}
 
-    // FIXME add assertion that item must be a vis_item;
-    AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item);
+void
+ExportContext::emit_function (const HIR::Function &fn)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok = mappings->lookup_ast_item (fn.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
 
-    // if its a generic function we need to output the full declaration
-    // otherwise we can let people link against this
+  // is this a CFG macro or not
+  if (item->is_marked_for_strip ())
+    return;
 
-    std::stringstream oss;
-    AST::Dump dumper (oss);
-    if (!fn.has_generics ())
-      {
-	// FIXME assert that this is actually an AST::Function
-	AST::Function &function = static_cast<AST::Function &> (vis_item);
-
-	// we can emit an extern block with abi of "rust"
-	Identifier item_name = function.get_function_name ();
-
-	// always empty for extern linkage
-	AST::WhereClause where_clause = AST::WhereClause::create_empty ();
-	std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
-
-	AST::Visibility vis = function.get_visibility ();
-	std::unique_ptr<AST::Type> return_type
-	  = std::unique_ptr<AST::Type> (nullptr);
-	if (function.has_return_type ())
-	  {
-	    return_type = function.get_return_type ()->clone_type ();
-	  }
-
-	std::vector<AST::NamedFunctionParam> function_params;
-	for (AST::FunctionParam &param : function.get_function_params ())
-	  {
-	    std::string name = param.get_pattern ()->as_string ();
-	    std::unique_ptr<AST::Type> param_type
-	      = param.get_type ()->clone_type ();
-
-	    AST::NamedFunctionParam p (name, std::move (param_type), {},
-				       param.get_locus ());
-	    function_params.push_back (std::move (p));
-	  }
-
-	AST::ExternalItem *external_item = new AST::ExternalFunctionItem (
-	  item_name, {} /* generic_params */, std::move (return_type),
-	  where_clause, std::move (function_params), false /* has_variadics */,
-	  {} /* variadic_outer_attrs */, vis, function.get_outer_attrs (),
-	  function.get_locus ());
-
-	std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
-	external_items.push_back (
-	  std::unique_ptr<AST::ExternalItem> (external_item));
-
-	AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
-				       std::move (external_items),
-				       vis_item.get_visibility (), {}, {},
-				       fn.get_locus ());
-
-	dumper.go (extern_block);
-      }
-    else
-      {
-	dumper.go (*item);
-      }
+  // FIXME add assertion that item must be a vis_item;
+  AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item);
 
-    // store the dump
-    public_interface_buffer += oss.str ();
-  }
+  // if its a generic function we need to output the full declaration
+  // otherwise we can let people link against this
 
-  const std::string &get_interface_buffer () const
-  {
-    return public_interface_buffer;
-  }
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  if (!fn.has_generics ())
+    {
+      // FIXME assert that this is actually an AST::Function
+      AST::Function &function = static_cast<AST::Function &> (vis_item);
+
+      // we can emit an extern block with abi of "rust"
+      Identifier item_name = function.get_function_name ();
+
+      // always empty for extern linkage
+      AST::WhereClause where_clause = AST::WhereClause::create_empty ();
+      std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
+
+      AST::Visibility vis = function.get_visibility ();
+      std::unique_ptr<AST::Type> return_type
+	= std::unique_ptr<AST::Type> (nullptr);
+      if (function.has_return_type ())
+	{
+	  return_type = function.get_return_type ()->clone_type ();
+	}
+
+      std::vector<AST::NamedFunctionParam> function_params;
+      for (AST::FunctionParam &param : function.get_function_params ())
+	{
+	  std::string name = param.get_pattern ()->as_string ();
+	  std::unique_ptr<AST::Type> param_type
+	    = param.get_type ()->clone_type ();
+
+	  AST::NamedFunctionParam p (name, std::move (param_type), {},
+				     param.get_locus ());
+	  function_params.push_back (std::move (p));
+	}
+
+      AST::ExternalItem *external_item = new AST::ExternalFunctionItem (
+	item_name, {} /* generic_params */, std::move (return_type),
+	where_clause, std::move (function_params), false /* has_variadics */,
+	{} /* variadic_outer_attrs */, vis, function.get_outer_attrs (),
+	function.get_locus ());
+
+      std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
+      external_items.push_back (
+	std::unique_ptr<AST::ExternalItem> (external_item));
+
+      AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
+				     std::move (external_items),
+				     vis_item.get_visibility (), {}, {},
+				     fn.get_locus ());
+
+      dumper.go (extern_block);
+    }
+  else
+    {
+      dumper.go (*item);
+    }
 
-private:
-  Analysis::Mappings *mappings;
+  // store the dump
+  public_interface_buffer += oss.str ();
+}
 
-  std::vector<std::reference_wrapper<const HIR::Module>> module_stack;
-  std::string public_interface_buffer;
-};
+const std::string &
+ExportContext::get_interface_buffer () const
+{
+  return public_interface_buffer;
+}
 
 // implicitly by using HIR nodes we know that these have passed CFG expansion
 // and they exist in the compilation unit
@@ -183,20 +182,28 @@ private:
 };
 
 PublicInterface::PublicInterface (HIR::Crate &crate)
-  : crate (crate), mappings (*Analysis::Mappings::get ())
+  : crate (crate), mappings (*Analysis::Mappings::get ()), context ()
 {}
 
 void
 PublicInterface::Export (HIR::Crate &crate)
 {
   PublicInterface interface (crate);
-  interface.go ();
+  interface.gather_export_data ();
+  interface.write_to_object_file ();
+}
+
+void
+PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path)
+{
+  PublicInterface interface (crate);
+  interface.gather_export_data ();
+  interface.write_to_path (output_path);
 }
 
 void
-PublicInterface::go ()
+PublicInterface::gather_export_data ()
 {
-  ExportContext context;
   ExportVisItems visitor (context);
   for (auto &item : crate.items)
     {
@@ -208,7 +215,11 @@ PublicInterface::go ()
       if (is_crate_public (vis_item))
 	vis_item.accept_vis (visitor);
     }
+}
 
+void
+PublicInterface::write_to_object_file () const
+{
   // done
   const auto &buf = context.get_interface_buffer ();
   std::string size_buffer = std::to_string (buf.size ());
@@ -236,6 +247,116 @@ PublicInterface::go ()
   rust_write_export_data (buf.c_str (), buf.size ());
 }
 
+void
+PublicInterface::write_to_path (const std::string &path) const
+{
+  // validate path contains correct extension
+  const std::string expected_file_name = expected_metadata_filename ();
+  const char *path_base_name = basename (path.c_str ());
+  if (strcmp (path_base_name, expected_file_name.c_str ()) != 0)
+    {
+      rust_error_at (Location (),
+		     "expected metadata-output path to have base file name of: "
+		     "%<%s%> got %<%s%>",
+		     expected_file_name.c_str (), path_base_name);
+      return;
+    }
+
+  // done
+  const auto &buf = context.get_interface_buffer ();
+  std::string size_buffer = std::to_string (buf.size ());
+
+  // md5 this
+  struct md5_ctx chksm;
+  unsigned char checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (buf.c_str (), buf.size (), &chksm);
+  md5_finish_ctx (&chksm, checksum);
+
+  // MAGIC MD5 DLIM  DLIM buffer-size DELIM contents
+  const std::string current_crate_name = mappings.get_current_crate_name ();
+
+  // write to path
+  FILE *nfd = fopen (path.c_str (), "wb");
+  if (nfd == NULL)
+    {
+      rust_error_at (Location (), "failed to open file %<%s%> for writing: %s",
+		     path.c_str (), xstrerror (errno));
+      return;
+    }
+
+  // write data
+  if (fwrite (kMagicHeader, sizeof (kMagicHeader), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (checksum, sizeof (checksum), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (current_crate_name.c_str (), current_crate_name.size (), 1, nfd)
+      < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (size_buffer.c_str (), size_buffer.size (), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (!buf.empty ())
+    if (fwrite (buf.c_str (), buf.size (), 1, nfd) < 1)
+      {
+	rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		       path.c_str (), xstrerror (errno));
+	fclose (nfd);
+	return;
+      }
+
+  // done
+  fclose (nfd);
+}
+
 bool
 PublicInterface::is_crate_public (const HIR::VisItem &item)
 {
@@ -251,5 +372,14 @@ PublicInterface::is_crate_public (const HIR::VisItem &item)
   return is_public && !has_path;
 }
 
+std::string
+PublicInterface::expected_metadata_filename ()
+{
+  auto mappings = Analysis::Mappings::get ();
+
+  const std::string current_crate_name = mappings->get_current_crate_name ();
+  return current_crate_name + extension_path;
+}
+
 } // namespace Metadata
 } // namespace Rust
diff --git a/gcc/rust/metadata/rust-export-metadata.h b/gcc/rust/metadata/rust-export-metadata.h
index 41c89aa7dbc..cbb6ecd65a6 100644
--- a/gcc/rust/metadata/rust-export-metadata.h
+++ b/gcc/rust/metadata/rust-export-metadata.h
@@ -29,19 +29,54 @@ namespace Metadata {
 static const char kMagicHeader[4] = {'G', 'R', 'S', 'T'};
 static const char kSzDelim[1] = {'$'};
 
+class ExportContext
+{
+public:
+  ExportContext ();
+
+  ~ExportContext ();
+
+  void push_module_scope (const HIR::Module &module);
+
+  const HIR::Module &pop_module_scope ();
+
+  void emit_trait (const HIR::Trait &trait);
+
+  void emit_function (const HIR::Function &fn);
+
+  const std::string &get_interface_buffer () const;
+
+private:
+  Analysis::Mappings *mappings;
+
+  std::vector<std::reference_wrapper<const HIR::Module>> module_stack;
+  std::string public_interface_buffer;
+};
+
 class PublicInterface
 {
 public:
   static void Export (HIR::Crate &crate);
 
+  static void ExportTo (HIR::Crate &crate, const std::string &output_path);
+
   static bool is_crate_public (const HIR::VisItem &item);
 
+  static std::string expected_metadata_filename ();
+
+protected:
+  void gather_export_data ();
+
+  void write_to_object_file () const;
+
+  void write_to_path (const std::string &path) const;
+
 private:
   PublicInterface (HIR::Crate &crate);
-  void go ();
 
   HIR::Crate &crate;
   Analysis::Mappings &mappings;
+  ExportContext context;
 };
 
 } // namespace Metadata
diff --git a/gcc/rust/rust-object-export.cc b/gcc/rust/rust-object-export.cc
index 0707b7999ba..f3007289ead 100644
--- a/gcc/rust/rust-object-export.cc
+++ b/gcc/rust/rust-object-export.cc
@@ -17,9 +17,6 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
-// FIXME: doesn't this duplicate lots of code from rust-backend.c? Is one meant
-// to be a replacement?
-
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -54,9 +51,6 @@
 #define TARGET_AIX 0
 #endif
 
-/* This file holds all the cases where the Rust frontend needs
-   information from gcc's backend.  */
-
 /* Return whether or not GCC has reported any errors.  */
 
 bool
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
index 7a9f8b30d25..eb4240a447f 100644
--- a/gcc/rust/rust-session-manager.cc
+++ b/gcc/rust/rust-session-manager.cc
@@ -461,6 +461,10 @@ Session::handle_option (
       options.set_edition (flag_rust_edition);
       break;
 
+    case OPT_frust_metadata_output_:
+      options.set_metadata_output (arg);
+      break;
+
     default:
       break;
     }
@@ -796,8 +800,22 @@ Session::parse_file (const char *filename)
       Analysis::ScanDeadcode::Scan (hir);
       Analysis::UnusedVariables::Lint (ctx);
 
-      // emit metadata
-      Metadata::PublicInterface::Export (hir);
+      // metadata
+      bool specified_emit_metadata
+	= flag_rust_embed_metadata || options.metadata_output_path_set ();
+      if (!specified_emit_metadata)
+	{
+	  Metadata::PublicInterface::ExportTo (
+	    hir, Metadata::PublicInterface::expected_metadata_filename ());
+	}
+      else
+	{
+	  if (flag_rust_embed_metadata)
+	    Metadata::PublicInterface::Export (hir);
+	  if (options.metadata_output_path_set ())
+	    Metadata::PublicInterface::ExportTo (
+	      hir, options.get_metadata_output ());
+	}
     }
 
   // pass to GCC middle-end
@@ -952,6 +970,8 @@ Session::injection (AST::Crate &crate)
    * an invalid crate type is not specified, so maybe just do that. Valid
    * crate types: bin lib dylib staticlib cdylib rlib proc-macro */
 
+  // this crate type will have options affecting the metadata ouput
+
   rust_debug ("finished injection");
 }
 
diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h
index e2aa8bc0fd9..c68e796461f 100644
--- a/gcc/rust/rust-session-manager.h
+++ b/gcc/rust/rust-session-manager.h
@@ -189,6 +189,8 @@ struct CompileOptions
   bool enable_test = false;
   bool debug_assertions = false;
   bool proc_macro = false;
+  std::string metadata_output_path;
+
   enum Edition
   {
     E2015 = 0,
@@ -236,6 +238,21 @@ struct CompileOptions
   {
     edition = static_cast<Edition> (raw_edition);
   }
+
+  void set_metadata_output (const std::string &path)
+  {
+    metadata_output_path = path;
+  }
+
+  const std::string &get_metadata_output () const
+  {
+    return metadata_output_path;
+  }
+
+  bool metadata_output_path_set () const
+  {
+    return !metadata_output_path.empty ();
+  }
 };
 
 /* Defines a compiler session. This is for a single compiler invocation, so


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-07-15 21:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-15 21:38 [gcc/devel/rust/master] Add new metadata interface to write directly to file Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).