Skip to content

Commit fce4a17

Browse files
committed
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means is: * When compiling an rlib, in addition to insdering foo.o into the archive, also insert foo.bc (the LLVM bytecode) of the optimized module. * When the compiler detects the -Z lto option, it will attempt to perform LTO on a staticlib or binary output. The compiler will emit an error if a dylib or rlib output is being generated. * The actual act of performing LTO is as follows: 1. Force all upstream libraries to have an rlib version available. 2. Load the bytecode of each upstream library from the rlib. 3. Link all this bytecode into the current LLVM module (just using llvm apis) 4. Run an internalization pass which internalizes all symbols except those found reachable for the local crate of compilation. 5. Run the LLVM LTO pass manager over this entire module 6a. If assembling an archive, then add all upstream rlibs into the output archive. This ignores all of the object/bitcode/metadata files rust generated and placed inside the rlibs. 6b. If linking a binary, create copies of all upstream rlibs, remove the rust-generated object-file, and then link everything as usual. As I have explained in #10741, this process is excruciatingly slow, so this is *not* turned on by default, and it is also why I have decided to hide it behind a -Z flag for now. The good news is that the binary sizes are about as small as they can be as a result of LTO, so it's definitely working. Closes #10741 Closes #10740
1 parent 52b835c commit fce4a17

File tree

20 files changed

+432
-124
lines changed

20 files changed

+432
-124
lines changed

src/librustc/back/archive.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
4242
}
4343
let o = Process::new(ar, args.as_slice(), opts).finish_with_output();
4444
if !o.status.success() {
45-
sess.err(format!("{} failed with: {}", ar, o.status));
45+
sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
46+
o.status));
4647
sess.note(format!("stdout ---\n{}", str::from_utf8(o.output)));
4748
sess.note(format!("stderr ---\n{}", str::from_utf8(o.error)));
4849
sess.abort_if_errors();
@@ -88,16 +89,34 @@ impl Archive {
8889

8990
/// Adds all of the contents of the rlib at the specified path to this
9091
/// archive.
91-
pub fn add_rlib(&mut self, rlib: &Path) {
92-
let name = rlib.filename_str().unwrap().split('-').next().unwrap();
93-
self.add_archive(rlib, name, [METADATA_FILENAME]);
92+
///
93+
/// This ignores adding the bytecode from the rlib, and if LTO is enabled
94+
/// then the object file also isn't added.
95+
pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool) {
96+
let object = format!("{}.o", name);
97+
let bytecode = format!("{}.bc", name);
98+
let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
99+
if lto {
100+
ignore.push(object.as_slice());
101+
}
102+
self.add_archive(rlib, name, ignore);
94103
}
95104

96105
/// Adds an arbitrary file to this archive
97106
pub fn add_file(&mut self, file: &Path) {
98107
run_ar(self.sess, "r", None, [&self.dst, file]);
99108
}
100109

110+
/// Removes a file from this archive
111+
pub fn remove_file(&mut self, file: &str) {
112+
run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
113+
}
114+
115+
pub fn files(&self) -> ~[~str] {
116+
let output = run_ar(self.sess, "t", None, [&self.dst]);
117+
str::from_utf8(output.output).lines().map(|s| s.to_owned()).collect()
118+
}
119+
101120
fn add_archive(&mut self, archive: &Path, name: &str, skip: &[&str]) {
102121
let loc = TempDir::new("rsar").unwrap();
103122

@@ -109,11 +128,17 @@ impl Archive {
109128
// The reason for this is that archives are keyed off the name of the
110129
// files, so if two files have the same name they will override one
111130
// another in the archive (bad).
131+
//
132+
// We skip any files explicitly desired for skipping, and we also skip
133+
// all SYMDEF files as these are just magical placeholders which get
134+
// re-created when we make a new archive anyway.
112135
let files = fs::readdir(loc.path());
113136
let mut inputs = ~[];
114137
for file in files.iter() {
115138
let filename = file.filename_str().unwrap();
116139
if skip.iter().any(|s| *s == filename) { continue }
140+
if filename.contains(".SYMDEF") { continue }
141+
117142
let filename = format!("r-{}-{}", name, filename);
118143
let new_filename = file.with_filename(filename);
119144
fs::rename(file, &new_filename);

0 commit comments

Comments
 (0)