]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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; | |
1a4d82fc JJ |
24 | |
25 | pub fn run(sess: &session::Session, llmod: ModuleRef, | |
26 | tm: TargetMachineRef, reachable: &[String]) { | |
27 | if sess.opts.cg.prefer_dynamic { | |
28 | sess.err("cannot prefer dynamic linking when performing LTO"); | |
29 | sess.note("only 'staticlib' and 'bin' outputs are supported with LTO"); | |
30 | sess.abort_if_errors(); | |
31 | } | |
32 | ||
33 | // Make sure we actually can run LTO | |
62682a34 | 34 | for crate_type in sess.crate_types.borrow().iter() { |
1a4d82fc JJ |
35 | match *crate_type { |
36 | config::CrateTypeExecutable | config::CrateTypeStaticlib => {} | |
37 | _ => { | |
38 | sess.fatal("lto can only be run for executables and \ | |
39 | static library outputs"); | |
40 | } | |
41 | } | |
42 | } | |
43 | ||
44 | // For each of our upstream dependencies, find the corresponding rlib and | |
45 | // load the bitcode from the archive. Then merge it into the current LLVM | |
46 | // module that we've got. | |
47 | let crates = sess.cstore.get_used_crates(cstore::RequireStatic); | |
85aaf69f | 48 | for (cnum, path) in crates { |
1a4d82fc JJ |
49 | let name = sess.cstore.get_crate_data(cnum).name.clone(); |
50 | let path = match path { | |
51 | Some(p) => p, | |
52 | None => { | |
53 | sess.fatal(&format!("could not find rlib for: `{}`", | |
c34b1796 | 54 | name)); |
1a4d82fc JJ |
55 | } |
56 | }; | |
57 | ||
58 | let archive = ArchiveRO::open(&path).expect("wanted an rlib"); | |
c34b1796 | 59 | let file = path.file_name().unwrap().to_str().unwrap(); |
85aaf69f | 60 | let file = &file[3..file.len() - 5]; // chop off lib/.rlib |
1a4d82fc | 61 | debug!("reading {}", file); |
c34b1796 | 62 | for i in 0.. { |
d9579d0f AL |
63 | let filename = format!("{}.{}.bytecode.deflate", file, i); |
64 | let msg = format!("check for {}", filename); | |
65 | let bc_encoded = time(sess.time_passes(), &msg, (), |_| { | |
66 | archive.iter().find(|section| { | |
67 | section.name() == Some(&filename[..]) | |
68 | }) | |
69 | }); | |
1a4d82fc JJ |
70 | let bc_encoded = match bc_encoded { |
71 | Some(data) => data, | |
72 | None => { | |
73 | if i == 0 { | |
74 | // No bitcode was found at all. | |
75 | sess.fatal(&format!("missing compressed bytecode in {}", | |
c34b1796 | 76 | path.display())); |
1a4d82fc JJ |
77 | } |
78 | // No more bitcode files to read. | |
d9579d0f AL |
79 | break |
80 | } | |
1a4d82fc | 81 | }; |
d9579d0f | 82 | let bc_encoded = bc_encoded.data(); |
1a4d82fc JJ |
83 | |
84 | let bc_decoded = if is_versioned_bytecode_format(bc_encoded) { | |
85aaf69f | 85 | time(sess.time_passes(), &format!("decode {}.{}.bc", file, i), (), |_| { |
1a4d82fc JJ |
86 | // Read the version |
87 | let version = extract_bytecode_format_version(bc_encoded); | |
88 | ||
89 | if version == 1 { | |
90 | // The only version existing so far | |
91 | let data_size = extract_compressed_bytecode_size_v1(bc_encoded); | |
92 | let compressed_data = &bc_encoded[ | |
93 | link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET.. | |
c34b1796 | 94 | (link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as usize)]; |
1a4d82fc JJ |
95 | |
96 | match flate::inflate_bytes(compressed_data) { | |
c34b1796 AL |
97 | Ok(inflated) => inflated, |
98 | Err(_) => { | |
1a4d82fc | 99 | sess.fatal(&format!("failed to decompress bc of `{}`", |
c34b1796 | 100 | name)) |
1a4d82fc JJ |
101 | } |
102 | } | |
103 | } else { | |
104 | sess.fatal(&format!("Unsupported bytecode format version {}", | |
c34b1796 | 105 | version)) |
1a4d82fc JJ |
106 | } |
107 | }) | |
108 | } else { | |
85aaf69f | 109 | time(sess.time_passes(), &format!("decode {}.{}.bc", file, i), (), |_| { |
1a4d82fc JJ |
110 | // the object must be in the old, pre-versioning format, so simply |
111 | // inflate everything and let LLVM decide if it can make sense of it | |
112 | match flate::inflate_bytes(bc_encoded) { | |
c34b1796 AL |
113 | Ok(bc) => bc, |
114 | Err(_) => { | |
1a4d82fc | 115 | sess.fatal(&format!("failed to decompress bc of `{}`", |
c34b1796 | 116 | name)) |
1a4d82fc JJ |
117 | } |
118 | } | |
119 | }) | |
120 | }; | |
121 | ||
85aaf69f | 122 | let ptr = bc_decoded.as_ptr(); |
1a4d82fc JJ |
123 | debug!("linking {}, part {}", name, i); |
124 | time(sess.time_passes(), | |
c34b1796 | 125 | &format!("ll link {}.{}", name, i), |
1a4d82fc JJ |
126 | (), |
127 | |()| unsafe { | |
128 | if !llvm::LLVMRustLinkInExternalBitcode(llmod, | |
129 | ptr as *const libc::c_char, | |
130 | bc_decoded.len() as libc::size_t) { | |
131 | write::llvm_err(sess.diagnostic().handler(), | |
132 | format!("failed to load bc of `{}`", | |
85aaf69f | 133 | &name[..])); |
1a4d82fc JJ |
134 | } |
135 | }); | |
136 | } | |
137 | } | |
138 | ||
139 | // Internalize everything but the reachable symbols of the current module | |
140 | let cstrs: Vec<CString> = reachable.iter().map(|s| { | |
85aaf69f | 141 | CString::new(s.clone()).unwrap() |
1a4d82fc JJ |
142 | }).collect(); |
143 | let arr: Vec<*const libc::c_char> = cstrs.iter().map(|c| c.as_ptr()).collect(); | |
144 | let ptr = arr.as_ptr(); | |
145 | unsafe { | |
146 | llvm::LLVMRustRunRestrictionPass(llmod, | |
147 | ptr as *const *const libc::c_char, | |
148 | arr.len() as libc::size_t); | |
149 | } | |
150 | ||
151 | if sess.no_landing_pads() { | |
152 | unsafe { | |
153 | llvm::LLVMRustMarkAllFunctionsNounwind(llmod); | |
154 | } | |
155 | } | |
156 | ||
157 | // Now we have one massive module inside of llmod. Time to run the | |
158 | // LTO-specific optimization passes that LLVM provides. | |
159 | // | |
160 | // This code is based off the code found in llvm's LTO code generator: | |
161 | // tools/lto/LTOCodeGenerator.cpp | |
162 | debug!("running the pass manager"); | |
163 | unsafe { | |
164 | let pm = llvm::LLVMCreatePassManager(); | |
165 | llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); | |
166 | llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); | |
167 | ||
c34b1796 AL |
168 | let opt = match sess.opts.optimize { |
169 | config::No => 0, | |
170 | config::Less => 1, | |
171 | config::Default => 2, | |
172 | config::Aggressive => 3, | |
173 | }; | |
85aaf69f | 174 | |
1a4d82fc | 175 | let builder = llvm::LLVMPassManagerBuilderCreate(); |
85aaf69f | 176 | llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt); |
1a4d82fc JJ |
177 | llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm, |
178 | /* Internalize = */ False, | |
179 | /* RunInliner = */ True); | |
180 | llvm::LLVMPassManagerBuilderDispose(builder); | |
181 | ||
182 | llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); | |
183 | ||
184 | time(sess.time_passes(), "LTO passes", (), |()| | |
185 | llvm::LLVMRunPassManager(pm, llmod)); | |
186 | ||
187 | llvm::LLVMDisposePassManager(pm); | |
188 | } | |
189 | debug!("lto done"); | |
190 | } | |
191 | ||
192 | fn is_versioned_bytecode_format(bc: &[u8]) -> bool { | |
193 | let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len(); | |
194 | return bc.len() > magic_id_byte_count && | |
85aaf69f | 195 | &bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC; |
1a4d82fc JJ |
196 | } |
197 | ||
198 | fn extract_bytecode_format_version(bc: &[u8]) -> u32 { | |
9346a6ac AL |
199 | let pos = link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET; |
200 | let byte_data = &bc[pos..pos + 4]; | |
201 | let data = unsafe { *(byte_data.as_ptr() as *const u32) }; | |
202 | u32::from_le(data) | |
1a4d82fc JJ |
203 | } |
204 | ||
205 | fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 { | |
9346a6ac AL |
206 | let pos = link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET; |
207 | let byte_data = &bc[pos..pos + 8]; | |
208 | let data = unsafe { *(byte_data.as_ptr() as *const u64) }; | |
209 | u64::from_le(data) | |
1a4d82fc | 210 | } |