about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/back/archive.rs57
-rw-r--r--src/librustc/back/lto.rs5
-rw-r--r--src/librustc/driver/driver.rs1
-rw-r--r--src/librustc/lib/llvm.rs9
-rw-r--r--src/librustc/metadata/cstore.rs3
-rw-r--r--src/librustc/metadata/filesearch.rs11
-rw-r--r--src/librustc/metadata/loader.rs77
-rw-r--r--src/rustllvm/RustWrapper.cpp40
8 files changed, 186 insertions, 17 deletions
diff --git a/src/librustc/back/archive.rs b/src/librustc/back/archive.rs
index 9cb73ecb07b..453f0c42d8d 100644
--- a/src/librustc/back/archive.rs
+++ b/src/librustc/back/archive.rs
@@ -12,21 +12,29 @@
 
 use driver::session::Session;
 use metadata::filesearch;
+use lib::llvm::{ArchiveRef, llvm};
 
+use std::cast;
 use std::io::fs;
+use std::libc;
 use std::os;
 use std::run::{ProcessOptions, Process, ProcessOutput};
 use std::str;
+use std::unstable::raw;
 use extra::tempfile::TempDir;
 use syntax::abi;
 
-pub static METADATA_FILENAME: &'static str = "metadata";
+pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";
 
 pub struct Archive {
     priv sess: Session,
     priv dst: Path,
 }
 
+pub struct ArchiveRO {
+    priv ptr: ArchiveRef,
+}
+
 fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
         paths: &[&Path]) -> ProcessOutput {
     let ar = sess.opts.ar.clone().unwrap_or_else(|| ~"ar");
@@ -193,3 +201,50 @@ impl Archive {
                                  perhaps an -L flag is missing?", name));
     }
 }
+
+impl ArchiveRO {
+    /// Opens a static archive for read-only purposes. This is more optimized
+    /// than the `open` method because it uses LLVM's internal `Archive` class
+    /// rather than shelling out to `ar` for everything.
+    ///
+    /// If this archive is used with a mutable method, then an error will be
+    /// raised.
+    pub fn open(dst: &Path) -> Option<ArchiveRO> {
+        unsafe {
+            let ar = dst.with_c_str(|dst| {
+                llvm::LLVMRustOpenArchive(dst)
+            });
+            if ar.is_null() {
+                None
+            } else {
+                Some(ArchiveRO { ptr: ar })
+            }
+        }
+    }
+
+    /// Read a file in the archive
+    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
+        unsafe {
+            let mut size = 0 as libc::size_t;
+            let ptr = file.with_c_str(|file| {
+                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
+            });
+            if ptr.is_null() {
+                None
+            } else {
+                Some(cast::transmute(raw::Slice {
+                    data: ptr,
+                    len: size as uint,
+                }))
+            }
+        }
+    }
+}
+
+impl Drop for ArchiveRO {
+    fn drop(&mut self) {
+        unsafe {
+            llvm::LLVMRustDestroyArchive(self.ptr);
+        }
+    }
+}
diff --git a/src/librustc/back/lto.rs b/src/librustc/back/lto.rs
index 65ff67975a2..95801ad55fb 100644
--- a/src/librustc/back/lto.rs
+++ b/src/librustc/back/lto.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use back::archive::Archive;
+use back::archive::ArchiveRO;
 use back::link;
 use driver::session;
 use lib::llvm::{ModuleRef, TargetMachineRef, llvm, True, False};
@@ -43,10 +43,11 @@ pub fn run(sess: session::Session, llmod: ModuleRef,
             }
         };
 
-        let archive = Archive::open(sess, path);
+        let archive = ArchiveRO::open(&path).expect("wanted an rlib");
         debug!("reading {}", name);
         let bc = time(sess.time_passes(), format!("read {}.bc", name), (), |_|
                       archive.read(format!("{}.bc", name)));
+        let bc = bc.expect("missing bytecode in archive!");
         let ptr = bc.as_ptr();
         debug!("linking {}", name);
         time(sess.time_passes(), format!("ll link {}", name), (), |()| unsafe {
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 070aed3aa79..f02a497ee46 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -1100,7 +1100,6 @@ pub fn early_error(emitter: @diagnostic::Emitter, msg: &str) -> ! {
 
 pub fn list_metadata(sess: Session, path: &Path, out: @mut io::Writer) {
     metadata::loader::list_file_metadata(
-        sess,
         token::get_ident_interner(),
         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
 }
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index 5b6c0c2b865..7db4a06e6c0 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -257,6 +257,8 @@ pub enum Pass_opaque {}
 pub type PassRef = *Pass_opaque;
 pub enum TargetMachine_opaque {}
 pub type TargetMachineRef = *TargetMachine_opaque;
+pub enum Archive_opaque {}
+pub type ArchiveRef = *Archive_opaque;
 
 pub mod debuginfo {
     use super::{ValueRef};
@@ -300,7 +302,7 @@ pub mod llvm {
     use super::{Bool, BuilderRef, ContextRef, MemoryBufferRef, ModuleRef};
     use super::{ObjectFileRef, Opcode, PassManagerRef, PassManagerBuilderRef};
     use super::{SectionIteratorRef, TargetDataRef, TypeKind, TypeRef, UseRef};
-    use super::{ValueRef, TargetMachineRef, FileType};
+    use super::{ValueRef, TargetMachineRef, FileType, ArchiveRef};
     use super::{CodeGenModel, RelocMode, CodeGenOptLevel};
     use super::debuginfo::*;
     use std::libc::{c_char, c_int, c_longlong, c_ushort, c_uint, c_ulonglong,
@@ -1748,6 +1750,11 @@ pub mod llvm {
                                           syms: **c_char,
                                           len: size_t);
         pub fn LLVMRustMarkAllFunctionsNounwind(M: ModuleRef);
+
+        pub fn LLVMRustOpenArchive(path: *c_char) -> ArchiveRef;
+        pub fn LLVMRustArchiveReadSection(AR: ArchiveRef, name: *c_char,
+                                          out_len: *mut size_t) -> *c_char;
+        pub fn LLVMRustDestroyArchive(AR: ArchiveRef);
     }
 }
 
diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs
index e236c4e38e6..2080db43a33 100644
--- a/src/librustc/metadata/cstore.rs
+++ b/src/librustc/metadata/cstore.rs
@@ -15,6 +15,7 @@
 
 use metadata::cstore;
 use metadata::decoder;
+use metadata::loader;
 
 use std::hashmap::HashMap;
 use extra;
@@ -29,6 +30,7 @@ pub type cnum_map = @mut HashMap<ast::CrateNum, ast::CrateNum>;
 
 pub enum MetadataBlob {
     MetadataVec(~[u8]),
+    MetadataArchive(loader::ArchiveMetadata),
 }
 
 pub struct crate_metadata {
@@ -216,6 +218,7 @@ impl MetadataBlob {
     pub fn as_slice<'a>(&'a self) -> &'a [u8] {
         match *self {
             MetadataVec(ref vec) => vec.as_slice(),
+            MetadataArchive(ref ar) => ar.as_slice(),
         }
     }
 }
diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs
index 9b6da280be0..66d42cf4429 100644
--- a/src/librustc/metadata/filesearch.rs
+++ b/src/librustc/metadata/filesearch.rs
@@ -123,7 +123,16 @@ pub fn search(filesearch: @FileSearch, pick: pick) {
         match io::result(|| fs::readdir(lib_search_path)) {
             Ok(files) => {
                 let mut rslt = FileDoesntMatch;
-                for path in files.iter() {
+                let is_rlib = |p: & &Path| {
+                    p.extension_str() == Some("rlib")
+                };
+                // Reading metadata out of rlibs is faster, and if we find both
+                // an rlib and a dylib we only read one of the files of
+                // metadata, so in the name of speed, bring all rlib files to
+                // the front of the search list.
+                let files1 = files.iter().filter(|p| is_rlib(p));
+                let files2 = files.iter().filter(|p| !is_rlib(p));
+                for path in files1.chain(files2) {
                     debug!("testing {}", path.display());
                     let maybe_picked = pick(path);
                     match maybe_picked {
diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs
index ae851a7377d..d670fc532b5 100644
--- a/src/librustc/metadata/loader.rs
+++ b/src/librustc/metadata/loader.rs
@@ -10,10 +10,10 @@
 
 //! Finds crate binaries and loads their metadata
 
-use back::archive::{Archive, METADATA_FILENAME};
+use back::archive::{ArchiveRO, METADATA_FILENAME};
 use driver::session::Session;
 use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
-use metadata::cstore::{MetadataBlob, MetadataVec};
+use metadata::cstore::{MetadataBlob, MetadataVec, MetadataArchive};
 use metadata::decoder;
 use metadata::encoder;
 use metadata::filesearch::{FileMatches, FileDoesntMatch};
@@ -61,6 +61,12 @@ pub struct Library {
     metadata: MetadataBlob,
 }
 
+pub struct ArchiveMetadata {
+    priv archive: ArchiveRO,
+    // See comments in ArchiveMetadata::new for why this is static
+    priv data: &'static [u8],
+}
+
 impl Context {
     pub fn load_library_crate(&self) -> Library {
         match self.find_library_crate() {
@@ -102,7 +108,7 @@ impl Context {
                     if candidate && existing {
                         FileMatches
                     } else if candidate {
-                        match get_metadata_section(self.sess, self.os, path) {
+                        match get_metadata_section(self.os, path) {
                             Some(cvec) =>
                                 if crate_matches(cvec.as_slice(), self.name,
                                                  self.version, self.hash) {
@@ -248,11 +254,60 @@ fn crate_matches(crate_data: &[u8],
     }
 }
 
-fn get_metadata_section(sess: Session, os: Os,
-                        filename: &Path) -> Option<MetadataBlob> {
+impl ArchiveMetadata {
+    fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
+        let data: &'static [u8] = {
+            let data = match ar.read(METADATA_FILENAME) {
+                Some(data) => data,
+                None => {
+                    debug!("didn't find '{}' in the archive", METADATA_FILENAME);
+                    return None;
+                }
+            };
+            // This data is actually a pointer inside of the archive itself, but
+            // we essentially want to cache it because the lookup inside the
+            // archive is a fairly expensive operation (and it's queried for
+            // *very* frequently). For this reason, we transmute it to the
+            // static lifetime to put into the struct. Note that the buffer is
+            // never actually handed out with a static lifetime, but rather the
+            // buffer is loaned with the lifetime of this containing object.
+            // Hence, we're guaranteed that the buffer will never be used after
+            // this object is dead, so this is a safe operation to transmute and
+            // store the data as a static buffer.
+            unsafe { cast::transmute(data) }
+        };
+        Some(ArchiveMetadata {
+            archive: ar,
+            data: data,
+        })
+    }
+
+    pub fn as_slice<'a>(&'a self) -> &'a [u8] { self.data }
+}
+
+// Just a small wrapper to time how long reading metadata takes.
+fn get_metadata_section(os: Os, filename: &Path) -> Option<MetadataBlob> {
+    use extra::time;
+    let start = time::precise_time_ns();
+    let ret = get_metadata_section_imp(os, filename);
+    info!("reading {} => {}ms", filename.filename_display(),
+           (time::precise_time_ns() - start) / 1000000);
+    return ret;
+}
+
+fn get_metadata_section_imp(os: Os, filename: &Path) -> Option<MetadataBlob> {
     if filename.filename_str().unwrap().ends_with(".rlib") {
-        let archive = Archive::open(sess, filename.clone());
-        return Some(MetadataVec(archive.read(METADATA_FILENAME)));
+        // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap
+        // internally to read the file. We also avoid even using a memcpy by
+        // just keeping the archive along while the metadata is in use.
+        let archive = match ArchiveRO::open(filename) {
+            Some(ar) => ar,
+            None => {
+                debug!("llvm didn't like `{}`", filename.display());
+                return None;
+            }
+        };
+        return ArchiveMetadata::new(archive).map(|ar| MetadataArchive(ar));
     }
     unsafe {
         let mb = filename.with_c_str(|buf| {
@@ -322,13 +377,13 @@ pub fn read_meta_section_name(os: Os) -> &'static str {
 }
 
 // A diagnostic function for dumping crate metadata to an output stream
-pub fn list_file_metadata(sess: Session,
-                          intr: @ident_interner,
+pub fn list_file_metadata(intr: @ident_interner,
                           os: Os,
                           path: &Path,
                           out: @mut io::Writer) {
-    match get_metadata_section(sess, os, path) {
-      option::Some(bytes) => decoder::list_crate_metadata(intr, bytes.as_slice(),
+    match get_metadata_section(os, path) {
+      option::Some(bytes) => decoder::list_crate_metadata(intr,
+                                                          bytes.as_slice(),
                                                           out),
       option::None => {
         write!(out, "could not find metadata in {}.\n", path.display())
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index fb611dd15c2..d66f90a5352 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #include "rustllvm.h"
+#include "llvm/Object/Archive.h"
 
 //===----------------------------------------------------------------------===
 //
@@ -19,6 +20,7 @@
 
 using namespace llvm;
 using namespace llvm::sys;
+using namespace llvm::object;
 
 const char *LLVMRustError;
 
@@ -558,3 +560,41 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) {
     }
     return true;
 }
+
+extern "C" void*
+LLVMRustOpenArchive(char *path) {
+    OwningPtr<MemoryBuffer> buf;
+    error_code err = MemoryBuffer::getFile(path, buf);
+    if (err) {
+        LLVMRustError = err.message().c_str();
+        return NULL;
+    }
+    Archive *ret = new Archive(buf.take(), err);
+    if (err) {
+        LLVMRustError = err.message().c_str();
+        return NULL;
+    }
+    return ret;
+}
+
+extern "C" const char*
+LLVMRustArchiveReadSection(Archive *ar, char *name, size_t *size) {
+    for (Archive::child_iterator child = ar->begin_children(),
+                                   end = ar->end_children();
+         child != end; ++child) {
+        StringRef sect_name;
+        error_code err = child->getName(sect_name);
+        if (err) continue;
+        if (sect_name.trim(" ") == name) {
+            StringRef buf = child->getBuffer();
+            *size = buf.size();
+            return buf.data();
+        }
+    }
+    return NULL;
+}
+
+extern "C" void
+LLVMRustDestroyArchive(Archive *ar) {
+    delete ar;
+}