about summary refs log tree commit diff
path: root/src/bootstrap
diff options
context:
space:
mode:
Diffstat (limited to 'src/bootstrap')
-rw-r--r--src/bootstrap/src/utils/cache.rs46
-rw-r--r--src/bootstrap/src/utils/cache/tests.rs52
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs20
3 files changed, 102 insertions, 16 deletions
diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs
index 29342cc5a2c..1c8cc4025df 100644
--- a/src/bootstrap/src/utils/cache.rs
+++ b/src/bootstrap/src/utils/cache.rs
@@ -1,3 +1,17 @@
+//! This module helps you efficiently store and retrieve values using interning.
+//!
+//! Interning is a neat trick that keeps only one copy of identical values, saving memory
+//! and making comparisons super fast. Here, we provide the `Interned<T>` struct and the `Internable` trait
+//! to make interning easy for different data types.
+//!
+//! The `Interner` struct handles caching for common types like `String`, `PathBuf`, and `Vec<String>`,
+//! while the `Cache` struct acts as a write-once storage for linking computation steps with their results.
+//!
+//! # Thread Safety
+//!
+//! We use `Mutex` to make sure interning and retrieval are thread-safe. But keep in mind—once a value is
+//! interned, it sticks around for the entire lifetime of the program.
+
 use std::any::{Any, TypeId};
 use std::borrow::Borrow;
 use std::cell::RefCell;
@@ -12,6 +26,9 @@ use std::{fmt, mem};
 
 use crate::core::builder::Step;
 
+/// Represents an interned value of type `T`, allowing for efficient comparisons and retrieval.
+///
+/// This struct stores a unique index referencing the interned value within an internal cache.
 pub struct Interned<T>(usize, PhantomData<*const T>);
 
 impl<T: Internable + Default> Default for Interned<T> {
@@ -111,6 +128,10 @@ impl<T: Internable + Ord> Ord for Interned<T> {
     }
 }
 
+/// A structure for managing the interning of values of type `T`.
+///
+/// `TyIntern<T>` maintains a mapping between values and their interned representations,
+/// ensuring that duplicate values are not stored multiple times.
 struct TyIntern<T: Clone + Eq> {
     items: Vec<T>,
     set: HashMap<T, Interned<T>>,
@@ -123,6 +144,9 @@ impl<T: Hash + Clone + Eq> Default for TyIntern<T> {
 }
 
 impl<T: Hash + Clone + Eq> TyIntern<T> {
+    /// Interns a borrowed value, ensuring it is stored uniquely.
+    ///
+    /// If the value has been previously interned, the same `Interned<T>` instance is returned.
     fn intern_borrow<B>(&mut self, item: &B) -> Interned<T>
     where
         B: Eq + Hash + ToOwned<Owned = T> + ?Sized,
@@ -138,6 +162,9 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
         interned
     }
 
+    /// Interns an owned value, storing it uniquely.
+    ///
+    /// If the value has been previously interned, the existing `Interned<T>` is returned.
     fn intern(&mut self, item: T) -> Interned<T> {
         if let Some(i) = self.set.get(&item) {
             return *i;
@@ -148,11 +175,16 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
         interned
     }
 
+    /// Retrieves a reference to the interned value associated with the given `Interned<T>` instance.
     fn get(&self, i: Interned<T>) -> &T {
         &self.items[i.0]
     }
 }
 
+/// A global interner for managing interned values of common types.
+///
+/// This structure maintains caches for `String`, `PathBuf`, and `Vec<String>`, ensuring efficient storage
+/// and retrieval of frequently used values.
 #[derive(Default)]
 pub struct Interner {
     strs: Mutex<TyIntern<String>>,
@@ -160,6 +192,10 @@ pub struct Interner {
     lists: Mutex<TyIntern<Vec<String>>>,
 }
 
+/// Defines the behavior required for a type to be internable.
+///
+/// Types implementing this trait must provide access to a static cache and define an `intern` method
+/// that ensures values are stored uniquely.
 trait Internable: Clone + Eq + Hash + 'static {
     fn intern_cache() -> &'static Mutex<TyIntern<Self>>;
 
@@ -187,11 +223,15 @@ impl Internable for Vec<String> {
 }
 
 impl Interner {
+    /// Interns a string reference, ensuring it is stored uniquely.
+    ///
+    /// If the string has been previously interned, the same `Interned<String>` instance is returned.
     pub fn intern_str(&self, s: &str) -> Interned<String> {
         self.strs.lock().unwrap().intern_borrow(s)
     }
 }
 
+/// A global instance of `Interner` that caches common interned values.
 pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
 
 /// This is essentially a `HashMap` which allows storing any type in its input and
@@ -209,10 +249,12 @@ pub struct Cache(
 );
 
 impl Cache {
+    /// Creates a new empty cache.
     pub fn new() -> Cache {
         Cache(RefCell::new(HashMap::new()))
     }
 
+    /// Stores the result of a computation step in the cache.
     pub fn put<S: Step>(&self, step: S, value: S::Output) {
         let mut cache = self.0.borrow_mut();
         let type_id = TypeId::of::<S>();
@@ -225,6 +267,7 @@ impl Cache {
         stepcache.insert(step, value);
     }
 
+    /// Retrieves a cached result for the given step, if available.
     pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
         let mut cache = self.0.borrow_mut();
         let type_id = TypeId::of::<S>();
@@ -255,3 +298,6 @@ impl Cache {
         self.0.borrow().contains_key(&TypeId::of::<S>())
     }
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs
new file mode 100644
index 00000000000..28f5563a589
--- /dev/null
+++ b/src/bootstrap/src/utils/cache/tests.rs
@@ -0,0 +1,52 @@
+use std::path::PathBuf;
+
+use crate::utils::cache::{INTERNER, Internable, TyIntern};
+
+#[test]
+fn test_string_interning() {
+    let s1 = INTERNER.intern_str("Hello");
+    let s2 = INTERNER.intern_str("Hello");
+    let s3 = INTERNER.intern_str("world");
+
+    assert_eq!(s1, s2, "Same strings should be interned to the same instance");
+    assert_ne!(s1, s3, "Different strings should have different interned values");
+}
+
+#[test]
+fn test_path_interning() {
+    let p1 = PathBuf::from("/tmp/file").intern();
+    let p2 = PathBuf::from("/tmp/file").intern();
+    let p3 = PathBuf::from("/tmp/other").intern();
+
+    assert_eq!(p1, p2);
+    assert_ne!(p1, p3);
+}
+
+#[test]
+fn test_vec_interning() {
+    let v1 = vec!["a".to_string(), "b".to_string()].intern();
+    let v2 = vec!["a".to_string(), "b".to_string()].intern();
+    let v3 = vec!["c".to_string()].intern();
+
+    assert_eq!(v1, v2);
+    assert_ne!(v1, v3);
+}
+
+#[test]
+fn test_interned_equality() {
+    let s1 = INTERNER.intern_str("test");
+    let s2 = INTERNER.intern_str("test");
+
+    assert_eq!(s1, s2);
+    assert_eq!(s1, "test");
+}
+
+#[test]
+fn test_ty_intern_intern_borrow() {
+    let mut interner = TyIntern::default();
+    let s1 = interner.intern_borrow("borrowed");
+    let s2 = interner.intern("borrowed".to_string());
+
+    assert_eq!(s1, s2);
+    assert_eq!(interner.get(s1), "borrowed");
+}
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index f6afd50afce..45797c1276c 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -29,11 +29,8 @@ use crate::core::config::TargetSelection;
 use crate::utils::exec::{BootstrapCommand, command};
 use crate::{Build, CLang, GitRepo};
 
-// The `cc` crate doesn't provide a way to obtain a path to the detected archiver,
-// so use some simplified logic here. First we respect the environment variable `AR`, then
-// try to infer the archiver path from the C compiler path.
-// In the future this logic should be replaced by calling into the `cc` crate.
-fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
+/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate.
+fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option<PathBuf> {
     if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) {
         Some(PathBuf::from(ar))
     } else if let Some(ar) = env::var_os("AR") {
@@ -57,16 +54,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option<PathBuf> {
     } else if target.contains("android") || target.contains("-wasi") {
         Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar")))
     } else {
-        let parent = cc.parent().unwrap();
-        let file = cc.file_name().unwrap().to_str().unwrap();
-        for suffix in &["gcc", "cc", "clang"] {
-            if let Some(idx) = file.rfind(suffix) {
-                let mut file = file[..idx].to_owned();
-                file.push_str("ar");
-                return Some(parent.join(&file));
-            }
-        }
-        Some(parent.join(file))
+        Some(default_ar)
     }
 }
 
@@ -138,7 +126,7 @@ pub fn find_target(build: &Build, target: TargetSelection) {
     let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) {
         ar
     } else {
-        cc2ar(compiler.path(), target)
+        cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program()))
     };
 
     build.cc.borrow_mut().insert(target, compiler.clone());