From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1643) id 5132B385AC33; Fri, 15 Jul 2022 21:38:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5132B385AC33 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Thomas Schwinge To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/rust/master] Add new metadata interface to write directly to file X-Act-Checkin: gcc X-Git-Author: Philip Herron X-Git-Refname: refs/heads/devel/rust/master X-Git-Oldrev: 418aef1b006182e84266dcf4f31ee721ed04301a X-Git-Newrev: 2d1378f7310be651e2a538f192d385b136b3d697 Message-Id: <20220715213817.5132B385AC33@sourceware.org> Date: Fri, 15 Jul 2022 21:38:17 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Jul 2022 21:38:17 -0000 https://gcc.gnu.org/g:2d1378f7310be651e2a538f192d385b136b3d697 commit 2d1378f7310be651e2a538f192d385b136b3d697 Author: Philip Herron 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 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 (*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 (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> generic_params; - - AST::Visibility vis = function.get_visibility (); - std::unique_ptr return_type - = std::unique_ptr (nullptr); - if (function.has_return_type ()) - { - return_type = function.get_return_type ()->clone_type (); - } - - std::vector function_params; - for (AST::FunctionParam ¶m : function.get_function_params ()) - { - std::string name = param.get_pattern ()->as_string (); - std::unique_ptr 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> external_items; - external_items.push_back ( - std::unique_ptr (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 (*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 (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> generic_params; + + AST::Visibility vis = function.get_visibility (); + std::unique_ptr return_type + = std::unique_ptr (nullptr); + if (function.has_return_type ()) + { + return_type = function.get_return_type ()->clone_type (); + } + + std::vector function_params; + for (AST::FunctionParam ¶m : function.get_function_params ()) + { + std::string name = param.get_pattern ()->as_string (); + std::unique_ptr 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> external_items; + external_items.push_back ( + std::unique_ptr (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> 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> 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 . */ -// 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 (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