]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2013-2015 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::lto; | |
c1a9b12d | 12 | use back::link::{get_linker, remove}; |
62682a34 | 13 | use session::config::{OutputFilenames, Passes, SomePasses, AllPasses}; |
1a4d82fc | 14 | use session::Session; |
b039eaaf | 15 | use session::config::{self, OutputType}; |
1a4d82fc JJ |
16 | use llvm; |
17 | use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef, ContextRef}; | |
18 | use llvm::SMDiagnosticRef; | |
19 | use trans::{CrateTranslation, ModuleTranslation}; | |
20 | use util::common::time; | |
c34b1796 | 21 | use util::common::path2cstr; |
1a4d82fc JJ |
22 | use syntax::codemap; |
23 | use syntax::diagnostic; | |
62682a34 | 24 | use syntax::diagnostic::{Emitter, Handler, Level}; |
1a4d82fc | 25 | |
b039eaaf | 26 | use std::collections::HashMap; |
85aaf69f | 27 | use std::ffi::{CStr, CString}; |
c34b1796 | 28 | use std::fs; |
b039eaaf | 29 | use std::path::{Path, PathBuf}; |
1a4d82fc JJ |
30 | use std::ptr; |
31 | use std::str; | |
1a4d82fc JJ |
32 | use std::sync::{Arc, Mutex}; |
33 | use std::sync::mpsc::channel; | |
34 | use std::thread; | |
35 | use libc::{self, c_uint, c_int, c_void}; | |
36 | ||
1a4d82fc JJ |
37 | pub fn llvm_err(handler: &diagnostic::Handler, msg: String) -> ! { |
38 | unsafe { | |
39 | let cstr = llvm::LLVMRustGetLastError(); | |
40 | if cstr == ptr::null() { | |
92a42be0 | 41 | panic!(handler.fatal(&msg[..])); |
1a4d82fc | 42 | } else { |
85aaf69f SL |
43 | let err = CStr::from_ptr(cstr).to_bytes(); |
44 | let err = String::from_utf8_lossy(err).to_string(); | |
1a4d82fc | 45 | libc::free(cstr as *mut _); |
92a42be0 | 46 | panic!(handler.fatal(&format!("{}: {}", &msg[..], &err[..]))); |
1a4d82fc JJ |
47 | } |
48 | } | |
49 | } | |
50 | ||
51 | pub fn write_output_file( | |
52 | handler: &diagnostic::Handler, | |
53 | target: llvm::TargetMachineRef, | |
54 | pm: llvm::PassManagerRef, | |
55 | m: ModuleRef, | |
56 | output: &Path, | |
57 | file_type: llvm::FileType) { | |
58 | unsafe { | |
c34b1796 | 59 | let output_c = path2cstr(output); |
1a4d82fc | 60 | let result = llvm::LLVMRustWriteOutputFile( |
85aaf69f | 61 | target, pm, m, output_c.as_ptr(), file_type); |
1a4d82fc | 62 | if !result { |
85aaf69f | 63 | llvm_err(handler, format!("could not write output to {}", output.display())); |
1a4d82fc JJ |
64 | } |
65 | } | |
66 | } | |
67 | ||
68 | ||
69 | struct Diagnostic { | |
70 | msg: String, | |
71 | code: Option<String>, | |
72 | lvl: Level, | |
73 | } | |
74 | ||
75 | // We use an Arc instead of just returning a list of diagnostics from the | |
bd371182 AL |
76 | // child thread because we need to make sure that the messages are seen even |
77 | // if the child thread panics (for example, when `fatal` is called). | |
1a4d82fc JJ |
78 | #[derive(Clone)] |
79 | struct SharedEmitter { | |
80 | buffer: Arc<Mutex<Vec<Diagnostic>>>, | |
81 | } | |
82 | ||
83 | impl SharedEmitter { | |
84 | fn new() -> SharedEmitter { | |
85 | SharedEmitter { | |
86 | buffer: Arc::new(Mutex::new(Vec::new())), | |
87 | } | |
88 | } | |
89 | ||
90 | fn dump(&mut self, handler: &Handler) { | |
91 | let mut buffer = self.buffer.lock().unwrap(); | |
85aaf69f | 92 | for diag in &*buffer { |
1a4d82fc JJ |
93 | match diag.code { |
94 | Some(ref code) => { | |
95 | handler.emit_with_code(None, | |
c34b1796 | 96 | &diag.msg, |
85aaf69f | 97 | &code[..], |
1a4d82fc JJ |
98 | diag.lvl); |
99 | }, | |
100 | None => { | |
101 | handler.emit(None, | |
c34b1796 | 102 | &diag.msg, |
1a4d82fc JJ |
103 | diag.lvl); |
104 | }, | |
105 | } | |
106 | } | |
107 | buffer.clear(); | |
108 | } | |
109 | } | |
110 | ||
111 | impl Emitter for SharedEmitter { | |
112 | fn emit(&mut self, cmsp: Option<(&codemap::CodeMap, codemap::Span)>, | |
113 | msg: &str, code: Option<&str>, lvl: Level) { | |
114 | assert!(cmsp.is_none(), "SharedEmitter doesn't support spans"); | |
115 | ||
116 | self.buffer.lock().unwrap().push(Diagnostic { | |
117 | msg: msg.to_string(), | |
118 | code: code.map(|s| s.to_string()), | |
119 | lvl: lvl, | |
120 | }); | |
121 | } | |
122 | ||
123 | fn custom_emit(&mut self, _cm: &codemap::CodeMap, | |
124 | _sp: diagnostic::RenderSpan, _msg: &str, _lvl: Level) { | |
125 | panic!("SharedEmitter doesn't support custom_emit"); | |
126 | } | |
127 | } | |
128 | ||
129 | ||
130 | // On android, we by default compile for armv7 processors. This enables | |
131 | // things like double word CAS instructions (rather than emulating them) | |
132 | // which are *far* more efficient. This is obviously undesirable in some | |
133 | // cases, so if any sort of target feature is specified we don't append v7 | |
134 | // to the feature list. | |
135 | // | |
136 | // On iOS only armv7 and newer are supported. So it is useful to | |
137 | // get all hardware potential via VFP3 (hardware floating point) | |
138 | // and NEON (SIMD) instructions supported by LLVM. | |
139 | // Note that without those flags various linking errors might | |
140 | // arise as some of intrinsics are converted into function calls | |
141 | // and nobody provides implementations those functions | |
142 | fn target_feature(sess: &Session) -> String { | |
143 | format!("{},{}", sess.target.target.options.features, sess.opts.cg.target_feature) | |
144 | } | |
145 | ||
146 | fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { | |
147 | match optimize { | |
148 | config::No => llvm::CodeGenLevelNone, | |
149 | config::Less => llvm::CodeGenLevelLess, | |
150 | config::Default => llvm::CodeGenLevelDefault, | |
151 | config::Aggressive => llvm::CodeGenLevelAggressive, | |
152 | } | |
153 | } | |
154 | ||
c1a9b12d | 155 | pub fn create_target_machine(sess: &Session) -> TargetMachineRef { |
1a4d82fc | 156 | let reloc_model_arg = match sess.opts.cg.relocation_model { |
85aaf69f | 157 | Some(ref s) => &s[..], |
c34b1796 | 158 | None => &sess.target.target.options.relocation_model[..], |
1a4d82fc JJ |
159 | }; |
160 | let reloc_model = match reloc_model_arg { | |
161 | "pic" => llvm::RelocPIC, | |
162 | "static" => llvm::RelocStatic, | |
163 | "default" => llvm::RelocDefault, | |
164 | "dynamic-no-pic" => llvm::RelocDynamicNoPic, | |
165 | _ => { | |
166 | sess.err(&format!("{:?} is not a valid relocation mode", | |
167 | sess.opts | |
168 | .cg | |
c34b1796 | 169 | .relocation_model)); |
1a4d82fc JJ |
170 | sess.abort_if_errors(); |
171 | unreachable!(); | |
172 | } | |
173 | }; | |
174 | ||
175 | let opt_level = get_llvm_opt_level(sess.opts.optimize); | |
176 | let use_softfp = sess.opts.cg.soft_float; | |
177 | ||
1a4d82fc JJ |
178 | let any_library = sess.crate_types.borrow().iter().any(|ty| { |
179 | *ty != config::CrateTypeExecutable | |
180 | }); | |
181 | ||
182 | let ffunction_sections = sess.target.target.options.function_sections; | |
183 | let fdata_sections = ffunction_sections; | |
184 | ||
185 | let code_model_arg = match sess.opts.cg.code_model { | |
85aaf69f | 186 | Some(ref s) => &s[..], |
c34b1796 | 187 | None => &sess.target.target.options.code_model[..], |
1a4d82fc JJ |
188 | }; |
189 | ||
190 | let code_model = match code_model_arg { | |
191 | "default" => llvm::CodeModelDefault, | |
192 | "small" => llvm::CodeModelSmall, | |
193 | "kernel" => llvm::CodeModelKernel, | |
194 | "medium" => llvm::CodeModelMedium, | |
195 | "large" => llvm::CodeModelLarge, | |
196 | _ => { | |
197 | sess.err(&format!("{:?} is not a valid code model", | |
198 | sess.opts | |
199 | .cg | |
c34b1796 | 200 | .code_model)); |
1a4d82fc JJ |
201 | sess.abort_if_errors(); |
202 | unreachable!(); | |
203 | } | |
204 | }; | |
205 | ||
c34b1796 | 206 | let triple = &sess.target.target.llvm_target; |
1a4d82fc JJ |
207 | |
208 | let tm = unsafe { | |
85aaf69f | 209 | let triple = CString::new(triple.as_bytes()).unwrap(); |
1a4d82fc | 210 | let cpu = match sess.opts.cg.target_cpu { |
85aaf69f SL |
211 | Some(ref s) => &**s, |
212 | None => &*sess.target.target.options.cpu | |
1a4d82fc | 213 | }; |
85aaf69f SL |
214 | let cpu = CString::new(cpu.as_bytes()).unwrap(); |
215 | let features = CString::new(target_feature(sess).as_bytes()).unwrap(); | |
1a4d82fc JJ |
216 | llvm::LLVMRustCreateTargetMachine( |
217 | triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), | |
218 | code_model, | |
219 | reloc_model, | |
220 | opt_level, | |
1a4d82fc | 221 | use_softfp, |
1a4d82fc JJ |
222 | !any_library && reloc_model == llvm::RelocPIC, |
223 | ffunction_sections, | |
224 | fdata_sections, | |
225 | ) | |
226 | }; | |
227 | ||
228 | if tm.is_null() { | |
229 | llvm_err(sess.diagnostic().handler(), | |
230 | format!("Could not create LLVM TargetMachine for triple: {}", | |
231 | triple).to_string()); | |
232 | } else { | |
233 | return tm; | |
234 | }; | |
235 | } | |
236 | ||
237 | ||
238 | /// Module-specific configuration for `optimize_and_codegen`. | |
239 | #[derive(Clone)] | |
c1a9b12d | 240 | pub struct ModuleConfig { |
1a4d82fc JJ |
241 | /// LLVM TargetMachine to use for codegen. |
242 | tm: TargetMachineRef, | |
243 | /// Names of additional optimization passes to run. | |
244 | passes: Vec<String>, | |
245 | /// Some(level) to optimize at a certain level, or None to run | |
246 | /// absolutely no optimizations (used for the metadata module). | |
247 | opt_level: Option<llvm::CodeGenOptLevel>, | |
248 | ||
249 | // Flags indicating which outputs to produce. | |
250 | emit_no_opt_bc: bool, | |
251 | emit_bc: bool, | |
252 | emit_lto_bc: bool, | |
253 | emit_ir: bool, | |
254 | emit_asm: bool, | |
255 | emit_obj: bool, | |
256 | ||
257 | // Miscellaneous flags. These are mostly copied from command-line | |
258 | // options. | |
259 | no_verify: bool, | |
260 | no_prepopulate_passes: bool, | |
261 | no_builtins: bool, | |
262 | time_passes: bool, | |
62682a34 SL |
263 | vectorize_loop: bool, |
264 | vectorize_slp: bool, | |
265 | merge_functions: bool, | |
92a42be0 | 266 | inline_threshold: Option<usize> |
1a4d82fc JJ |
267 | } |
268 | ||
269 | unsafe impl Send for ModuleConfig { } | |
270 | ||
271 | impl ModuleConfig { | |
272 | fn new(tm: TargetMachineRef, passes: Vec<String>) -> ModuleConfig { | |
273 | ModuleConfig { | |
274 | tm: tm, | |
275 | passes: passes, | |
276 | opt_level: None, | |
277 | ||
278 | emit_no_opt_bc: false, | |
279 | emit_bc: false, | |
280 | emit_lto_bc: false, | |
281 | emit_ir: false, | |
282 | emit_asm: false, | |
283 | emit_obj: false, | |
284 | ||
285 | no_verify: false, | |
286 | no_prepopulate_passes: false, | |
287 | no_builtins: false, | |
288 | time_passes: false, | |
62682a34 SL |
289 | vectorize_loop: false, |
290 | vectorize_slp: false, | |
291 | merge_functions: false, | |
92a42be0 | 292 | inline_threshold: None |
1a4d82fc JJ |
293 | } |
294 | } | |
295 | ||
296 | fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { | |
297 | self.no_verify = sess.no_verify(); | |
298 | self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; | |
299 | self.no_builtins = trans.no_builtins; | |
300 | self.time_passes = sess.time_passes(); | |
92a42be0 | 301 | self.inline_threshold = sess.opts.cg.inline_threshold; |
62682a34 SL |
302 | |
303 | // Copy what clang does by turning on loop vectorization at O2 and | |
304 | // slp vectorization at O3. Otherwise configure other optimization aspects | |
305 | // of this pass manager builder. | |
306 | self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && | |
307 | (sess.opts.optimize == config::Default || | |
308 | sess.opts.optimize == config::Aggressive); | |
309 | self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && | |
310 | sess.opts.optimize == config::Aggressive; | |
311 | ||
312 | self.merge_functions = sess.opts.optimize == config::Default || | |
313 | sess.opts.optimize == config::Aggressive; | |
1a4d82fc JJ |
314 | } |
315 | } | |
316 | ||
317 | /// Additional resources used by optimize_and_codegen (not module specific) | |
318 | struct CodegenContext<'a> { | |
319 | // Extra resources used for LTO: (sess, reachable). This will be `None` | |
320 | // when running in a worker thread. | |
321 | lto_ctxt: Option<(&'a Session, &'a [String])>, | |
322 | // Handler to use for diagnostics produced during codegen. | |
323 | handler: &'a Handler, | |
9346a6ac AL |
324 | // LLVM passes added by plugins. |
325 | plugin_passes: Vec<String>, | |
1a4d82fc JJ |
326 | // LLVM optimizations for which we want to print remarks. |
327 | remark: Passes, | |
b039eaaf SL |
328 | // Worker thread number |
329 | worker: usize, | |
1a4d82fc JJ |
330 | } |
331 | ||
332 | impl<'a> CodegenContext<'a> { | |
333 | fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> { | |
334 | CodegenContext { | |
335 | lto_ctxt: Some((sess, reachable)), | |
336 | handler: sess.diagnostic().handler(), | |
9346a6ac | 337 | plugin_passes: sess.plugin_llvm_passes.borrow().clone(), |
1a4d82fc | 338 | remark: sess.opts.cg.remark.clone(), |
b039eaaf | 339 | worker: 0, |
1a4d82fc JJ |
340 | } |
341 | } | |
342 | } | |
343 | ||
344 | struct HandlerFreeVars<'a> { | |
345 | llcx: ContextRef, | |
346 | cgcx: &'a CodegenContext<'a>, | |
347 | } | |
348 | ||
85aaf69f | 349 | unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<'a>, |
62682a34 SL |
350 | msg: &'b str, |
351 | cookie: c_uint) { | |
1a4d82fc JJ |
352 | use syntax::codemap::ExpnId; |
353 | ||
1a4d82fc JJ |
354 | match cgcx.lto_ctxt { |
355 | Some((sess, _)) => { | |
d9579d0f | 356 | sess.codemap().with_expn_info(ExpnId::from_u32(cookie), |info| match info { |
85aaf69f SL |
357 | Some(ei) => sess.span_err(ei.call_site, msg), |
358 | None => sess.err(msg), | |
1a4d82fc JJ |
359 | }); |
360 | } | |
361 | ||
362 | None => { | |
85aaf69f | 363 | cgcx.handler.err(msg); |
1a4d82fc JJ |
364 | cgcx.handler.note("build without -C codegen-units for more exact errors"); |
365 | } | |
366 | } | |
367 | } | |
368 | ||
85aaf69f SL |
369 | unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, |
370 | user: *const c_void, | |
371 | cookie: c_uint) { | |
e9174d1e | 372 | let HandlerFreeVars { cgcx, .. } = *(user as *const HandlerFreeVars); |
85aaf69f SL |
373 | |
374 | let msg = llvm::build_string(|s| llvm::LLVMWriteSMDiagnosticToString(diag, s)) | |
375 | .expect("non-UTF8 SMDiagnostic"); | |
376 | ||
377 | report_inline_asm(cgcx, &msg[..], cookie); | |
378 | } | |
379 | ||
1a4d82fc | 380 | unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { |
e9174d1e | 381 | let HandlerFreeVars { llcx, cgcx } = *(user as *const HandlerFreeVars); |
1a4d82fc JJ |
382 | |
383 | match llvm::diagnostic::Diagnostic::unpack(info) { | |
85aaf69f SL |
384 | llvm::diagnostic::InlineAsm(inline) => { |
385 | report_inline_asm(cgcx, | |
386 | &*llvm::twine_to_string(inline.message), | |
387 | inline.cookie); | |
388 | } | |
389 | ||
1a4d82fc | 390 | llvm::diagnostic::Optimization(opt) => { |
85aaf69f | 391 | let pass_name = str::from_utf8(CStr::from_ptr(opt.pass_name).to_bytes()) |
1a4d82fc JJ |
392 | .ok() |
393 | .expect("got a non-UTF8 pass name from LLVM"); | |
394 | let enabled = match cgcx.remark { | |
395 | AllPasses => true, | |
396 | SomePasses(ref v) => v.iter().any(|s| *s == pass_name), | |
397 | }; | |
398 | ||
399 | if enabled { | |
400 | let loc = llvm::debug_loc_to_string(llcx, opt.debug_loc); | |
85aaf69f SL |
401 | cgcx.handler.note(&format!("optimization {} for {} at {}: {}", |
402 | opt.kind.describe(), | |
403 | pass_name, | |
404 | if loc.is_empty() { "[unknown]" } else { &*loc }, | |
405 | llvm::twine_to_string(opt.message))); | |
1a4d82fc JJ |
406 | } |
407 | } | |
408 | ||
409 | _ => (), | |
410 | } | |
411 | } | |
412 | ||
413 | // Unsafe due to LLVM calls. | |
414 | unsafe fn optimize_and_codegen(cgcx: &CodegenContext, | |
415 | mtrans: ModuleTranslation, | |
416 | config: ModuleConfig, | |
417 | name_extra: String, | |
418 | output_names: OutputFilenames) { | |
419 | let ModuleTranslation { llmod, llcx } = mtrans; | |
420 | let tm = config.tm; | |
421 | ||
422 | // llcx doesn't outlive this function, so we can put this on the stack. | |
423 | let fv = HandlerFreeVars { | |
424 | llcx: llcx, | |
425 | cgcx: cgcx, | |
426 | }; | |
427 | let fv = &fv as *const HandlerFreeVars as *mut c_void; | |
428 | ||
429 | llvm::LLVMSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, fv); | |
85aaf69f | 430 | llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, fv); |
1a4d82fc JJ |
431 | |
432 | if config.emit_no_opt_bc { | |
433 | let ext = format!("{}.no-opt.bc", name_extra); | |
85aaf69f | 434 | let out = output_names.with_extension(&ext); |
c34b1796 | 435 | let out = path2cstr(&out); |
1a4d82fc JJ |
436 | llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); |
437 | } | |
438 | ||
c1a9b12d SL |
439 | if config.opt_level.is_some() { |
440 | // Create the two optimizing pass managers. These mirror what clang | |
441 | // does, and are by populated by LLVM's default PassManagerBuilder. | |
442 | // Each manager has a different set of passes, but they also share | |
443 | // some common passes. | |
444 | let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); | |
445 | let mpm = llvm::LLVMCreatePassManager(); | |
446 | ||
447 | // If we're verifying or linting, add them to the function pass | |
448 | // manager. | |
449 | let addpass = |pass: &str| { | |
450 | let pass = CString::new(pass).unwrap(); | |
451 | llvm::LLVMRustAddPass(fpm, pass.as_ptr()) | |
452 | }; | |
1a4d82fc | 453 | |
c1a9b12d SL |
454 | if !config.no_verify { assert!(addpass("verify")); } |
455 | if !config.no_prepopulate_passes { | |
456 | llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); | |
457 | llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); | |
458 | with_llvm_pmb(llmod, &config, &mut |b| { | |
459 | llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); | |
460 | llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); | |
461 | }) | |
462 | } | |
1a4d82fc | 463 | |
c1a9b12d SL |
464 | for pass in &config.passes { |
465 | if !addpass(pass) { | |
466 | cgcx.handler.warn(&format!("unknown pass `{}`, ignoring", | |
467 | pass)); | |
1a4d82fc | 468 | } |
c1a9b12d | 469 | } |
1a4d82fc | 470 | |
c1a9b12d SL |
471 | for pass in &cgcx.plugin_passes { |
472 | if !addpass(pass) { | |
473 | cgcx.handler.err(&format!("a plugin asked for LLVM pass \ | |
474 | `{}` but LLVM does not \ | |
475 | recognize it", pass)); | |
9346a6ac | 476 | } |
c1a9b12d | 477 | } |
9346a6ac | 478 | |
c1a9b12d | 479 | cgcx.handler.abort_if_errors(); |
9346a6ac | 480 | |
c1a9b12d | 481 | // Finally, run the actual optimization passes |
b039eaaf | 482 | time(config.time_passes, &format!("llvm function passes [{}]", cgcx.worker), || |
c1a9b12d | 483 | llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); |
b039eaaf | 484 | time(config.time_passes, &format!("llvm module passes [{}]", cgcx.worker), || |
c1a9b12d | 485 | llvm::LLVMRunPassManager(mpm, llmod)); |
1a4d82fc | 486 | |
c1a9b12d SL |
487 | // Deallocate managers that we're now done with |
488 | llvm::LLVMDisposePassManager(fpm); | |
489 | llvm::LLVMDisposePassManager(mpm); | |
1a4d82fc | 490 | |
c1a9b12d SL |
491 | match cgcx.lto_ctxt { |
492 | Some((sess, reachable)) if sess.lto() => { | |
e9174d1e | 493 | time(sess.time_passes(), "all lto passes", || |
b039eaaf SL |
494 | lto::run(sess, llmod, tm, reachable, &config, |
495 | &name_extra, &output_names)); | |
1a4d82fc | 496 | |
c1a9b12d SL |
497 | if config.emit_lto_bc { |
498 | let name = format!("{}.lto.bc", name_extra); | |
499 | let out = output_names.with_extension(&name); | |
500 | let out = path2cstr(&out); | |
501 | llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); | |
502 | } | |
503 | }, | |
504 | _ => {}, | |
505 | } | |
1a4d82fc JJ |
506 | } |
507 | ||
508 | // A codegen-specific pass manager is used to generate object | |
509 | // files for an LLVM module. | |
510 | // | |
511 | // Apparently each of these pass managers is a one-shot kind of | |
512 | // thing, so we create a new one for each type of output. The | |
513 | // pass manager passed to the closure should be ensured to not | |
514 | // escape the closure itself, and the manager should only be | |
515 | // used once. | |
516 | unsafe fn with_codegen<F>(tm: TargetMachineRef, | |
517 | llmod: ModuleRef, | |
518 | no_builtins: bool, | |
519 | f: F) where | |
520 | F: FnOnce(PassManagerRef), | |
521 | { | |
522 | let cpm = llvm::LLVMCreatePassManager(); | |
523 | llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); | |
524 | llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); | |
525 | f(cpm); | |
1a4d82fc JJ |
526 | } |
527 | ||
528 | if config.emit_bc { | |
529 | let ext = format!("{}.bc", name_extra); | |
85aaf69f | 530 | let out = output_names.with_extension(&ext); |
c34b1796 | 531 | let out = path2cstr(&out); |
1a4d82fc JJ |
532 | llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); |
533 | } | |
534 | ||
b039eaaf | 535 | time(config.time_passes, &format!("codegen passes [{}]", cgcx.worker), || { |
1a4d82fc JJ |
536 | if config.emit_ir { |
537 | let ext = format!("{}.ll", name_extra); | |
85aaf69f | 538 | let out = output_names.with_extension(&ext); |
c34b1796 | 539 | let out = path2cstr(&out); |
1a4d82fc JJ |
540 | with_codegen(tm, llmod, config.no_builtins, |cpm| { |
541 | llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr()); | |
62682a34 | 542 | llvm::LLVMDisposePassManager(cpm); |
1a4d82fc JJ |
543 | }) |
544 | } | |
545 | ||
546 | if config.emit_asm { | |
c34b1796 | 547 | let path = output_names.with_extension(&format!("{}.s", name_extra)); |
1a4d82fc | 548 | with_codegen(tm, llmod, config.no_builtins, |cpm| { |
62682a34 SL |
549 | write_output_file(cgcx.handler, tm, cpm, llmod, &path, |
550 | llvm::AssemblyFileType); | |
1a4d82fc JJ |
551 | }); |
552 | } | |
553 | ||
554 | if config.emit_obj { | |
c34b1796 | 555 | let path = output_names.with_extension(&format!("{}.o", name_extra)); |
1a4d82fc JJ |
556 | with_codegen(tm, llmod, config.no_builtins, |cpm| { |
557 | write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType); | |
558 | }); | |
559 | } | |
560 | }); | |
561 | ||
562 | llvm::LLVMDisposeModule(llmod); | |
563 | llvm::LLVMContextDispose(llcx); | |
564 | llvm::LLVMRustDisposeTargetMachine(tm); | |
565 | } | |
566 | ||
567 | pub fn run_passes(sess: &Session, | |
568 | trans: &CrateTranslation, | |
b039eaaf | 569 | output_types: &HashMap<OutputType, Option<PathBuf>>, |
1a4d82fc JJ |
570 | crate_output: &OutputFilenames) { |
571 | // It's possible that we have `codegen_units > 1` but only one item in | |
572 | // `trans.modules`. We could theoretically proceed and do LTO in that | |
573 | // case, but it would be confusing to have the validity of | |
574 | // `-Z lto -C codegen-units=2` depend on details of the crate being | |
575 | // compiled, so we complain regardless. | |
576 | if sess.lto() && sess.opts.cg.codegen_units > 1 { | |
577 | // This case is impossible to handle because LTO expects to be able | |
578 | // to combine the entire crate and all its dependencies into a | |
579 | // single compilation unit, but each codegen unit is in a separate | |
580 | // LLVM context, so they can't easily be combined. | |
581 | sess.fatal("can't perform LTO when using multiple codegen units"); | |
582 | } | |
583 | ||
584 | // Sanity check | |
585 | assert!(trans.modules.len() == sess.opts.cg.codegen_units); | |
586 | ||
1a4d82fc JJ |
587 | let tm = create_target_machine(sess); |
588 | ||
589 | // Figure out what we actually need to build. | |
590 | ||
591 | let mut modules_config = ModuleConfig::new(tm, sess.opts.cg.passes.clone()); | |
592 | let mut metadata_config = ModuleConfig::new(tm, vec!()); | |
593 | ||
594 | modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); | |
595 | ||
596 | // Save all versions of the bytecode if we're saving our temporaries. | |
597 | if sess.opts.cg.save_temps { | |
598 | modules_config.emit_no_opt_bc = true; | |
599 | modules_config.emit_bc = true; | |
600 | modules_config.emit_lto_bc = true; | |
601 | metadata_config.emit_bc = true; | |
602 | } | |
603 | ||
604 | // Emit bitcode files for the crate if we're emitting an rlib. | |
605 | // Whenever an rlib is created, the bitcode is inserted into the | |
606 | // archive in order to allow LTO against it. | |
607 | let needs_crate_bitcode = | |
608 | sess.crate_types.borrow().contains(&config::CrateTypeRlib) && | |
b039eaaf | 609 | sess.opts.output_types.contains_key(&OutputType::Exe); |
c1a9b12d | 610 | let needs_crate_object = |
b039eaaf | 611 | sess.opts.output_types.contains_key(&OutputType::Exe); |
1a4d82fc JJ |
612 | if needs_crate_bitcode { |
613 | modules_config.emit_bc = true; | |
614 | } | |
615 | ||
b039eaaf | 616 | for output_type in output_types.keys() { |
1a4d82fc | 617 | match *output_type { |
b039eaaf SL |
618 | OutputType::Bitcode => { modules_config.emit_bc = true; }, |
619 | OutputType::LlvmAssembly => { modules_config.emit_ir = true; }, | |
620 | OutputType::Assembly => { | |
1a4d82fc JJ |
621 | modules_config.emit_asm = true; |
622 | // If we're not using the LLVM assembler, this function | |
623 | // could be invoked specially with output_type_assembly, so | |
624 | // in this case we still want the metadata object file. | |
b039eaaf | 625 | if !sess.opts.output_types.contains_key(&OutputType::Assembly) { |
1a4d82fc JJ |
626 | metadata_config.emit_obj = true; |
627 | } | |
628 | }, | |
b039eaaf SL |
629 | OutputType::Object => { modules_config.emit_obj = true; }, |
630 | OutputType::Exe => { | |
1a4d82fc JJ |
631 | modules_config.emit_obj = true; |
632 | metadata_config.emit_obj = true; | |
633 | }, | |
b039eaaf | 634 | OutputType::DepInfo => {} |
1a4d82fc JJ |
635 | } |
636 | } | |
637 | ||
638 | modules_config.set_flags(sess, trans); | |
639 | metadata_config.set_flags(sess, trans); | |
640 | ||
641 | ||
bd371182 | 642 | // Populate a buffer with a list of codegen threads. Items are processed in |
1a4d82fc JJ |
643 | // LIFO order, just because it's a tiny bit simpler that way. (The order |
644 | // doesn't actually matter.) | |
645 | let mut work_items = Vec::with_capacity(1 + trans.modules.len()); | |
646 | ||
647 | { | |
648 | let work = build_work_item(sess, | |
649 | trans.metadata_module, | |
650 | metadata_config.clone(), | |
651 | crate_output.clone(), | |
652 | "metadata".to_string()); | |
653 | work_items.push(work); | |
654 | } | |
655 | ||
656 | for (index, mtrans) in trans.modules.iter().enumerate() { | |
657 | let work = build_work_item(sess, | |
658 | *mtrans, | |
659 | modules_config.clone(), | |
660 | crate_output.clone(), | |
661 | format!("{}", index)); | |
662 | work_items.push(work); | |
663 | } | |
664 | ||
665 | // Process the work items, optionally using worker threads. | |
666 | if sess.opts.cg.codegen_units == 1 { | |
c34b1796 | 667 | run_work_singlethreaded(sess, &trans.reachable, work_items); |
1a4d82fc JJ |
668 | } else { |
669 | run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units); | |
670 | } | |
671 | ||
672 | // All codegen is finished. | |
673 | unsafe { | |
674 | llvm::LLVMRustDisposeTargetMachine(tm); | |
675 | } | |
676 | ||
677 | // Produce final compile outputs. | |
85aaf69f SL |
678 | let copy_gracefully = |from: &Path, to: &Path| { |
679 | if let Err(e) = fs::copy(from, to) { | |
680 | sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); | |
681 | } | |
682 | }; | |
1a4d82fc | 683 | |
b039eaaf SL |
684 | let copy_if_one_unit = |ext: &str, |
685 | output_type: OutputType, | |
686 | keep_numbered: bool| { | |
1a4d82fc JJ |
687 | if sess.opts.cg.codegen_units == 1 { |
688 | // 1) Only one codegen unit. In this case it's no difficulty | |
689 | // to copy `foo.0.x` to `foo.x`. | |
c1a9b12d SL |
690 | copy_gracefully(&crate_output.with_extension(ext), |
691 | &crate_output.path(output_type)); | |
1a4d82fc JJ |
692 | if !sess.opts.cg.save_temps && !keep_numbered { |
693 | // The user just wants `foo.x`, not `foo.0.x`. | |
694 | remove(sess, &crate_output.with_extension(ext)); | |
695 | } | |
b039eaaf SL |
696 | } else if crate_output.outputs.contains_key(&output_type) { |
697 | // 2) Multiple codegen units, with `--emit foo=some_name`. We have | |
698 | // no good solution for this case, so warn the user. | |
699 | sess.warn(&format!("ignoring emit path because multiple .{} files \ | |
700 | were produced", ext)); | |
701 | } else if crate_output.single_output_file.is_some() { | |
702 | // 3) Multiple codegen units, with `-o some_name`. We have | |
703 | // no good solution for this case, so warn the user. | |
704 | sess.warn(&format!("ignoring -o because multiple .{} files \ | |
705 | were produced", ext)); | |
1a4d82fc | 706 | } else { |
b039eaaf SL |
707 | // 4) Multiple codegen units, but no explicit name. We |
708 | // just leave the `foo.0.x` files in place. | |
709 | // (We don't have to do any work in this case.) | |
1a4d82fc JJ |
710 | } |
711 | }; | |
712 | ||
1a4d82fc JJ |
713 | // Flag to indicate whether the user explicitly requested bitcode. |
714 | // Otherwise, we produced it only as a temporary output, and will need | |
715 | // to get rid of it. | |
716 | let mut user_wants_bitcode = false; | |
c1a9b12d | 717 | let mut user_wants_objects = false; |
b039eaaf | 718 | for output_type in output_types.keys() { |
1a4d82fc | 719 | match *output_type { |
b039eaaf | 720 | OutputType::Bitcode => { |
1a4d82fc JJ |
721 | user_wants_bitcode = true; |
722 | // Copy to .bc, but always keep the .0.bc. There is a later | |
723 | // check to figure out if we should delete .0.bc files, or keep | |
724 | // them for making an rlib. | |
b039eaaf | 725 | copy_if_one_unit("0.bc", OutputType::Bitcode, true); |
1a4d82fc | 726 | } |
b039eaaf SL |
727 | OutputType::LlvmAssembly => { |
728 | copy_if_one_unit("0.ll", OutputType::LlvmAssembly, false); | |
1a4d82fc | 729 | } |
b039eaaf SL |
730 | OutputType::Assembly => { |
731 | copy_if_one_unit("0.s", OutputType::Assembly, false); | |
1a4d82fc | 732 | } |
b039eaaf | 733 | OutputType::Object => { |
c1a9b12d | 734 | user_wants_objects = true; |
b039eaaf | 735 | copy_if_one_unit("0.o", OutputType::Object, true); |
1a4d82fc | 736 | } |
b039eaaf SL |
737 | OutputType::Exe | |
738 | OutputType::DepInfo => {} | |
1a4d82fc JJ |
739 | } |
740 | } | |
741 | let user_wants_bitcode = user_wants_bitcode; | |
742 | ||
743 | // Clean up unwanted temporary files. | |
744 | ||
745 | // We create the following files by default: | |
746 | // - crate.0.bc | |
747 | // - crate.0.o | |
748 | // - crate.metadata.bc | |
749 | // - crate.metadata.o | |
750 | // - crate.o (linked from crate.##.o) | |
751 | // - crate.bc (copied from crate.0.bc) | |
752 | // We may create additional files if requested by the user (through | |
753 | // `-C save-temps` or `--emit=` flags). | |
754 | ||
755 | if !sess.opts.cg.save_temps { | |
756 | // Remove the temporary .0.o objects. If the user didn't | |
757 | // explicitly request bitcode (with --emit=bc), and the bitcode is not | |
758 | // needed for building an rlib, then we must remove .0.bc as well. | |
759 | ||
760 | // Specific rules for keeping .0.bc: | |
761 | // - If we're building an rlib (`needs_crate_bitcode`), then keep | |
762 | // it. | |
763 | // - If the user requested bitcode (`user_wants_bitcode`), and | |
764 | // codegen_units > 1, then keep it. | |
765 | // - If the user requested bitcode but codegen_units == 1, then we | |
766 | // can toss .0.bc because we copied it to .bc earlier. | |
767 | // - If we're not building an rlib and the user didn't request | |
768 | // bitcode, then delete .0.bc. | |
769 | // If you change how this works, also update back::link::link_rlib, | |
770 | // where .0.bc files are (maybe) deleted after making an rlib. | |
771 | let keep_numbered_bitcode = needs_crate_bitcode || | |
772 | (user_wants_bitcode && sess.opts.cg.codegen_units > 1); | |
773 | ||
c1a9b12d SL |
774 | let keep_numbered_objects = needs_crate_object || |
775 | (user_wants_objects && sess.opts.cg.codegen_units > 1); | |
776 | ||
85aaf69f | 777 | for i in 0..trans.modules.len() { |
c1a9b12d | 778 | if modules_config.emit_obj && !keep_numbered_objects { |
1a4d82fc | 779 | let ext = format!("{}.o", i); |
c1a9b12d | 780 | remove(sess, &crate_output.with_extension(&ext)); |
1a4d82fc JJ |
781 | } |
782 | ||
783 | if modules_config.emit_bc && !keep_numbered_bitcode { | |
784 | let ext = format!("{}.bc", i); | |
c1a9b12d | 785 | remove(sess, &crate_output.with_extension(&ext)); |
1a4d82fc JJ |
786 | } |
787 | } | |
788 | ||
789 | if metadata_config.emit_bc && !user_wants_bitcode { | |
790 | remove(sess, &crate_output.with_extension("metadata.bc")); | |
791 | } | |
792 | } | |
793 | ||
794 | // We leave the following files around by default: | |
795 | // - crate.o | |
796 | // - crate.metadata.o | |
797 | // - crate.bc | |
798 | // These are used in linking steps and will be cleaned up afterward. | |
799 | ||
800 | // FIXME: time_llvm_passes support - does this use a global context or | |
801 | // something? | |
c34b1796 AL |
802 | if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { |
803 | unsafe { llvm::LLVMRustPrintPassTimings(); } | |
804 | } | |
1a4d82fc JJ |
805 | } |
806 | ||
807 | struct WorkItem { | |
808 | mtrans: ModuleTranslation, | |
809 | config: ModuleConfig, | |
810 | output_names: OutputFilenames, | |
811 | name_extra: String | |
812 | } | |
813 | ||
814 | fn build_work_item(sess: &Session, | |
815 | mtrans: ModuleTranslation, | |
816 | config: ModuleConfig, | |
817 | output_names: OutputFilenames, | |
818 | name_extra: String) | |
819 | -> WorkItem | |
820 | { | |
821 | let mut config = config; | |
822 | config.tm = create_target_machine(sess); | |
823 | WorkItem { mtrans: mtrans, config: config, output_names: output_names, | |
824 | name_extra: name_extra } | |
825 | } | |
826 | ||
827 | fn execute_work_item(cgcx: &CodegenContext, | |
828 | work_item: WorkItem) { | |
829 | unsafe { | |
830 | optimize_and_codegen(cgcx, work_item.mtrans, work_item.config, | |
831 | work_item.name_extra, work_item.output_names); | |
832 | } | |
833 | } | |
834 | ||
835 | fn run_work_singlethreaded(sess: &Session, | |
836 | reachable: &[String], | |
837 | work_items: Vec<WorkItem>) { | |
838 | let cgcx = CodegenContext::new_with_session(sess, reachable); | |
1a4d82fc JJ |
839 | |
840 | // Since we're running single-threaded, we can pass the session to | |
841 | // the proc, allowing `optimize_and_codegen` to perform LTO. | |
62682a34 | 842 | for work in work_items.into_iter().rev() { |
1a4d82fc JJ |
843 | execute_work_item(&cgcx, work); |
844 | } | |
845 | } | |
846 | ||
847 | fn run_work_multithreaded(sess: &Session, | |
848 | work_items: Vec<WorkItem>, | |
c34b1796 | 849 | num_workers: usize) { |
1a4d82fc JJ |
850 | // Run some workers to process the work items. |
851 | let work_items_arc = Arc::new(Mutex::new(work_items)); | |
852 | let mut diag_emitter = SharedEmitter::new(); | |
853 | let mut futures = Vec::with_capacity(num_workers); | |
854 | ||
85aaf69f | 855 | for i in 0..num_workers { |
1a4d82fc JJ |
856 | let work_items_arc = work_items_arc.clone(); |
857 | let diag_emitter = diag_emitter.clone(); | |
9346a6ac | 858 | let plugin_passes = sess.plugin_llvm_passes.borrow().clone(); |
1a4d82fc JJ |
859 | let remark = sess.opts.cg.remark.clone(); |
860 | ||
861 | let (tx, rx) = channel(); | |
862 | let mut tx = Some(tx); | |
863 | futures.push(rx); | |
864 | ||
85aaf69f | 865 | thread::Builder::new().name(format!("codegen-{}", i)).spawn(move || { |
62682a34 | 866 | let diag_handler = Handler::with_emitter(true, box diag_emitter); |
1a4d82fc JJ |
867 | |
868 | // Must construct cgcx inside the proc because it has non-Send | |
869 | // fields. | |
870 | let cgcx = CodegenContext { | |
871 | lto_ctxt: None, | |
872 | handler: &diag_handler, | |
9346a6ac | 873 | plugin_passes: plugin_passes, |
1a4d82fc | 874 | remark: remark, |
b039eaaf | 875 | worker: i, |
1a4d82fc JJ |
876 | }; |
877 | ||
878 | loop { | |
879 | // Avoid holding the lock for the entire duration of the match. | |
880 | let maybe_work = work_items_arc.lock().unwrap().pop(); | |
881 | match maybe_work { | |
882 | Some(work) => { | |
883 | execute_work_item(&cgcx, work); | |
884 | ||
885 | // Make sure to fail the worker so the main thread can | |
886 | // tell that there were errors. | |
887 | cgcx.handler.abort_if_errors(); | |
888 | } | |
889 | None => break, | |
890 | } | |
891 | } | |
892 | ||
893 | tx.take().unwrap().send(()).unwrap(); | |
85aaf69f | 894 | }).unwrap(); |
1a4d82fc JJ |
895 | } |
896 | ||
897 | let mut panicked = false; | |
85aaf69f | 898 | for rx in futures { |
1a4d82fc JJ |
899 | match rx.recv() { |
900 | Ok(()) => {}, | |
901 | Err(_) => { | |
902 | panicked = true; | |
903 | }, | |
904 | } | |
905 | // Display any new diagnostics. | |
906 | diag_emitter.dump(sess.diagnostic().handler()); | |
907 | } | |
908 | if panicked { | |
909 | sess.fatal("aborting due to worker thread panic"); | |
910 | } | |
911 | } | |
912 | ||
913 | pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { | |
c1a9b12d | 914 | let (pname, mut cmd) = get_linker(sess); |
1a4d82fc | 915 | |
b039eaaf SL |
916 | cmd.arg("-c").arg("-o").arg(&outputs.path(OutputType::Object)) |
917 | .arg(&outputs.temp_path(OutputType::Assembly)); | |
c34b1796 | 918 | debug!("{:?}", cmd); |
1a4d82fc JJ |
919 | |
920 | match cmd.output() { | |
921 | Ok(prog) => { | |
922 | if !prog.status.success() { | |
923 | sess.err(&format!("linking with `{}` failed: {}", | |
924 | pname, | |
c34b1796 AL |
925 | prog.status)); |
926 | sess.note(&format!("{:?}", &cmd)); | |
927 | let mut note = prog.stderr.clone(); | |
92a42be0 | 928 | note.extend_from_slice(&prog.stdout); |
85aaf69f | 929 | sess.note(str::from_utf8(¬e[..]).unwrap()); |
1a4d82fc JJ |
930 | sess.abort_if_errors(); |
931 | } | |
932 | }, | |
933 | Err(e) => { | |
c1a9b12d | 934 | sess.err(&format!("could not exec the linker `{}`: {}", pname, e)); |
1a4d82fc JJ |
935 | sess.abort_if_errors(); |
936 | } | |
937 | } | |
938 | } | |
939 | ||
c1a9b12d | 940 | pub unsafe fn configure_llvm(sess: &Session) { |
1a4d82fc JJ |
941 | let mut llvm_c_strs = Vec::new(); |
942 | let mut llvm_args = Vec::new(); | |
62682a34 | 943 | |
1a4d82fc | 944 | { |
85aaf69f SL |
945 | let mut add = |arg: &str| { |
946 | let s = CString::new(arg).unwrap(); | |
1a4d82fc JJ |
947 | llvm_args.push(s.as_ptr()); |
948 | llvm_c_strs.push(s); | |
949 | }; | |
950 | add("rustc"); // fake program name | |
1a4d82fc JJ |
951 | if sess.time_llvm_passes() { add("-time-passes"); } |
952 | if sess.print_llvm_passes() { add("-debug-pass=Structure"); } | |
953 | ||
85aaf69f SL |
954 | // FIXME #21627 disable faulty FastISel on AArch64 (even for -O0) |
955 | if sess.target.target.arch == "aarch64" { add("-fast-isel=0"); } | |
956 | ||
957 | for arg in &sess.opts.cg.llvm_args { | |
c34b1796 | 958 | add(&(*arg)); |
1a4d82fc JJ |
959 | } |
960 | } | |
961 | ||
c1a9b12d SL |
962 | llvm::LLVMInitializePasses(); |
963 | ||
964 | // Only initialize the platforms supported by Rust here, because | |
965 | // using --llvm-root will have multiple platforms that rustllvm | |
966 | // doesn't actually link to and it's pointless to put target info | |
967 | // into the registry that Rust cannot generate machine code for. | |
968 | llvm::LLVMInitializeX86TargetInfo(); | |
969 | llvm::LLVMInitializeX86Target(); | |
970 | llvm::LLVMInitializeX86TargetMC(); | |
971 | llvm::LLVMInitializeX86AsmPrinter(); | |
972 | llvm::LLVMInitializeX86AsmParser(); | |
973 | ||
974 | llvm::LLVMInitializeARMTargetInfo(); | |
975 | llvm::LLVMInitializeARMTarget(); | |
976 | llvm::LLVMInitializeARMTargetMC(); | |
977 | llvm::LLVMInitializeARMAsmPrinter(); | |
978 | llvm::LLVMInitializeARMAsmParser(); | |
979 | ||
980 | llvm::LLVMInitializeAArch64TargetInfo(); | |
981 | llvm::LLVMInitializeAArch64Target(); | |
982 | llvm::LLVMInitializeAArch64TargetMC(); | |
983 | llvm::LLVMInitializeAArch64AsmPrinter(); | |
984 | llvm::LLVMInitializeAArch64AsmParser(); | |
985 | ||
986 | llvm::LLVMInitializeMipsTargetInfo(); | |
987 | llvm::LLVMInitializeMipsTarget(); | |
988 | llvm::LLVMInitializeMipsTargetMC(); | |
989 | llvm::LLVMInitializeMipsAsmPrinter(); | |
990 | llvm::LLVMInitializeMipsAsmParser(); | |
991 | ||
992 | llvm::LLVMInitializePowerPCTargetInfo(); | |
993 | llvm::LLVMInitializePowerPCTarget(); | |
994 | llvm::LLVMInitializePowerPCTargetMC(); | |
995 | llvm::LLVMInitializePowerPCAsmPrinter(); | |
996 | llvm::LLVMInitializePowerPCAsmParser(); | |
997 | ||
998 | llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, | |
999 | llvm_args.as_ptr()); | |
1a4d82fc JJ |
1000 | } |
1001 | ||
c1a9b12d SL |
1002 | pub unsafe fn with_llvm_pmb(llmod: ModuleRef, |
1003 | config: &ModuleConfig, | |
1004 | f: &mut FnMut(llvm::PassManagerBuilderRef)) { | |
1a4d82fc JJ |
1005 | // Create the PassManagerBuilder for LLVM. We configure it with |
1006 | // reasonable defaults and prepare it to actually populate the pass | |
1007 | // manager. | |
1008 | let builder = llvm::LLVMPassManagerBuilderCreate(); | |
c1a9b12d | 1009 | let opt = config.opt_level.unwrap_or(llvm::CodeGenLevelNone); |
92a42be0 | 1010 | let inline_threshold = config.inline_threshold; |
62682a34 SL |
1011 | |
1012 | llvm::LLVMRustConfigurePassManagerBuilder(builder, opt, | |
1013 | config.merge_functions, | |
1014 | config.vectorize_slp, | |
1015 | config.vectorize_loop); | |
1016 | ||
1017 | llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); | |
1018 | ||
1019 | // Here we match what clang does (kinda). For O0 we only inline | |
1020 | // always-inline functions (but don't add lifetime intrinsics), at O1 we | |
1021 | // inline with lifetime intrinsics, and O2+ we add an inliner with a | |
1022 | // thresholds copied from clang. | |
92a42be0 SL |
1023 | match (opt, inline_threshold) { |
1024 | (_, Some(t)) => { | |
1025 | llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); | |
1026 | } | |
1027 | (llvm::CodeGenLevelNone, _) => { | |
1a4d82fc JJ |
1028 | llvm::LLVMRustAddAlwaysInlinePass(builder, false); |
1029 | } | |
92a42be0 | 1030 | (llvm::CodeGenLevelLess, _) => { |
1a4d82fc JJ |
1031 | llvm::LLVMRustAddAlwaysInlinePass(builder, true); |
1032 | } | |
92a42be0 | 1033 | (llvm::CodeGenLevelDefault, _) => { |
62682a34 | 1034 | llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); |
1a4d82fc | 1035 | } |
92a42be0 | 1036 | (llvm::CodeGenLevelAggressive, _) => { |
62682a34 | 1037 | llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); |
1a4d82fc JJ |
1038 | } |
1039 | } | |
1a4d82fc | 1040 | |
c1a9b12d | 1041 | f(builder); |
1a4d82fc | 1042 | llvm::LLVMPassManagerBuilderDispose(builder); |
1a4d82fc | 1043 | } |