1use std::collections::BTreeMap;
2use std::ffi::{CStr, CString};
3use std::fs::File;
4use std::path::Path;
5use std::sync::Arc;
6use std::{io, iter, slice};
7
8use object::read::archive::ArchiveFile;
9use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
10use rustc_codegen_ssa::back::symbol_export;
11use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
12use rustc_codegen_ssa::traits::*;
13use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
14use rustc_data_structures::fx::FxHashMap;
15use rustc_data_structures::memmap::Mmap;
16use rustc_errors::{DiagCtxtHandle, FatalError};
17use rustc_hir::def_id::LOCAL_CRATE;
18use rustc_middle::bug;
19use rustc_middle::dep_graph::WorkProduct;
20use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
21use rustc_session::config::{self, CrateType, Lto};
22use tracing::{debug, info};
23
24use crate::back::write::{
25 self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode,
26};
27use crate::errors::{
28 DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib, LtoProcMacro,
29};
30use crate::llvm::{self, build_string};
31use crate::{LlvmCodegenBackend, ModuleLlvm};
32
33const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
36
37fn crate_type_allows_lto(crate_type: CrateType) -> bool {
38 match crate_type {
39 CrateType::Executable
40 | CrateType::Dylib
41 | CrateType::Staticlib
42 | CrateType::Cdylib
43 | CrateType::ProcMacro => true,
44 CrateType::Rlib => false,
45 }
46}
47
48fn prepare_lto(
49 cgcx: &CodegenContext<LlvmCodegenBackend>,
50 dcx: DiagCtxtHandle<'_>,
51) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
52 let export_threshold = match cgcx.lto {
53 Lto::ThinLocal => SymbolExportLevel::Rust,
55
56 Lto::Fat | Lto::Thin => symbol_export::crates_export_threshold(&cgcx.crate_types),
58
59 Lto::No => panic!("didn't request LTO but we're doing LTO"),
60 };
61
62 let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
63 if info.level.is_below_threshold(export_threshold) || info.used {
64 Some(CString::new(name.as_str()).unwrap())
65 } else {
66 None
67 }
68 };
69 let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
70 let mut symbols_below_threshold = {
71 let _timer = cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
72 exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::<Vec<CString>>()
73 };
74 info!("{} symbols to preserve in this crate", symbols_below_threshold.len());
75
76 let mut upstream_modules = Vec::new();
83 if cgcx.lto != Lto::ThinLocal {
84 for crate_type in cgcx.crate_types.iter() {
86 if !crate_type_allows_lto(*crate_type) {
87 dcx.emit_err(LtoDisallowed);
88 return Err(FatalError);
89 } else if *crate_type == CrateType::Dylib {
90 if !cgcx.opts.unstable_opts.dylib_lto {
91 dcx.emit_err(LtoDylib);
92 return Err(FatalError);
93 }
94 } else if *crate_type == CrateType::ProcMacro && !cgcx.opts.unstable_opts.dylib_lto {
95 dcx.emit_err(LtoProcMacro);
96 return Err(FatalError);
97 }
98 }
99
100 if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
101 dcx.emit_err(DynamicLinkingWithLTO);
102 return Err(FatalError);
103 }
104
105 for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
106 let exported_symbols =
107 cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
108 {
109 let _timer =
110 cgcx.prof.generic_activity("LLVM_lto_generate_symbols_below_threshold");
111 symbols_below_threshold
112 .extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
113 }
114
115 let archive_data = unsafe {
116 Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
117 .expect("couldn't map rlib")
118 };
119 let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
120 let obj_files = archive
121 .members()
122 .filter_map(|child| {
123 child.ok().and_then(|c| {
124 std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))
125 })
126 })
127 .filter(|&(name, _)| looks_like_rust_object_file(name));
128 for (name, child) in obj_files {
129 info!("adding bitcode from {}", name);
130 match get_bitcode_slice_from_object_data(
131 child.data(&*archive_data).expect("corrupt rlib"),
132 cgcx,
133 ) {
134 Ok(data) => {
135 let module = SerializedModule::FromRlib(data.to_vec());
136 upstream_modules.push((module, CString::new(name).unwrap()));
137 }
138 Err(e) => {
139 dcx.emit_err(e);
140 return Err(FatalError);
141 }
142 }
143 }
144 }
145 }
146
147 symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
151 Ok((symbols_below_threshold, upstream_modules))
152}
153
154fn get_bitcode_slice_from_object_data<'a>(
155 obj: &'a [u8],
156 cgcx: &CodegenContext<LlvmCodegenBackend>,
157) -> Result<&'a [u8], LtoBitcodeFromRlib> {
158 if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") {
162 return Ok(obj);
163 }
164 let section_name = bitcode_section_name(cgcx).to_str().unwrap().trim_start_matches("__LLVM,");
168 let mut len = 0;
169 let data = unsafe {
170 llvm::LLVMRustGetSliceFromObjectDataByName(
171 obj.as_ptr(),
172 obj.len(),
173 section_name.as_ptr(),
174 section_name.len(),
175 &mut len,
176 )
177 };
178 if !data.is_null() {
179 assert!(len != 0);
180 let bc = unsafe { slice::from_raw_parts(data, len) };
181
182 assert!(obj.as_ptr() <= bc.as_ptr());
184 assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr());
185
186 Ok(bc)
187 } else {
188 assert!(len == 0);
189 Err(LtoBitcodeFromRlib {
190 llvm_err: llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()),
191 })
192 }
193}
194
195pub(crate) fn run_fat(
198 cgcx: &CodegenContext<LlvmCodegenBackend>,
199 modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
200 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
201) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
202 let dcx = cgcx.create_dcx();
203 let dcx = dcx.handle();
204 let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
205 let symbols_below_threshold =
206 symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
207 fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
208}
209
210pub(crate) fn run_thin(
214 cgcx: &CodegenContext<LlvmCodegenBackend>,
215 modules: Vec<(String, ThinBuffer)>,
216 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
217) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
218 let dcx = cgcx.create_dcx();
219 let dcx = dcx.handle();
220 let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
221 let symbols_below_threshold =
222 symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
223 if cgcx.opts.cg.linker_plugin_lto.enabled() {
224 unreachable!(
225 "We should never reach this case if the LTO step \
226 is deferred to the linker"
227 );
228 }
229 thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
230}
231
232pub(crate) fn prepare_thin(
233 module: ModuleCodegen<ModuleLlvm>,
234 emit_summary: bool,
235) -> (String, ThinBuffer) {
236 let name = module.name;
237 let buffer = ThinBuffer::new(module.module_llvm.llmod(), true, emit_summary);
238 (name, buffer)
239}
240
241fn fat_lto(
242 cgcx: &CodegenContext<LlvmCodegenBackend>,
243 dcx: DiagCtxtHandle<'_>,
244 modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
245 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
246 mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
247 symbols_below_threshold: &[*const libc::c_char],
248) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
249 let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
250 info!("going for a fat lto");
251
252 let mut in_memory = Vec::new();
263 serialized_modules.extend(cached_modules.into_iter().map(|(buffer, wp)| {
264 info!("pushing cached module {:?}", wp.cgu_name);
265 (buffer, CString::new(wp.cgu_name).unwrap())
266 }));
267 for module in modules {
268 match module {
269 FatLtoInput::InMemory(m) => in_memory.push(m),
270 FatLtoInput::Serialized { name, buffer } => {
271 info!("pushing serialized module {:?}", name);
272 let buffer = SerializedModule::Local(buffer);
273 serialized_modules.push((buffer, CString::new(name).unwrap()));
274 }
275 }
276 }
277
278 let costliest_module = in_memory
288 .iter()
289 .enumerate()
290 .filter(|&(_, module)| module.kind == ModuleKind::Regular)
291 .map(|(i, module)| {
292 let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) };
293 (cost, i)
294 })
295 .max();
296
297 let module: ModuleCodegen<ModuleLlvm> = match costliest_module {
303 Some((_cost, i)) => in_memory.remove(i),
304 None => {
305 assert!(!serialized_modules.is_empty(), "must have at least one serialized module");
306 let (buffer, name) = serialized_modules.remove(0);
307 info!("no in-memory regular modules to choose from, parsing {:?}", name);
308 ModuleCodegen {
309 module_llvm: ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx)?,
310 name: name.into_string().unwrap(),
311 kind: ModuleKind::Regular,
312 }
313 }
314 };
315 {
316 let (llcx, llmod) = {
317 let llvm = &module.module_llvm;
318 (&llvm.llcx, llvm.llmod())
319 };
320 info!("using {:?} as a base module", module.name);
321
322 let _handler =
326 DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::LTO);
327
328 for module in in_memory {
334 let buffer = ModuleBuffer::new(module.module_llvm.llmod());
335 let llmod_id = CString::new(&module.name[..]).unwrap();
336 serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
337 }
338 serialized_modules.sort_by(|module1, module2| module1.1.cmp(&module2.1));
340
341 let mut linker = Linker::new(llmod);
344 for (bc_decoded, name) in serialized_modules {
345 let _timer = cgcx
346 .prof
347 .generic_activity_with_arg_recorder("LLVM_fat_lto_link_module", |recorder| {
348 recorder.record_arg(format!("{name:?}"))
349 });
350 info!("linking {:?}", name);
351 let data = bc_decoded.data();
352 linker.add(data).map_err(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }))?;
353 }
354 drop(linker);
355 save_temp_bitcode(cgcx, &module, "lto.input");
356
357 unsafe {
359 let ptr = symbols_below_threshold.as_ptr();
360 llvm::LLVMRustRunRestrictionPass(
361 llmod,
362 ptr as *const *const libc::c_char,
363 symbols_below_threshold.len() as libc::size_t,
364 );
365 save_temp_bitcode(cgcx, &module, "lto.after-restriction");
366 }
367 }
368
369 Ok(LtoModuleCodegen::Fat(module))
370}
371
372pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>);
373
374impl<'a> Linker<'a> {
375 pub(crate) fn new(llmod: &'a llvm::Module) -> Self {
376 unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) }
377 }
378
379 pub(crate) fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
380 unsafe {
381 if llvm::LLVMRustLinkerAdd(
382 self.0,
383 bytecode.as_ptr() as *const libc::c_char,
384 bytecode.len(),
385 ) {
386 Ok(())
387 } else {
388 Err(())
389 }
390 }
391 }
392}
393
394impl Drop for Linker<'_> {
395 fn drop(&mut self) {
396 unsafe {
397 llvm::LLVMRustLinkerFree(&mut *(self.0 as *mut _));
398 }
399 }
400}
401
402fn thin_lto(
433 cgcx: &CodegenContext<LlvmCodegenBackend>,
434 dcx: DiagCtxtHandle<'_>,
435 modules: Vec<(String, ThinBuffer)>,
436 serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
437 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
438 symbols_below_threshold: &[*const libc::c_char],
439) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
440 let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
441 unsafe {
442 info!("going for that thin, thin LTO");
443
444 let green_modules: FxHashMap<_, _> =
445 cached_modules.iter().map(|(_, wp)| (wp.cgu_name.clone(), wp.clone())).collect();
446
447 let full_scope_len = modules.len() + serialized_modules.len() + cached_modules.len();
448 let mut thin_buffers = Vec::with_capacity(modules.len());
449 let mut module_names = Vec::with_capacity(full_scope_len);
450 let mut thin_modules = Vec::with_capacity(full_scope_len);
451
452 for (i, (name, buffer)) in modules.into_iter().enumerate() {
453 info!("local module: {} - {}", i, name);
454 let cname = CString::new(name.as_bytes()).unwrap();
455 thin_modules.push(llvm::ThinLTOModule {
456 identifier: cname.as_ptr(),
457 data: buffer.data().as_ptr(),
458 len: buffer.data().len(),
459 });
460 thin_buffers.push(buffer);
461 module_names.push(cname);
462 }
463
464 let mut serialized = Vec::with_capacity(serialized_modules.len() + cached_modules.len());
481
482 let cached_modules =
483 cached_modules.into_iter().map(|(sm, wp)| (sm, CString::new(wp.cgu_name).unwrap()));
484
485 for (module, name) in serialized_modules.into_iter().chain(cached_modules) {
486 info!("upstream or cached module {:?}", name);
487 thin_modules.push(llvm::ThinLTOModule {
488 identifier: name.as_ptr(),
489 data: module.data().as_ptr(),
490 len: module.data().len(),
491 });
492 serialized.push(module);
493 module_names.push(name);
494 }
495
496 assert_eq!(thin_modules.len(), module_names.len());
498
499 let data = llvm::LLVMRustCreateThinLTOData(
504 thin_modules.as_ptr(),
505 thin_modules.len(),
506 symbols_below_threshold.as_ptr(),
507 symbols_below_threshold.len(),
508 )
509 .ok_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext))?;
510
511 let data = ThinData(data);
512
513 info!("thin LTO data created");
514
515 let (key_map_path, prev_key_map, curr_key_map) = if let Some(ref incr_comp_session_dir) =
516 cgcx.incr_comp_session_dir
517 {
518 let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME);
519 let prev =
523 if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None };
524 let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names);
525 (Some(path), prev, curr)
526 } else {
527 assert!(green_modules.is_empty());
530 let curr = ThinLTOKeysMap::default();
531 (None, None, curr)
532 };
533 info!("thin LTO cache key map loaded");
534 info!("prev_key_map: {:#?}", prev_key_map);
535 info!("curr_key_map: {:#?}", curr_key_map);
536
537 let shared = Arc::new(ThinShared {
542 data,
543 thin_buffers,
544 serialized_modules: serialized,
545 module_names,
546 });
547
548 let mut copy_jobs = vec![];
549 let mut opt_jobs = vec![];
550
551 info!("checking which modules can be-reused and which have to be re-optimized.");
552 for (module_index, module_name) in shared.module_names.iter().enumerate() {
553 let module_name = module_name_to_str(module_name);
554 if let (Some(prev_key_map), true) =
555 (prev_key_map.as_ref(), green_modules.contains_key(module_name))
556 {
557 assert!(cgcx.incr_comp_session_dir.is_some());
558
559 if prev_key_map.keys.get(module_name) == curr_key_map.keys.get(module_name) {
562 let work_product = green_modules[module_name].clone();
563 copy_jobs.push(work_product);
564 info!(" - {}: re-used", module_name);
565 assert!(cgcx.incr_comp_session_dir.is_some());
566 continue;
567 }
568 }
569
570 info!(" - {}: re-compiled", module_name);
571 opt_jobs.push(LtoModuleCodegen::Thin(ThinModule {
572 shared: Arc::clone(&shared),
573 idx: module_index,
574 }));
575 }
576
577 if let Some(path) = key_map_path {
580 if let Err(err) = curr_key_map.save_to_file(&path) {
581 return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
582 }
583 }
584
585 Ok((opt_jobs, copy_jobs))
586 }
587}
588
589pub(crate) fn run_pass_manager(
590 cgcx: &CodegenContext<LlvmCodegenBackend>,
591 dcx: DiagCtxtHandle<'_>,
592 module: &mut ModuleCodegen<ModuleLlvm>,
593 thin: bool,
594) -> Result<(), FatalError> {
595 let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
596 let config = cgcx.config(module.kind);
597
598 debug!("running the pass manager");
604 let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
605 let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
606
607 debug!("running llvm pm opt pipeline");
610 unsafe {
611 write::llvm_optimize(
612 cgcx,
613 dcx,
614 module,
615 config,
616 opt_level,
617 opt_stage,
618 write::AutodiffStage::DuringAD,
619 )?;
620 }
621 if cfg!(llvm_enzyme) && !thin {
623 unsafe {
624 write::llvm_optimize(
625 cgcx,
626 dcx,
627 module,
628 config,
629 opt_level,
630 llvm::OptStage::FatLTO,
631 write::AutodiffStage::PostAD,
632 )?;
633 }
634 }
635 debug!("lto done");
636 Ok(())
637}
638
639pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer);
640
641unsafe impl Send for ModuleBuffer {}
642unsafe impl Sync for ModuleBuffer {}
643
644impl ModuleBuffer {
645 pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer {
646 ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) })
647 }
648}
649
650impl ModuleBufferMethods for ModuleBuffer {
651 fn data(&self) -> &[u8] {
652 unsafe {
653 let ptr = llvm::LLVMRustModuleBufferPtr(self.0);
654 let len = llvm::LLVMRustModuleBufferLen(self.0);
655 slice::from_raw_parts(ptr, len)
656 }
657 }
658}
659
660impl Drop for ModuleBuffer {
661 fn drop(&mut self) {
662 unsafe {
663 llvm::LLVMRustModuleBufferFree(&mut *(self.0 as *mut _));
664 }
665 }
666}
667
668pub struct ThinData(&'static mut llvm::ThinLTOData);
669
670unsafe impl Send for ThinData {}
671unsafe impl Sync for ThinData {}
672
673impl Drop for ThinData {
674 fn drop(&mut self) {
675 unsafe {
676 llvm::LLVMRustFreeThinLTOData(&mut *(self.0 as *mut _));
677 }
678 }
679}
680
681pub struct ThinBuffer(&'static mut llvm::ThinLTOBuffer);
682
683unsafe impl Send for ThinBuffer {}
684unsafe impl Sync for ThinBuffer {}
685
686impl ThinBuffer {
687 pub(crate) fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer {
688 unsafe {
689 let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, emit_summary);
690 ThinBuffer(buffer)
691 }
692 }
693}
694
695impl ThinBufferMethods for ThinBuffer {
696 fn data(&self) -> &[u8] {
697 unsafe {
698 let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _;
699 let len = llvm::LLVMRustThinLTOBufferLen(self.0);
700 slice::from_raw_parts(ptr, len)
701 }
702 }
703
704 fn thin_link_data(&self) -> &[u8] {
705 unsafe {
706 let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _;
707 let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0);
708 slice::from_raw_parts(ptr, len)
709 }
710 }
711}
712
713impl Drop for ThinBuffer {
714 fn drop(&mut self) {
715 unsafe {
716 llvm::LLVMRustThinLTOBufferFree(&mut *(self.0 as *mut _));
717 }
718 }
719}
720
721pub(crate) unsafe fn optimize_thin_module(
722 thin_module: ThinModule<LlvmCodegenBackend>,
723 cgcx: &CodegenContext<LlvmCodegenBackend>,
724) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
725 let dcx = cgcx.create_dcx();
726 let dcx = dcx.handle();
727
728 let module_name = &thin_module.shared.module_names[thin_module.idx];
729
730 let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx)?;
736 let mut module = ModuleCodegen {
737 module_llvm,
738 name: thin_module.name().to_string(),
739 kind: ModuleKind::Regular,
740 };
741 {
742 let target = &*module.module_llvm.tm;
743 let llmod = module.module_llvm.llmod();
744 save_temp_bitcode(cgcx, &module, "thin-lto-input");
745
746 {
755 let _timer =
756 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
757 unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) };
758 save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
759 }
760
761 {
762 let _timer = cgcx
763 .prof
764 .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
765 if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) }
766 {
767 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
768 }
769 save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
770 }
771
772 {
773 let _timer = cgcx
774 .prof
775 .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
776 if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) }
777 {
778 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
779 }
780 save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
781 }
782
783 {
784 let _timer =
785 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
786 if unsafe {
787 !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target)
788 } {
789 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
790 }
791 save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
792 }
793
794 {
800 info!("running thin lto passes over {}", module.name);
801 run_pass_manager(cgcx, dcx, &mut module, true)?;
802 save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
803 }
804 }
805 Ok(module)
806}
807
808#[derive(Debug, Default)]
810struct ThinLTOKeysMap {
811 keys: BTreeMap<String, String>,
813}
814
815impl ThinLTOKeysMap {
816 fn save_to_file(&self, path: &Path) -> io::Result<()> {
817 use std::io::Write;
818 let mut writer = File::create_buffered(path)?;
819 for (module, key) in &self.keys {
822 writeln!(writer, "{module} {key}")?;
823 }
824 Ok(())
825 }
826
827 fn load_from_file(path: &Path) -> io::Result<Self> {
828 use std::io::BufRead;
829 let mut keys = BTreeMap::default();
830 let file = File::open_buffered(path)?;
831 for line in file.lines() {
832 let line = line?;
833 let mut split = line.split(' ');
834 let module = split.next().unwrap();
835 let key = split.next().unwrap();
836 assert_eq!(split.next(), None, "Expected two space-separated values, found {line:?}");
837 keys.insert(module.to_string(), key.to_string());
838 }
839 Ok(Self { keys })
840 }
841
842 fn from_thin_lto_modules(
843 data: &ThinData,
844 modules: &[llvm::ThinLTOModule],
845 names: &[CString],
846 ) -> Self {
847 let keys = iter::zip(modules, names)
848 .map(|(module, name)| {
849 let key = build_string(|rust_str| unsafe {
850 llvm::LLVMRustComputeLTOCacheKey(rust_str, module.identifier, data.0);
851 })
852 .expect("Invalid ThinLTO module key");
853 (module_name_to_str(name).to_string(), key)
854 })
855 .collect();
856 Self { keys }
857 }
858}
859
860fn module_name_to_str(c_str: &CStr) -> &str {
861 c_str.to_str().unwrap_or_else(|e| {
862 bug!("Encountered non-utf8 LLVM module name `{}`: {}", c_str.to_string_lossy(), e)
863 })
864}
865
866pub(crate) fn parse_module<'a>(
867 cx: &'a llvm::Context,
868 name: &CStr,
869 data: &[u8],
870 dcx: DiagCtxtHandle<'_>,
871) -> Result<&'a llvm::Module, FatalError> {
872 unsafe {
873 llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr())
874 .ok_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode))
875 }
876}