From 4ded0b82caf33969d44b47c20afcbde4cf523424 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Fri, 12 Apr 2024 00:09:43 +0300
Subject: [PATCH] linker: Avoid some allocations in search directory iteration

---
 compiler/rustc_codegen_ssa/src/back/link.rs | 27 +++++++++------------
 compiler/rustc_interface/src/passes.rs      |  4 ++-
 compiler/rustc_metadata/src/native_libs.rs  |  8 +++---
 compiler/rustc_session/src/filesearch.rs    |  4 +--
 4 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index ac278de02af..f4374392b9b 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -56,8 +56,13 @@ use std::{env, fmt, fs, io, mem, str};
 pub struct SearchPaths(OnceCell<Vec<PathBuf>>);
 
 impl SearchPaths {
-    pub(super) fn get(&self, sess: &Session) -> &[PathBuf] {
-        self.0.get_or_init(|| archive_search_paths(sess))
+    pub(super) fn get(&self, sess: &Session) -> impl Iterator<Item = &Path> {
+        let native_search_paths = || {
+            Vec::from_iter(
+                sess.target_filesearch(PathKind::Native).search_path_dirs().map(|p| p.to_owned()),
+            )
+        };
+        self.0.get_or_init(native_search_paths).iter().map(|p| &**p)
     }
 }
 
@@ -310,8 +315,6 @@ fn link_rlib<'a>(
     flavor: RlibFlavor,
     tmpdir: &MaybeTempDir,
 ) -> Result<Box<dyn ArchiveBuilder + 'a>, ErrorGuaranteed> {
-    let lib_search_paths = archive_search_paths(sess);
-
     let mut ab = archive_builder_builder.new_archive_builder(sess);
 
     let trailing_metadata = match flavor {
@@ -378,26 +381,24 @@ fn link_rlib<'a>(
     // feature then we'll need to figure out how to record what objects were
     // loaded from the libraries found here and then encode that into the
     // metadata of the rlib we're generating somehow.
+    let search_paths = SearchPaths::default();
     for lib in codegen_results.crate_info.used_libraries.iter() {
         let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {
             continue;
         };
+        let search_paths = search_paths.get(sess);
         if flavor == RlibFlavor::Normal
             && let Some(filename) = lib.filename
         {
-            let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess);
+            let path = find_native_static_library(filename.as_str(), true, search_paths, sess);
             let src = read(path)
                 .map_err(|e| sess.dcx().emit_fatal(errors::ReadFileError { message: e }))?;
             let (data, _) = create_wrapper_file(sess, ".bundled_lib".to_string(), &src);
             let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str());
             packed_bundled_libs.push(wrapper_file);
         } else {
-            let path = find_native_static_library(
-                lib.name.as_str(),
-                lib.verbatim,
-                &lib_search_paths,
-                sess,
-            );
+            let path =
+                find_native_static_library(lib.name.as_str(), lib.verbatim, search_paths, sess);
             ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
                 sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })
             });
@@ -1445,10 +1446,6 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> (bool, bool) {
     }
 }
 
-fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
-    sess.target_filesearch(PathKind::Native).search_path_dirs()
-}
-
 #[derive(PartialEq)]
 enum RlibFlavor {
     Normal,
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index d04d30b3cb0..fe546d05ff2 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -170,7 +170,9 @@ fn configure_and_expand(
         let mut old_path = OsString::new();
         if cfg!(windows) {
             old_path = env::var_os("PATH").unwrap_or(old_path);
-            let mut new_path = sess.host_filesearch(PathKind::All).search_path_dirs();
+            let mut new_path = Vec::from_iter(
+                sess.host_filesearch(PathKind::All).search_path_dirs().map(|p| p.to_owned()),
+            );
             for path in env::split_paths(&old_path) {
                 if !new_path.contains(&path) {
                     new_path.push(path);
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index baa2e1ff602..c43b730eccf 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -17,12 +17,12 @@ use rustc_target::spec::abi::Abi;
 
 use crate::errors;
 
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
-pub fn find_native_static_library(
+pub fn find_native_static_library<'a>(
     name: &str,
     verbatim: bool,
-    search_paths: &[PathBuf],
+    search_paths: impl Iterator<Item = &'a Path>,
     sess: &Session,
 ) -> PathBuf {
     let formats = if verbatim {
@@ -60,7 +60,7 @@ fn find_bundled_library(
         && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
     {
         let verbatim = verbatim.unwrap_or(false);
-        let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs();
+        let search_paths = sess.target_filesearch(PathKind::Native).search_path_dirs();
         return find_native_static_library(name.as_str(), verbatim, search_paths, sess)
             .file_name()
             .and_then(|s| s.to_str())
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 4f0e3354680..6120900d9f4 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -47,8 +47,8 @@ impl<'a> FileSearch<'a> {
     }
 
     /// Returns just the directories within the search paths.
-    pub fn search_path_dirs(&self) -> Vec<PathBuf> {
-        self.search_paths().map(|sp| sp.dir.to_path_buf()).collect()
+    pub fn search_path_dirs(&self) -> impl Iterator<Item = &'a Path> {
+        self.search_paths().map(|sp| &*sp.dir)
     }
 }