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