Skip to content

Commit e91e0c7

Browse files
committed
The embedded bitcode should always be prepared for LTO/ThinLTO
1 parent fda6892 commit e91e0c7

File tree

9 files changed

+190
-50
lines changed

9 files changed

+190
-50
lines changed

compiler/rustc_codegen_llvm/src/back/lto.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,17 @@ pub(crate) fn run_pass_manager(
604604
debug!("running the pass manager");
605605
let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
606606
let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
607-
unsafe { write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage) }?;
607+
unsafe {
608+
write::llvm_optimize(
609+
cgcx,
610+
dcx,
611+
module.module_llvm.llmod(),
612+
&*module.module_llvm.tm,
613+
config,
614+
opt_level,
615+
opt_stage,
616+
)
617+
}?;
608618
debug!("lto done");
609619
Ok(())
610620
}

compiler/rustc_codegen_llvm/src/back/write.rs

+103-48
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
513513
pub(crate) unsafe fn llvm_optimize(
514514
cgcx: &CodegenContext<LlvmCodegenBackend>,
515515
dcx: DiagCtxtHandle<'_>,
516-
module: &ModuleCodegen<ModuleLlvm>,
516+
llmod: &llvm::Module,
517+
tm: &llvm::TargetMachine,
517518
config: &ModuleConfig,
518519
opt_level: config::OptLevel,
519520
opt_stage: llvm::OptStage,
@@ -572,8 +573,8 @@ pub(crate) unsafe fn llvm_optimize(
572573

573574
let result = unsafe {
574575
llvm::LLVMRustOptimize(
575-
module.module_llvm.llmod(),
576-
&*module.module_llvm.tm,
576+
llmod,
577+
tm,
577578
to_pass_builder_opt_level(opt_level),
578579
opt_stage,
579580
cgcx.opts.cg.linker_plugin_lto.enabled(),
@@ -635,8 +636,51 @@ pub(crate) unsafe fn optimize(
635636
_ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
636637
_ => llvm::OptStage::PreLinkNoLTO,
637638
};
638-
return unsafe { llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage) };
639+
if opt_stage == llvm::OptStage::PreLinkNoLTO
640+
&& config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)
641+
{
642+
let _timer = cgcx.prof.generic_activity_with_arg(
643+
"LLVM_module_codegen_prepare_embed_bitcode",
644+
&*module.name,
645+
);
646+
// The embedded bitcode is used to run LTO/ThinLTO.
647+
// `OptStage::PreLinkNoLTO` is not suitable as input for LTO,
648+
// as it may run certain passes that cannot be executed multiple times,
649+
// such as LLVM's Call Graph Profile pass. So, we create a copy to
650+
// run `OptStage::PreLinkThinLTO` for the subsequent LTO process.
651+
let llmod = unsafe { llvm::LLVMCloneModule(module.module_llvm.llmod()) };
652+
unsafe {
653+
llvm_optimize(
654+
cgcx,
655+
dcx,
656+
llmod,
657+
&*module.module_llvm.tm,
658+
config,
659+
opt_level,
660+
llvm::OptStage::PreLinkThinLTO,
661+
)
662+
}?;
663+
let embed_thin =
664+
ThinBuffer::new(llmod, config.emit_thin_lto, config.emit_thin_lto_summary);
665+
let thin_bc_out = cgcx.output_filenames.temp_path(OutputType::ThinBitcode, module_name);
666+
if let Err(err) = fs::write(&thin_bc_out, embed_thin.data()) {
667+
dcx.emit_err(WriteBytecode { path: &thin_bc_out, err });
668+
}
669+
unsafe { llvm::LLVMDisposeModule(llmod) };
670+
}
671+
unsafe {
672+
llvm_optimize(
673+
cgcx,
674+
dcx,
675+
module.module_llvm.llmod(),
676+
&*module.module_llvm.tm,
677+
config,
678+
opt_level,
679+
opt_stage,
680+
)
681+
}?;
639682
}
683+
640684
Ok(())
641685
}
642686

@@ -716,11 +760,54 @@ pub(crate) unsafe fn codegen(
716760
// asm from LLVM and use `gcc` to create the object file.
717761

718762
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
719-
let bc_summary_out =
720-
cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name);
721763
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
722764

765+
if config.emit_ir {
766+
let _timer =
767+
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
768+
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
769+
let out_c = path_to_c_string(&out);
770+
771+
extern "C" fn demangle_callback(
772+
input_ptr: *const c_char,
773+
input_len: size_t,
774+
output_ptr: *mut c_char,
775+
output_len: size_t,
776+
) -> size_t {
777+
let input =
778+
unsafe { slice::from_raw_parts(input_ptr as *const u8, input_len as usize) };
779+
780+
let Ok(input) = str::from_utf8(input) else { return 0 };
781+
782+
let output = unsafe {
783+
slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize)
784+
};
785+
let mut cursor = io::Cursor::new(output);
786+
787+
let Ok(demangled) = rustc_demangle::try_demangle(input) else { return 0 };
788+
789+
if write!(cursor, "{demangled:#}").is_err() {
790+
// Possible only if provided buffer is not big enough
791+
return 0;
792+
}
793+
794+
cursor.position() as size_t
795+
}
796+
797+
let result =
798+
unsafe { llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback) };
799+
800+
if result == llvm::LLVMRustResult::Success {
801+
record_artifact_size(&cgcx.prof, "llvm_ir", &out);
802+
}
803+
804+
result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteIr { path: &out }))?;
805+
}
806+
723807
if config.bitcode_needed() {
808+
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
809+
let bc_summary_out =
810+
cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name);
724811
let _timer = cgcx
725812
.prof
726813
.generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name);
@@ -767,54 +854,22 @@ pub(crate) unsafe fn codegen(
767854
let _timer = cgcx
768855
.prof
769856
.generic_activity_with_arg("LLVM_module_codegen_embed_bitcode", &*module.name);
857+
let thin_bc_out =
858+
cgcx.output_filenames.temp_path(OutputType::ThinBitcode, module_name);
859+
let thin_data;
860+
let mut data = data;
861+
if thin_bc_out.exists() {
862+
thin_data = fs::read(&thin_bc_out).unwrap();
863+
debug!("removing embed bitcode file {:?}", thin_bc_out);
864+
ensure_removed(dcx, &thin_bc_out);
865+
data = thin_data.as_slice();
866+
}
770867
unsafe {
771868
embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data);
772869
}
773870
}
774871
}
775872

776-
if config.emit_ir {
777-
let _timer =
778-
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_ir", &*module.name);
779-
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
780-
let out_c = path_to_c_string(&out);
781-
782-
extern "C" fn demangle_callback(
783-
input_ptr: *const c_char,
784-
input_len: size_t,
785-
output_ptr: *mut c_char,
786-
output_len: size_t,
787-
) -> size_t {
788-
let input =
789-
unsafe { slice::from_raw_parts(input_ptr as *const u8, input_len as usize) };
790-
791-
let Ok(input) = str::from_utf8(input) else { return 0 };
792-
793-
let output = unsafe {
794-
slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize)
795-
};
796-
let mut cursor = io::Cursor::new(output);
797-
798-
let Ok(demangled) = rustc_demangle::try_demangle(input) else { return 0 };
799-
800-
if write!(cursor, "{demangled:#}").is_err() {
801-
// Possible only if provided buffer is not big enough
802-
return 0;
803-
}
804-
805-
cursor.position() as size_t
806-
}
807-
808-
let result =
809-
unsafe { llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback) };
810-
811-
if result == llvm::LLVMRustResult::Success {
812-
record_artifact_size(&cgcx.prof, "llvm_ir", &out);
813-
}
814-
815-
result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteIr { path: &out }))?;
816-
}
817-
818873
if config.emit_asm {
819874
let _timer =
820875
cgcx.prof.generic_activity_with_arg("LLVM_module_codegen_emit_asm", &*module.name);

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@ unsafe extern "C" {
870870
pub fn LLVMModuleCreateWithNameInContext(ModuleID: *const c_char, C: &Context) -> &Module;
871871
pub fn LLVMGetModuleContext(M: &Module) -> &Context;
872872
pub fn LLVMCloneModule(M: &Module) -> &Module;
873+
pub fn LLVMDisposeModule(M: &Module);
873874

874875
/// Data layout. See Module::getDataLayout.
875876
pub fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char;

compiler/rustc_codegen_ssa/src/back/write.rs

+3
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,9 @@ fn produce_final_output_artifacts(
618618
// them for making an rlib.
619619
copy_if_one_unit(OutputType::Bitcode, true);
620620
}
621+
OutputType::ThinBitcode => {
622+
copy_if_one_unit(OutputType::ThinBitcode, true);
623+
}
621624
OutputType::ThinLinkBitcode => {
622625
copy_if_one_unit(OutputType::ThinLinkBitcode, false);
623626
}

compiler/rustc_session/src/config.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ impl FromStr for SplitDwarfKind {
508508
pub enum OutputType {
509509
Bitcode,
510510
ThinLinkBitcode,
511+
ThinBitcode,
511512
Assembly,
512513
LlvmAssembly,
513514
Mir,
@@ -538,6 +539,7 @@ impl OutputType {
538539
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
539540
OutputType::Bitcode
540541
| OutputType::ThinLinkBitcode
542+
| OutputType::ThinBitcode
541543
| OutputType::Assembly
542544
| OutputType::LlvmAssembly
543545
| OutputType::Mir
@@ -549,6 +551,7 @@ impl OutputType {
549551
match *self {
550552
OutputType::Bitcode => "llvm-bc",
551553
OutputType::ThinLinkBitcode => "thin-link-bitcode",
554+
OutputType::ThinBitcode => "thin-llvm-bc",
552555
OutputType::Assembly => "asm",
553556
OutputType::LlvmAssembly => "llvm-ir",
554557
OutputType::Mir => "mir",
@@ -566,6 +569,7 @@ impl OutputType {
566569
"mir" => OutputType::Mir,
567570
"llvm-bc" => OutputType::Bitcode,
568571
"thin-link-bitcode" => OutputType::ThinLinkBitcode,
572+
"thin-llvm-bc" => OutputType::ThinBitcode,
569573
"obj" => OutputType::Object,
570574
"metadata" => OutputType::Metadata,
571575
"link" => OutputType::Exe,
@@ -576,9 +580,10 @@ impl OutputType {
576580

577581
fn shorthands_display() -> String {
578582
format!(
579-
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
583+
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}",
580584
OutputType::Bitcode.shorthand(),
581585
OutputType::ThinLinkBitcode.shorthand(),
586+
OutputType::ThinBitcode.shorthand(),
582587
OutputType::Assembly.shorthand(),
583588
OutputType::LlvmAssembly.shorthand(),
584589
OutputType::Mir.shorthand(),
@@ -593,6 +598,7 @@ impl OutputType {
593598
match *self {
594599
OutputType::Bitcode => "bc",
595600
OutputType::ThinLinkBitcode => "indexing.o",
601+
OutputType::ThinBitcode => "thin.bc",
596602
OutputType::Assembly => "s",
597603
OutputType::LlvmAssembly => "ll",
598604
OutputType::Mir => "mir",
@@ -611,6 +617,7 @@ impl OutputType {
611617
| OutputType::DepInfo => true,
612618
OutputType::Bitcode
613619
| OutputType::ThinLinkBitcode
620+
| OutputType::ThinBitcode
614621
| OutputType::Object
615622
| OutputType::Metadata
616623
| OutputType::Exe => false,
@@ -698,6 +705,7 @@ impl OutputTypes {
698705
self.0.keys().any(|k| match *k {
699706
OutputType::Bitcode
700707
| OutputType::ThinLinkBitcode
708+
| OutputType::ThinBitcode
701709
| OutputType::Assembly
702710
| OutputType::LlvmAssembly
703711
| OutputType::Mir
@@ -712,6 +720,7 @@ impl OutputTypes {
712720
self.0.keys().any(|k| match *k {
713721
OutputType::Bitcode
714722
| OutputType::ThinLinkBitcode
723+
| OutputType::ThinBitcode
715724
| OutputType::Assembly
716725
| OutputType::LlvmAssembly
717726
| OutputType::Mir
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![crate_name = "interesting"]
2+
#![crate_type = "rlib"]
3+
4+
extern crate opaque;
5+
6+
#[no_mangle]
7+
#[inline(never)]
8+
pub fn function_called_once() {
9+
opaque::foo();
10+
}
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern crate interesting;
2+
3+
fn main() {
4+
interesting::function_called_once();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![crate_name = "opaque"]
2+
#![crate_type = "rlib"]
3+
4+
#[inline(never)]
5+
pub fn foo() {}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// This test case verifies that we successfully complete an LTO build with PGO
2+
// using the embedded bitcode.
3+
// It also ensures that the generated IR correctly includes the call results.
4+
5+
//@ needs-profiler-runtime
6+
//@ ignore-cross-compile
7+
8+
use std::path::Path;
9+
10+
use run_make_support::{llvm_filecheck, llvm_profdata, rfs, run, rustc};
11+
12+
fn main() {
13+
let path_prof_data_dir = Path::new("prof_data_dir");
14+
let path_merged_profdata = path_prof_data_dir.join("merged.profdata");
15+
rustc().input("opaque.rs").codegen_units(1).run();
16+
rfs::create_dir_all(&path_prof_data_dir);
17+
rustc()
18+
.input("interesting.rs")
19+
.profile_generate(&path_prof_data_dir)
20+
.opt()
21+
.codegen_units(1)
22+
.run();
23+
rustc()
24+
.input("main.rs")
25+
.arg("-Clto=thin")
26+
.opt()
27+
.codegen_units(1)
28+
.profile_generate(&path_prof_data_dir)
29+
.opt()
30+
.run();
31+
run("main");
32+
llvm_profdata().merge().output(&path_merged_profdata).input(path_prof_data_dir).run();
33+
rustc().input("interesting.rs").profile_use(&path_merged_profdata).opt().codegen_units(1).run();
34+
rustc()
35+
.input("main.rs")
36+
.arg("-Clto=thin")
37+
.opt()
38+
.codegen_units(1)
39+
.profile_use(&path_merged_profdata)
40+
.opt()
41+
.run();
42+
}

0 commit comments

Comments
 (0)