]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/lto.rs
c0b1492a7845099c2d22a2a13f5738c269b11d2c
[rust.git] / src / librustc_trans / back / lto.rs
1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use super::link;
12 use super::write;
13 use rustc::session::{self, config};
14 use llvm;
15 use llvm::archive_ro::ArchiveRO;
16 use llvm::{ModuleRef, TargetMachineRef, True, False};
17 use rustc::metadata::cstore;
18 use rustc::util::common::time;
19
20 use libc;
21 use flate;
22
23 use std::ffi::CString;
24 use std::iter;
25 use std::mem;
26 use std::num::Int;
27
28 pub fn run(sess: &session::Session, llmod: ModuleRef,
29            tm: TargetMachineRef, reachable: &[String]) {
30     if sess.opts.cg.prefer_dynamic {
31         sess.err("cannot prefer dynamic linking when performing LTO");
32         sess.note("only 'staticlib' and 'bin' outputs are supported with LTO");
33         sess.abort_if_errors();
34     }
35
36     // Make sure we actually can run LTO
37     for crate_type in sess.crate_types.borrow().iter() {
38         match *crate_type {
39             config::CrateTypeExecutable | config::CrateTypeStaticlib => {}
40             _ => {
41                 sess.fatal("lto can only be run for executables and \
42                             static library outputs");
43             }
44         }
45     }
46
47     // For each of our upstream dependencies, find the corresponding rlib and
48     // load the bitcode from the archive. Then merge it into the current LLVM
49     // module that we've got.
50     let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
51     for (cnum, path) in crates.into_iter() {
52         let name = sess.cstore.get_crate_data(cnum).name.clone();
53         let path = match path {
54             Some(p) => p,
55             None => {
56                 sess.fatal(&format!("could not find rlib for: `{}`",
57                                    name)[]);
58             }
59         };
60
61         let archive = ArchiveRO::open(&path).expect("wanted an rlib");
62         let file = path.filename_str().unwrap();
63         let file = &file[3..(file.len() - 5)]; // chop off lib/.rlib
64         debug!("reading {}", file);
65         for i in iter::count(0u, 1) {
66             let bc_encoded = time(sess.time_passes(),
67                                   format!("check for {}.{}.bytecode.deflate", name, i).as_slice(),
68                                   (),
69                                   |_| {
70                                       archive.read(&format!("{}.{}.bytecode.deflate",
71                                                            file, i)[])
72                                   });
73             let bc_encoded = match bc_encoded {
74                 Some(data) => data,
75                 None => {
76                     if i == 0 {
77                         // No bitcode was found at all.
78                         sess.fatal(&format!("missing compressed bytecode in {}",
79                                            path.display())[]);
80                     }
81                     // No more bitcode files to read.
82                     break;
83                 },
84             };
85
86             let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
87                 time(sess.time_passes(), format!("decode {}.{}.bc", file, i).as_slice(), (), |_| {
88                     // Read the version
89                     let version = extract_bytecode_format_version(bc_encoded);
90
91                     if version == 1 {
92                         // The only version existing so far
93                         let data_size = extract_compressed_bytecode_size_v1(bc_encoded);
94                         let compressed_data = &bc_encoded[
95                             link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET..
96                             (link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as uint)];
97
98                         match flate::inflate_bytes(compressed_data) {
99                             Some(inflated) => inflated,
100                             None => {
101                                 sess.fatal(&format!("failed to decompress bc of `{}`",
102                                                    name)[])
103                             }
104                         }
105                     } else {
106                         sess.fatal(&format!("Unsupported bytecode format version {}",
107                                            version)[])
108                     }
109                 })
110             } else {
111                 time(sess.time_passes(), format!("decode {}.{}.bc", file, i).as_slice(), (), |_| {
112                 // the object must be in the old, pre-versioning format, so simply
113                 // inflate everything and let LLVM decide if it can make sense of it
114                     match flate::inflate_bytes(bc_encoded) {
115                         Some(bc) => bc,
116                         None => {
117                             sess.fatal(&format!("failed to decompress bc of `{}`",
118                                                name)[])
119                         }
120                     }
121                 })
122             };
123
124             let ptr = bc_decoded.as_slice().as_ptr();
125             debug!("linking {}, part {}", name, i);
126             time(sess.time_passes(),
127                  &format!("ll link {}.{}", name, i)[],
128                  (),
129                  |()| unsafe {
130                 if !llvm::LLVMRustLinkInExternalBitcode(llmod,
131                                                         ptr as *const libc::c_char,
132                                                         bc_decoded.len() as libc::size_t) {
133                     write::llvm_err(sess.diagnostic().handler(),
134                                     format!("failed to load bc of `{}`",
135                                             &name[]));
136                 }
137             });
138         }
139     }
140
141     // Internalize everything but the reachable symbols of the current module
142     let cstrs: Vec<CString> = reachable.iter().map(|s| {
143         CString::from_slice(s.as_bytes())
144     }).collect();
145     let arr: Vec<*const libc::c_char> = cstrs.iter().map(|c| c.as_ptr()).collect();
146     let ptr = arr.as_ptr();
147     unsafe {
148         llvm::LLVMRustRunRestrictionPass(llmod,
149                                          ptr as *const *const libc::c_char,
150                                          arr.len() as libc::size_t);
151     }
152
153     if sess.no_landing_pads() {
154         unsafe {
155             llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
156         }
157     }
158
159     // Now we have one massive module inside of llmod. Time to run the
160     // LTO-specific optimization passes that LLVM provides.
161     //
162     // This code is based off the code found in llvm's LTO code generator:
163     //      tools/lto/LTOCodeGenerator.cpp
164     debug!("running the pass manager");
165     unsafe {
166         let pm = llvm::LLVMCreatePassManager();
167         llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod);
168         llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
169
170         let opt = sess.opts.cg.opt_level.unwrap_or(0) as libc::c_uint;
171
172         let builder = llvm::LLVMPassManagerBuilderCreate();
173         llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt);
174         llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm,
175             /* Internalize = */ False,
176             /* RunInliner = */ True);
177         llvm::LLVMPassManagerBuilderDispose(builder);
178
179         llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _);
180
181         time(sess.time_passes(), "LTO passes", (), |()|
182              llvm::LLVMRunPassManager(pm, llmod));
183
184         llvm::LLVMDisposePassManager(pm);
185     }
186     debug!("lto done");
187 }
188
189 fn is_versioned_bytecode_format(bc: &[u8]) -> bool {
190     let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len();
191     return bc.len() > magic_id_byte_count &&
192            &bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC;
193 }
194
195 fn extract_bytecode_format_version(bc: &[u8]) -> u32 {
196     return read_from_le_bytes::<u32>(bc, link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET);
197 }
198
199 fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 {
200     return read_from_le_bytes::<u64>(bc, link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET);
201 }
202
203 fn read_from_le_bytes<T: Int>(bytes: &[u8], position_in_bytes: uint) -> T {
204     let byte_data = &bytes[position_in_bytes..(position_in_bytes + mem::size_of::<T>())];
205     let data = unsafe {
206         *(byte_data.as_ptr() as *const T)
207     };
208
209     Int::from_le(data)
210 }
211