1use std::collections::BTreeMap;
2use std::ffi::{CStr, CString};
3use std::fs::File;
4use std::path::{Path, PathBuf};
5use std::ptr::NonNull;
6use std::sync::Arc;
7use std::{io, iter, slice};
8
9use object::read::archive::ArchiveFile;
10use object::{Object, ObjectSection};
11use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
12use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
13use rustc_codegen_ssa::traits::*;
14use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file};
15use rustc_data_structures::fx::FxHashMap;
16use rustc_data_structures::memmap::Mmap;
17use rustc_errors::{DiagCtxtHandle, FatalError};
18use rustc_middle::bug;
19use rustc_middle::dep_graph::WorkProduct;
20use rustc_session::config::{self, Lto};
21use tracing::{debug, info};
22
23use crate::back::write::{
24 self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, save_temp_bitcode,
25};
26use crate::errors::{LlvmError, LtoBitcodeFromRlib};
27use crate::llvm::{self, build_string};
28use crate::{LlvmCodegenBackend, ModuleLlvm, SimpleCx};
29
30const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
33
34fn prepare_lto(
35 cgcx: &CodegenContext<LlvmCodegenBackend>,
36 exported_symbols_for_lto: &[String],
37 each_linked_rlib_for_lto: &[PathBuf],
38 dcx: DiagCtxtHandle<'_>,
39) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
40 let mut symbols_below_threshold = exported_symbols_for_lto
41 .iter()
42 .map(|symbol| CString::new(symbol.to_owned()).unwrap())
43 .collect::<Vec<CString>>();
44
45 symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned());
49
50 let mut upstream_modules = Vec::new();
57 if cgcx.lto != Lto::ThinLocal {
58 for path in each_linked_rlib_for_lto {
59 let archive_data = unsafe {
60 Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
61 .expect("couldn't map rlib")
62 };
63 let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
64 let obj_files = archive
65 .members()
66 .filter_map(|child| {
67 child.ok().and_then(|c| {
68 std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))
69 })
70 })
71 .filter(|&(name, _)| looks_like_rust_object_file(name));
72 for (name, child) in obj_files {
73 info!("adding bitcode from {}", name);
74 match get_bitcode_slice_from_object_data(
75 child.data(&*archive_data).expect("corrupt rlib"),
76 cgcx,
77 ) {
78 Ok(data) => {
79 let module = SerializedModule::FromRlib(data.to_vec());
80 upstream_modules.push((module, CString::new(name).unwrap()));
81 }
82 Err(e) => {
83 dcx.emit_err(e);
84 return Err(FatalError);
85 }
86 }
87 }
88 }
89 }
90
91 Ok((symbols_below_threshold, upstream_modules))
92}
93
94fn get_bitcode_slice_from_object_data<'a>(
95 obj: &'a [u8],
96 cgcx: &CodegenContext<LlvmCodegenBackend>,
97) -> Result<&'a [u8], LtoBitcodeFromRlib> {
98 if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") {
102 return Ok(obj);
103 }
104 let section_name = bitcode_section_name(cgcx).to_str().unwrap().trim_start_matches("__LLVM,");
108
109 let obj =
110 object::File::parse(obj).map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })?;
111
112 let section = obj
113 .section_by_name(section_name)
114 .ok_or_else(|| LtoBitcodeFromRlib { err: format!("Can't find section {section_name}") })?;
115
116 section.data().map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })
117}
118
119pub(crate) fn run_fat(
122 cgcx: &CodegenContext<LlvmCodegenBackend>,
123 exported_symbols_for_lto: &[String],
124 each_linked_rlib_for_lto: &[PathBuf],
125 modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
126) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
127 let dcx = cgcx.create_dcx();
128 let dcx = dcx.handle();
129 let (symbols_below_threshold, upstream_modules) =
130 prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
131 let symbols_below_threshold =
132 symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
133 fat_lto(cgcx, dcx, modules, upstream_modules, &symbols_below_threshold)
134}
135
136pub(crate) fn run_thin(
140 cgcx: &CodegenContext<LlvmCodegenBackend>,
141 exported_symbols_for_lto: &[String],
142 each_linked_rlib_for_lto: &[PathBuf],
143 modules: Vec<(String, ThinBuffer)>,
144 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
145) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
146 let dcx = cgcx.create_dcx();
147 let dcx = dcx.handle();
148 let (symbols_below_threshold, upstream_modules) =
149 prepare_lto(cgcx, exported_symbols_for_lto, each_linked_rlib_for_lto, dcx)?;
150 let symbols_below_threshold =
151 symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
152 if cgcx.opts.cg.linker_plugin_lto.enabled() {
153 unreachable!(
154 "We should never reach this case if the LTO step \
155 is deferred to the linker"
156 );
157 }
158 thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
159}
160
161pub(crate) fn prepare_thin(
162 module: ModuleCodegen<ModuleLlvm>,
163 emit_summary: bool,
164) -> (String, ThinBuffer) {
165 let name = module.name;
166 let buffer = ThinBuffer::new(module.module_llvm.llmod(), true, emit_summary);
167 (name, buffer)
168}
169
170fn fat_lto(
171 cgcx: &CodegenContext<LlvmCodegenBackend>,
172 dcx: DiagCtxtHandle<'_>,
173 modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
174 mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
175 symbols_below_threshold: &[*const libc::c_char],
176) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
177 let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_build_monolithic_module");
178 info!("going for a fat lto");
179
180 let mut in_memory = Vec::new();
187 for module in modules {
188 match module {
189 FatLtoInput::InMemory(m) => in_memory.push(m),
190 FatLtoInput::Serialized { name, buffer } => {
191 info!("pushing serialized module {:?}", name);
192 serialized_modules.push((buffer, CString::new(name).unwrap()));
193 }
194 }
195 }
196
197 let costliest_module = in_memory
207 .iter()
208 .enumerate()
209 .filter(|&(_, module)| module.kind == ModuleKind::Regular)
210 .map(|(i, module)| {
211 let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) };
212 (cost, i)
213 })
214 .max();
215
216 let module: ModuleCodegen<ModuleLlvm> = match costliest_module {
222 Some((_cost, i)) => in_memory.remove(i),
223 None => {
224 assert!(!serialized_modules.is_empty(), "must have at least one serialized module");
225 let (buffer, name) = serialized_modules.remove(0);
226 info!("no in-memory regular modules to choose from, parsing {:?}", name);
227 let llvm_module = ModuleLlvm::parse(cgcx, &name, buffer.data(), dcx)?;
228 ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module)
229 }
230 };
231 {
232 let (llcx, llmod) = {
233 let llvm = &module.module_llvm;
234 (&llvm.llcx, llvm.llmod())
235 };
236 info!("using {:?} as a base module", module.name);
237
238 let _handler =
242 DiagnosticHandlers::new(cgcx, dcx, llcx, &module, CodegenDiagnosticsStage::LTO);
243
244 for module in in_memory {
250 let buffer = ModuleBuffer::new(module.module_llvm.llmod());
251 let llmod_id = CString::new(&module.name[..]).unwrap();
252 serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
253 }
254 serialized_modules.sort_by(|module1, module2| module1.1.cmp(&module2.1));
256
257 let mut linker = Linker::new(llmod);
260 for (bc_decoded, name) in serialized_modules {
261 let _timer = cgcx
262 .prof
263 .generic_activity_with_arg_recorder("LLVM_fat_lto_link_module", |recorder| {
264 recorder.record_arg(format!("{name:?}"))
265 });
266 info!("linking {:?}", name);
267 let data = bc_decoded.data();
268 linker.add(data).map_err(|()| write::llvm_err(dcx, LlvmError::LoadBitcode { name }))?;
269 }
270 drop(linker);
271 save_temp_bitcode(cgcx, &module, "lto.input");
272
273 unsafe {
275 let ptr = symbols_below_threshold.as_ptr();
276 llvm::LLVMRustRunRestrictionPass(
277 llmod,
278 ptr as *const *const libc::c_char,
279 symbols_below_threshold.len() as libc::size_t,
280 );
281 }
282 save_temp_bitcode(cgcx, &module, "lto.after-restriction");
283 }
284
285 Ok(module)
286}
287
288pub(crate) struct Linker<'a>(&'a mut llvm::Linker<'a>);
289
290impl<'a> Linker<'a> {
291 pub(crate) fn new(llmod: &'a llvm::Module) -> Self {
292 unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) }
293 }
294
295 pub(crate) fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
296 unsafe {
297 if llvm::LLVMRustLinkerAdd(
298 self.0,
299 bytecode.as_ptr() as *const libc::c_char,
300 bytecode.len(),
301 ) {
302 Ok(())
303 } else {
304 Err(())
305 }
306 }
307 }
308}
309
310impl Drop for Linker<'_> {
311 fn drop(&mut self) {
312 unsafe {
313 llvm::LLVMRustLinkerFree(&mut *(self.0 as *mut _));
314 }
315 }
316}
317
318fn thin_lto(
349 cgcx: &CodegenContext<LlvmCodegenBackend>,
350 dcx: DiagCtxtHandle<'_>,
351 modules: Vec<(String, ThinBuffer)>,
352 serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
353 cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
354 symbols_below_threshold: &[*const libc::c_char],
355) -> Result<(Vec<ThinModule<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
356 let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis");
357 unsafe {
358 info!("going for that thin, thin LTO");
359
360 let green_modules: FxHashMap<_, _> =
361 cached_modules.iter().map(|(_, wp)| (wp.cgu_name.clone(), wp.clone())).collect();
362
363 let full_scope_len = modules.len() + serialized_modules.len() + cached_modules.len();
364 let mut thin_buffers = Vec::with_capacity(modules.len());
365 let mut module_names = Vec::with_capacity(full_scope_len);
366 let mut thin_modules = Vec::with_capacity(full_scope_len);
367
368 for (i, (name, buffer)) in modules.into_iter().enumerate() {
369 info!("local module: {} - {}", i, name);
370 let cname = CString::new(name.as_bytes()).unwrap();
371 thin_modules.push(llvm::ThinLTOModule {
372 identifier: cname.as_ptr(),
373 data: buffer.data().as_ptr(),
374 len: buffer.data().len(),
375 });
376 thin_buffers.push(buffer);
377 module_names.push(cname);
378 }
379
380 let mut serialized = Vec::with_capacity(serialized_modules.len() + cached_modules.len());
397
398 let cached_modules =
399 cached_modules.into_iter().map(|(sm, wp)| (sm, CString::new(wp.cgu_name).unwrap()));
400
401 for (module, name) in serialized_modules.into_iter().chain(cached_modules) {
402 info!("upstream or cached module {:?}", name);
403 thin_modules.push(llvm::ThinLTOModule {
404 identifier: name.as_ptr(),
405 data: module.data().as_ptr(),
406 len: module.data().len(),
407 });
408 serialized.push(module);
409 module_names.push(name);
410 }
411
412 assert_eq!(thin_modules.len(), module_names.len());
414
415 let data = llvm::LLVMRustCreateThinLTOData(
420 thin_modules.as_ptr(),
421 thin_modules.len(),
422 symbols_below_threshold.as_ptr(),
423 symbols_below_threshold.len(),
424 )
425 .ok_or_else(|| write::llvm_err(dcx, LlvmError::PrepareThinLtoContext))?;
426
427 let data = ThinData(data);
428
429 info!("thin LTO data created");
430
431 let (key_map_path, prev_key_map, curr_key_map) = if let Some(ref incr_comp_session_dir) =
432 cgcx.incr_comp_session_dir
433 {
434 let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME);
435 let prev =
439 if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None };
440 let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names);
441 (Some(path), prev, curr)
442 } else {
443 assert!(green_modules.is_empty());
446 let curr = ThinLTOKeysMap::default();
447 (None, None, curr)
448 };
449 info!("thin LTO cache key map loaded");
450 info!("prev_key_map: {:#?}", prev_key_map);
451 info!("curr_key_map: {:#?}", curr_key_map);
452
453 let shared = Arc::new(ThinShared {
458 data,
459 thin_buffers,
460 serialized_modules: serialized,
461 module_names,
462 });
463
464 let mut copy_jobs = vec![];
465 let mut opt_jobs = vec![];
466
467 info!("checking which modules can be-reused and which have to be re-optimized.");
468 for (module_index, module_name) in shared.module_names.iter().enumerate() {
469 let module_name = module_name_to_str(module_name);
470 if let (Some(prev_key_map), true) =
471 (prev_key_map.as_ref(), green_modules.contains_key(module_name))
472 {
473 assert!(cgcx.incr_comp_session_dir.is_some());
474
475 if prev_key_map.keys.get(module_name) == curr_key_map.keys.get(module_name) {
478 let work_product = green_modules[module_name].clone();
479 copy_jobs.push(work_product);
480 info!(" - {}: re-used", module_name);
481 assert!(cgcx.incr_comp_session_dir.is_some());
482 continue;
483 }
484 }
485
486 info!(" - {}: re-compiled", module_name);
487 opt_jobs.push(ThinModule { shared: Arc::clone(&shared), idx: module_index });
488 }
489
490 if let Some(path) = key_map_path
493 && let Err(err) = curr_key_map.save_to_file(&path)
494 {
495 return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err }));
496 }
497
498 Ok((opt_jobs, copy_jobs))
499 }
500}
501
502fn enable_autodiff_settings(ad: &[config::AutoDiff]) {
503 for val in ad {
504 match val {
506 config::AutoDiff::PrintPerf => {
507 llvm::set_print_perf(true);
508 }
509 config::AutoDiff::PrintAA => {
510 llvm::set_print_activity(true);
511 }
512 config::AutoDiff::PrintTA => {
513 llvm::set_print_type(true);
514 }
515 config::AutoDiff::PrintTAFn(fun) => {
516 llvm::set_print_type(true); llvm::set_print_type_fun(&fun); }
519 config::AutoDiff::Inline => {
520 llvm::set_inline(true);
521 }
522 config::AutoDiff::LooseTypes => {
523 llvm::set_loose_types(true);
524 }
525 config::AutoDiff::PrintSteps => {
526 llvm::set_print(true);
527 }
528 config::AutoDiff::PrintPasses => {}
530 config::AutoDiff::PrintModBefore => {}
532 config::AutoDiff::PrintModAfter => {}
534 config::AutoDiff::PrintModFinal => {}
536 config::AutoDiff::Enable => {}
538 config::AutoDiff::NoPostopt => {}
540 }
541 }
542 llvm::set_strict_aliasing(false);
544 llvm::set_rust_rules(true);
546}
547
548pub(crate) fn run_pass_manager(
549 cgcx: &CodegenContext<LlvmCodegenBackend>,
550 dcx: DiagCtxtHandle<'_>,
551 module: &mut ModuleCodegen<ModuleLlvm>,
552 thin: bool,
553) -> Result<(), FatalError> {
554 let _timer = cgcx.prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
555 let config = cgcx.config(module.kind);
556
557 debug!("running the pass manager");
563 let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
564 let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
565
566 let enable_ad = config.autodiff.contains(&config::AutoDiff::Enable);
573 let enable_gpu = config.offload.contains(&config::Offload::Enable);
574 let stage = if thin {
575 write::AutodiffStage::PreAD
576 } else {
577 if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD }
578 };
579
580 if enable_ad {
581 enable_autodiff_settings(&config.autodiff);
582 }
583
584 unsafe {
585 write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?;
586 }
587
588 if enable_gpu && !thin {
589 let cx =
590 SimpleCx::new(module.module_llvm.llmod(), &module.module_llvm.llcx, cgcx.pointer_size);
591 crate::builder::gpu_offload::handle_gpu_code(cgcx, &cx);
592 }
593
594 if cfg!(llvm_enzyme) && enable_ad && !thin {
595 let opt_stage = llvm::OptStage::FatLTO;
596 let stage = write::AutodiffStage::PostAD;
597 if !config.autodiff.contains(&config::AutoDiff::NoPostopt) {
598 unsafe {
599 write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?;
600 }
601 }
602
603 if config.autodiff.contains(&config::AutoDiff::PrintModFinal) {
606 unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
607 }
608 }
609
610 debug!("lto done");
611 Ok(())
612}
613
614pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer);
615
616unsafe impl Send for ModuleBuffer {}
617unsafe impl Sync for ModuleBuffer {}
618
619impl ModuleBuffer {
620 pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer {
621 ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) })
622 }
623}
624
625impl ModuleBufferMethods for ModuleBuffer {
626 fn data(&self) -> &[u8] {
627 unsafe {
628 let ptr = llvm::LLVMRustModuleBufferPtr(self.0);
629 let len = llvm::LLVMRustModuleBufferLen(self.0);
630 slice::from_raw_parts(ptr, len)
631 }
632 }
633}
634
635impl Drop for ModuleBuffer {
636 fn drop(&mut self) {
637 unsafe {
638 llvm::LLVMRustModuleBufferFree(&mut *(self.0 as *mut _));
639 }
640 }
641}
642
643pub struct ThinData(&'static mut llvm::ThinLTOData);
644
645unsafe impl Send for ThinData {}
646unsafe impl Sync for ThinData {}
647
648impl Drop for ThinData {
649 fn drop(&mut self) {
650 unsafe {
651 llvm::LLVMRustFreeThinLTOData(&mut *(self.0 as *mut _));
652 }
653 }
654}
655
656pub struct ThinBuffer(&'static mut llvm::ThinLTOBuffer);
657
658unsafe impl Send for ThinBuffer {}
659unsafe impl Sync for ThinBuffer {}
660
661impl ThinBuffer {
662 pub(crate) fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer {
663 unsafe {
664 let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, emit_summary);
665 ThinBuffer(buffer)
666 }
667 }
668
669 pub(crate) unsafe fn from_raw_ptr(ptr: *mut llvm::ThinLTOBuffer) -> ThinBuffer {
670 let mut ptr = NonNull::new(ptr).unwrap();
671 ThinBuffer(unsafe { ptr.as_mut() })
672 }
673}
674
675impl ThinBufferMethods for ThinBuffer {
676 fn data(&self) -> &[u8] {
677 unsafe {
678 let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _;
679 let len = llvm::LLVMRustThinLTOBufferLen(self.0);
680 slice::from_raw_parts(ptr, len)
681 }
682 }
683
684 fn thin_link_data(&self) -> &[u8] {
685 unsafe {
686 let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _;
687 let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0);
688 slice::from_raw_parts(ptr, len)
689 }
690 }
691}
692
693impl Drop for ThinBuffer {
694 fn drop(&mut self) {
695 unsafe {
696 llvm::LLVMRustThinLTOBufferFree(&mut *(self.0 as *mut _));
697 }
698 }
699}
700
701pub(crate) fn optimize_thin_module(
702 thin_module: ThinModule<LlvmCodegenBackend>,
703 cgcx: &CodegenContext<LlvmCodegenBackend>,
704) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
705 let dcx = cgcx.create_dcx();
706 let dcx = dcx.handle();
707
708 let module_name = &thin_module.shared.module_names[thin_module.idx];
709
710 let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx)?;
716 let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
717 if cgcx.config(ModuleKind::Regular).embed_bitcode() {
719 module.thin_lto_buffer = Some(thin_module.data().to_vec());
720 }
721 {
722 let target = &*module.module_llvm.tm;
723 let llmod = module.module_llvm.llmod();
724 save_temp_bitcode(cgcx, &module, "thin-lto-input");
725
726 {
735 let _timer =
736 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
737 unsafe {
738 llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw())
739 };
740 save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
741 }
742
743 {
744 let _timer = cgcx
745 .prof
746 .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
747 if unsafe { !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) }
748 {
749 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
750 }
751 save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
752 }
753
754 {
755 let _timer = cgcx
756 .prof
757 .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
758 if unsafe { !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) }
759 {
760 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
761 }
762 save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
763 }
764
765 {
766 let _timer =
767 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
768 if unsafe {
769 !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw())
770 } {
771 return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
772 }
773 save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
774 }
775
776 {
782 info!("running thin lto passes over {}", module.name);
783 run_pass_manager(cgcx, dcx, &mut module, true)?;
784 save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
785 }
786 }
787 Ok(module)
788}
789
790#[derive(Debug, Default)]
792struct ThinLTOKeysMap {
793 keys: BTreeMap<String, String>,
795}
796
797impl ThinLTOKeysMap {
798 fn save_to_file(&self, path: &Path) -> io::Result<()> {
799 use std::io::Write;
800 let mut writer = File::create_buffered(path)?;
801 for (module, key) in &self.keys {
804 writeln!(writer, "{module} {key}")?;
805 }
806 Ok(())
807 }
808
809 fn load_from_file(path: &Path) -> io::Result<Self> {
810 use std::io::BufRead;
811 let mut keys = BTreeMap::default();
812 let file = File::open_buffered(path)?;
813 for line in file.lines() {
814 let line = line?;
815 let mut split = line.split(' ');
816 let module = split.next().unwrap();
817 let key = split.next().unwrap();
818 assert_eq!(split.next(), None, "Expected two space-separated values, found {line:?}");
819 keys.insert(module.to_string(), key.to_string());
820 }
821 Ok(Self { keys })
822 }
823
824 fn from_thin_lto_modules(
825 data: &ThinData,
826 modules: &[llvm::ThinLTOModule],
827 names: &[CString],
828 ) -> Self {
829 let keys = iter::zip(modules, names)
830 .map(|(module, name)| {
831 let key = build_string(|rust_str| unsafe {
832 llvm::LLVMRustComputeLTOCacheKey(rust_str, module.identifier, data.0);
833 })
834 .expect("Invalid ThinLTO module key");
835 (module_name_to_str(name).to_string(), key)
836 })
837 .collect();
838 Self { keys }
839 }
840}
841
842fn module_name_to_str(c_str: &CStr) -> &str {
843 c_str.to_str().unwrap_or_else(|e| {
844 bug!("Encountered non-utf8 LLVM module name `{}`: {}", c_str.to_string_lossy(), e)
845 })
846}
847
848pub(crate) fn parse_module<'a>(
849 cx: &'a llvm::Context,
850 name: &CStr,
851 data: &[u8],
852 dcx: DiagCtxtHandle<'_>,
853) -> Result<&'a llvm::Module, FatalError> {
854 unsafe {
855 llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr())
856 .ok_or_else(|| write::llvm_err(dcx, LlvmError::ParseBitcode))
857 }
858}