]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/lto.rs
Remove the in-tree `flate` crate
[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 back::link;
12 use back::write;
13 use back::symbol_export::{self, ExportedSymbols};
14 use rustc::session::{self, config};
15 use llvm;
16 use llvm::archive_ro::ArchiveRO;
17 use llvm::{ModuleRef, TargetMachineRef, True, False};
18 use rustc::util::common::time;
19 use rustc::util::common::path2cstr;
20 use rustc::hir::def_id::LOCAL_CRATE;
21 use back::write::{ModuleConfig, with_llvm_pmb};
22
23 use libc;
24 use flate2::read::ZlibDecoder;
25
26 use std::io::Read;
27 use std::ffi::CString;
28 use std::path::Path;
29
30 pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
31     match crate_type {
32         config::CrateTypeExecutable |
33         config::CrateTypeStaticlib  |
34         config::CrateTypeCdylib     => true,
35
36         config::CrateTypeDylib     |
37         config::CrateTypeRlib      |
38         config::CrateTypeProcMacro => false,
39     }
40 }
41
42 pub fn run(sess: &session::Session,
43            llmod: ModuleRef,
44            tm: TargetMachineRef,
45            exported_symbols: &ExportedSymbols,
46            config: &ModuleConfig,
47            temp_no_opt_bc_filename: &Path) {
48     if sess.opts.cg.prefer_dynamic {
49         sess.struct_err("cannot prefer dynamic linking when performing LTO")
50             .note("only 'staticlib', 'bin', and 'cdylib' outputs are \
51                    supported with LTO")
52             .emit();
53         sess.abort_if_errors();
54     }
55
56     // Make sure we actually can run LTO
57     for crate_type in sess.crate_types.borrow().iter() {
58         if !crate_type_allows_lto(*crate_type) {
59             sess.fatal("lto can only be run for executables, cdylibs and \
60                             static library outputs");
61         }
62     }
63
64     let export_threshold =
65         symbol_export::crates_export_threshold(&sess.crate_types.borrow());
66
67     let symbol_filter = &|&(ref name, level): &(String, _)| {
68         if symbol_export::is_below_threshold(level, export_threshold) {
69             let mut bytes = Vec::with_capacity(name.len() + 1);
70             bytes.extend(name.bytes());
71             Some(CString::new(bytes).unwrap())
72         } else {
73             None
74         }
75     };
76
77     let mut symbol_white_list: Vec<CString> = exported_symbols
78         .exported_symbols(LOCAL_CRATE)
79         .iter()
80         .filter_map(symbol_filter)
81         .collect();
82
83     // For each of our upstream dependencies, find the corresponding rlib and
84     // load the bitcode from the archive. Then merge it into the current LLVM
85     // module that we've got.
86     link::each_linked_rlib(sess, &mut |cnum, path| {
87         // `#![no_builtins]` crates don't participate in LTO.
88         if sess.cstore.is_no_builtins(cnum) {
89             return;
90         }
91
92         symbol_white_list.extend(
93             exported_symbols.exported_symbols(cnum)
94                             .iter()
95                             .filter_map(symbol_filter));
96
97         let archive = ArchiveRO::open(&path).expect("wanted an rlib");
98         let bytecodes = archive.iter().filter_map(|child| {
99             child.ok().and_then(|c| c.name().map(|name| (name, c)))
100         }).filter(|&(name, _)| name.ends_with("bytecode.deflate"));
101         for (name, data) in bytecodes {
102             let bc_encoded = data.data();
103
104             let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
105                 time(sess.time_passes(), &format!("decode {}", name), || {
106                     // Read the version
107                     let version = extract_bytecode_format_version(bc_encoded);
108
109                     if version == 1 {
110                         // The only version existing so far
111                         let data_size = extract_compressed_bytecode_size_v1(bc_encoded);
112                         let compressed_data = &bc_encoded[
113                             link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET..
114                             (link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as usize)];
115
116                         let mut inflated = Vec::new();
117                         let res = ZlibDecoder::new(compressed_data)
118                             .read_to_end(&mut inflated);
119                         if res.is_err() {
120                             sess.fatal(&format!("failed to decompress bc of `{}`",
121                                                name))
122                         }
123                         inflated
124                     } else {
125                         sess.fatal(&format!("Unsupported bytecode format version {}",
126                                            version))
127                     }
128                 })
129             } else {
130                 time(sess.time_passes(), &format!("decode {}", name), || {
131                     // the object must be in the old, pre-versioning format, so
132                     // simply inflate everything and let LLVM decide if it can
133                     // make sense of it
134                     let mut inflated = Vec::new();
135                     let res = ZlibDecoder::new(bc_encoded)
136                         .read_to_end(&mut inflated);
137                     if res.is_err() {
138                         sess.fatal(&format!("failed to decompress bc of `{}`",
139                                            name))
140                     }
141                     inflated
142                 })
143             };
144
145             let ptr = bc_decoded.as_ptr();
146             debug!("linking {}", name);
147             time(sess.time_passes(), &format!("ll link {}", name), || unsafe {
148                 if !llvm::LLVMRustLinkInExternalBitcode(llmod,
149                                                         ptr as *const libc::c_char,
150                                                         bc_decoded.len() as libc::size_t) {
151                     write::llvm_err(sess.diagnostic(),
152                                     format!("failed to load bc of `{}`",
153                                             name));
154                 }
155             });
156         }
157     });
158
159     // Internalize everything but the exported symbols of the current module
160     let arr: Vec<*const libc::c_char> = symbol_white_list.iter()
161                                                          .map(|c| c.as_ptr())
162                                                          .collect();
163     let ptr = arr.as_ptr();
164     unsafe {
165         llvm::LLVMRustRunRestrictionPass(llmod,
166                                          ptr as *const *const libc::c_char,
167                                          arr.len() as libc::size_t);
168     }
169
170     if sess.no_landing_pads() {
171         unsafe {
172             llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
173         }
174     }
175
176     if sess.opts.cg.save_temps {
177         let cstr = path2cstr(temp_no_opt_bc_filename);
178         unsafe {
179             llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
180         }
181     }
182
183     // Now we have one massive module inside of llmod. Time to run the
184     // LTO-specific optimization passes that LLVM provides.
185     //
186     // This code is based off the code found in llvm's LTO code generator:
187     //      tools/lto/LTOCodeGenerator.cpp
188     debug!("running the pass manager");
189     unsafe {
190         let pm = llvm::LLVMCreatePassManager();
191         llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod);
192         let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
193         assert!(!pass.is_null());
194         llvm::LLVMRustAddPass(pm, pass);
195
196         with_llvm_pmb(llmod, config, &mut |b| {
197             llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm,
198                 /* Internalize = */ False,
199                 /* RunInliner = */ True);
200         });
201
202         let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
203         assert!(!pass.is_null());
204         llvm::LLVMRustAddPass(pm, pass);
205
206         time(sess.time_passes(), "LTO passes", ||
207              llvm::LLVMRunPassManager(pm, llmod));
208
209         llvm::LLVMDisposePassManager(pm);
210     }
211     debug!("lto done");
212 }
213
214 fn is_versioned_bytecode_format(bc: &[u8]) -> bool {
215     let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len();
216     return bc.len() > magic_id_byte_count &&
217            &bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC;
218 }
219
220 fn extract_bytecode_format_version(bc: &[u8]) -> u32 {
221     let pos = link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET;
222     let byte_data = &bc[pos..pos + 4];
223     let data = unsafe { *(byte_data.as_ptr() as *const u32) };
224     u32::from_le(data)
225 }
226
227 fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 {
228     let pos = link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET;
229     let byte_data = &bc[pos..pos + 8];
230     let data = unsafe { *(byte_data.as_ptr() as *const u64) };
231     u64::from_le(data)
232 }