mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
rustc_codegen_llvm: add support for writing summary bitcode
Typical uses of ThinLTO don't have any use for this as a standalone file, but distributed ThinLTO uses this to make the linker phase more efficient. With clang you'd do something like `clang -flto=thin -fthin-link-bitcode=foo.indexing.o -c foo.c` and then get both foo.o (full of bitcode) and foo.indexing.o (just the summary or index part of the bitcode). That's then usable by a two-stage linking process that's more friendly to distributed build systems like bazel, which is why I'm working on this area. I talked some to @teresajohnson about naming in this area, as things seem to be a little confused between various blog posts and build systems. "bitcode index" and "bitcode summary" tend to be a little too ambiguous, and she tends to use "thin link bitcode" and "minimized bitcode" (which matches the descriptions in LLVM). Since the clang option is thin-link-bitcode, I went with that to try and not add a new spelling in the world. Per @dtolnay, you can work around the lack of this by using `lld --thinlto-index-only` to do the indexing on regular .o files of bitcode, but that is a bit wasteful on actions when we already have all the information in rustc and could just write out the matching minimized bitcode. I didn't test that at all in our infrastructure, because by the time I learned that I already had this patch largely written.
This commit is contained in:
parent
e8fbd99128
commit
aa91871539
@ -200,7 +200,7 @@ fn produce_final_output_artifacts(
|
|||||||
// to get rid of it.
|
// to get rid of it.
|
||||||
for output_type in crate_output.outputs.keys() {
|
for output_type in crate_output.outputs.keys() {
|
||||||
match *output_type {
|
match *output_type {
|
||||||
OutputType::Bitcode => {
|
OutputType::Bitcode | OutputType::ThinLinkBitcode => {
|
||||||
// Cranelift doesn't have bitcode
|
// Cranelift doesn't have bitcode
|
||||||
// user_wants_bitcode = true;
|
// user_wants_bitcode = true;
|
||||||
// // Copy to .bc, but always keep the .0.bc. There is a later
|
// // Copy to .bc, but always keep the .0.bc. There is a later
|
||||||
|
@ -335,6 +335,10 @@ impl ThinBufferMethods for ThinBuffer {
|
|||||||
fn data(&self) -> &[u8] {
|
fn data(&self) -> &[u8] {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn thin_link_data(&self) -> &[u8] {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GccContext {
|
pub struct GccContext {
|
||||||
|
@ -687,6 +687,14 @@ impl ThinBufferMethods for ThinBuffer {
|
|||||||
slice::from_raw_parts(ptr, len)
|
slice::from_raw_parts(ptr, len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn thin_link_data(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
let ptr = llvm::LLVMRustThinLTOBufferThinLinkDataPtr(self.0) as *const _;
|
||||||
|
let len = llvm::LLVMRustThinLTOBufferThinLinkDataLen(self.0);
|
||||||
|
slice::from_raw_parts(ptr, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ThinBuffer {
|
impl Drop for ThinBuffer {
|
||||||
|
@ -708,6 +708,8 @@ pub(crate) unsafe fn codegen(
|
|||||||
// asm from LLVM and use `gcc` to create the object file.
|
// asm from LLVM and use `gcc` to create the object file.
|
||||||
|
|
||||||
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
||||||
|
let bc_index_out =
|
||||||
|
cgcx.output_filenames.temp_path(OutputType::ThinLinkBitcode, module_name);
|
||||||
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
||||||
|
|
||||||
if config.bitcode_needed() {
|
if config.bitcode_needed() {
|
||||||
@ -716,6 +718,7 @@ pub(crate) unsafe fn codegen(
|
|||||||
.generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name);
|
.generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &*module.name);
|
||||||
let thin = ThinBuffer::new(llmod, config.emit_thin_lto);
|
let thin = ThinBuffer::new(llmod, config.emit_thin_lto);
|
||||||
let data = thin.data();
|
let data = thin.data();
|
||||||
|
let index_data = thin.thin_link_data();
|
||||||
|
|
||||||
if let Some(bitcode_filename) = bc_out.file_name() {
|
if let Some(bitcode_filename) = bc_out.file_name() {
|
||||||
cgcx.prof.artifact_size(
|
cgcx.prof.artifact_size(
|
||||||
@ -725,14 +728,33 @@ pub(crate) unsafe fn codegen(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(thin_link_bitcode_filename) = bc_index_out.file_name() {
|
||||||
|
cgcx.prof.artifact_size(
|
||||||
|
"llvm_bitcode_summary",
|
||||||
|
thin_link_bitcode_filename.to_string_lossy(),
|
||||||
|
index_data.len() as u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _timer = cgcx.prof.generic_activity_with_arg(
|
||||||
|
"LLVM_module_codegen_emit_bitcode_index",
|
||||||
|
&*module.name,
|
||||||
|
);
|
||||||
|
if let Err(err) = fs::write(&bc_index_out, index_data) {
|
||||||
|
dcx.emit_err(WriteBytecode { path: &bc_index_out, err });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
if config.emit_bc || config.emit_obj == EmitObj::Bitcode {
|
||||||
let _timer = cgcx
|
{
|
||||||
.prof
|
let _timer = cgcx.prof.generic_activity_with_arg(
|
||||||
.generic_activity_with_arg("LLVM_module_codegen_emit_bitcode", &*module.name);
|
"LLVM_module_codegen_emit_bitcode",
|
||||||
|
&*module.name,
|
||||||
|
);
|
||||||
if let Err(err) = fs::write(&bc_out, data) {
|
if let Err(err) = fs::write(&bc_out, data) {
|
||||||
dcx.emit_err(WriteBytecode { path: &bc_out, err });
|
dcx.emit_err(WriteBytecode { path: &bc_out, err });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
|
if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) {
|
||||||
let _timer = cgcx
|
let _timer = cgcx
|
||||||
|
@ -2354,6 +2354,8 @@ extern "C" {
|
|||||||
pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer);
|
pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer);
|
||||||
pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char;
|
pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char;
|
||||||
pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t;
|
pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t;
|
||||||
|
pub fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char;
|
||||||
|
pub fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t;
|
||||||
pub fn LLVMRustCreateThinLTOData(
|
pub fn LLVMRustCreateThinLTOData(
|
||||||
Modules: *const ThinLTOModule,
|
Modules: *const ThinLTOModule,
|
||||||
NumModules: c_uint,
|
NumModules: c_uint,
|
||||||
|
@ -107,6 +107,7 @@ pub struct ModuleConfig {
|
|||||||
pub emit_asm: bool,
|
pub emit_asm: bool,
|
||||||
pub emit_obj: EmitObj,
|
pub emit_obj: EmitObj,
|
||||||
pub emit_thin_lto: bool,
|
pub emit_thin_lto: bool,
|
||||||
|
pub emit_thin_lto_index: bool,
|
||||||
pub bc_cmdline: String,
|
pub bc_cmdline: String,
|
||||||
|
|
||||||
// Miscellaneous flags. These are mostly copied from command-line
|
// Miscellaneous flags. These are mostly copied from command-line
|
||||||
@ -231,6 +232,10 @@ impl ModuleConfig {
|
|||||||
),
|
),
|
||||||
emit_obj,
|
emit_obj,
|
||||||
emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto,
|
emit_thin_lto: sess.opts.unstable_opts.emit_thin_lto,
|
||||||
|
emit_thin_lto_index: if_regular!(
|
||||||
|
sess.opts.output_types.contains_key(&OutputType::ThinLinkBitcode),
|
||||||
|
false
|
||||||
|
),
|
||||||
bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(),
|
bc_cmdline: sess.target.bitcode_llvm_cmdline.to_string(),
|
||||||
|
|
||||||
verify_llvm_ir: sess.verify_llvm_ir(),
|
verify_llvm_ir: sess.verify_llvm_ir(),
|
||||||
@ -282,6 +287,7 @@ impl ModuleConfig {
|
|||||||
|
|
||||||
pub fn bitcode_needed(&self) -> bool {
|
pub fn bitcode_needed(&self) -> bool {
|
||||||
self.emit_bc
|
self.emit_bc
|
||||||
|
|| self.emit_thin_lto_index
|
||||||
|| self.emit_obj == EmitObj::Bitcode
|
|| self.emit_obj == EmitObj::Bitcode
|
||||||
|| self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)
|
|| self.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full)
|
||||||
}
|
}
|
||||||
@ -629,6 +635,9 @@ fn produce_final_output_artifacts(
|
|||||||
// them for making an rlib.
|
// them for making an rlib.
|
||||||
copy_if_one_unit(OutputType::Bitcode, true);
|
copy_if_one_unit(OutputType::Bitcode, true);
|
||||||
}
|
}
|
||||||
|
OutputType::ThinLinkBitcode => {
|
||||||
|
copy_if_one_unit(OutputType::ThinLinkBitcode, false);
|
||||||
|
}
|
||||||
OutputType::LlvmAssembly => {
|
OutputType::LlvmAssembly => {
|
||||||
copy_if_one_unit(OutputType::LlvmAssembly, false);
|
copy_if_one_unit(OutputType::LlvmAssembly, false);
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
|
|||||||
|
|
||||||
pub trait ThinBufferMethods: Send + Sync {
|
pub trait ThinBufferMethods: Send + Sync {
|
||||||
fn data(&self) -> &[u8];
|
fn data(&self) -> &[u8];
|
||||||
|
fn thin_link_data(&self) -> &[u8];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ModuleBufferMethods: Send + Sync {
|
pub trait ModuleBufferMethods: Send + Sync {
|
||||||
|
@ -1488,6 +1488,7 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M,
|
|||||||
// a ThinLTO summary attached.
|
// a ThinLTO summary attached.
|
||||||
struct LLVMRustThinLTOBuffer {
|
struct LLVMRustThinLTOBuffer {
|
||||||
std::string data;
|
std::string data;
|
||||||
|
std::string thin_link_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" LLVMRustThinLTOBuffer*
|
extern "C" LLVMRustThinLTOBuffer*
|
||||||
@ -1495,6 +1496,7 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) {
|
|||||||
auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
|
auto Ret = std::make_unique<LLVMRustThinLTOBuffer>();
|
||||||
{
|
{
|
||||||
auto OS = raw_string_ostream(Ret->data);
|
auto OS = raw_string_ostream(Ret->data);
|
||||||
|
auto ThinLinkOS = raw_string_ostream(Ret->thin_link_data);
|
||||||
{
|
{
|
||||||
if (is_thin) {
|
if (is_thin) {
|
||||||
PassBuilder PB;
|
PassBuilder PB;
|
||||||
@ -1508,7 +1510,7 @@ LLVMRustThinLTOBufferCreate(LLVMModuleRef M, bool is_thin) {
|
|||||||
PB.registerLoopAnalyses(LAM);
|
PB.registerLoopAnalyses(LAM);
|
||||||
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
|
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
|
||||||
ModulePassManager MPM;
|
ModulePassManager MPM;
|
||||||
MPM.addPass(ThinLTOBitcodeWriterPass(OS, nullptr));
|
MPM.addPass(ThinLTOBitcodeWriterPass(OS, &ThinLinkOS));
|
||||||
MPM.run(*unwrap(M), MAM);
|
MPM.run(*unwrap(M), MAM);
|
||||||
} else {
|
} else {
|
||||||
WriteBitcodeToFile(*unwrap(M), OS);
|
WriteBitcodeToFile(*unwrap(M), OS);
|
||||||
@ -1533,6 +1535,16 @@ LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) {
|
|||||||
return Buffer->data.length();
|
return Buffer->data.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" const void*
|
||||||
|
LLVMRustThinLTOBufferThinLinkDataPtr(const LLVMRustThinLTOBuffer *Buffer) {
|
||||||
|
return Buffer->thin_link_data.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" size_t
|
||||||
|
LLVMRustThinLTOBufferThinLinkDataLen(const LLVMRustThinLTOBuffer *Buffer) {
|
||||||
|
return Buffer->thin_link_data.length();
|
||||||
|
}
|
||||||
|
|
||||||
// This is what we used to parse upstream bitcode for actual ThinLTO
|
// This is what we used to parse upstream bitcode for actual ThinLTO
|
||||||
// processing. We'll call this once per module optimized through ThinLTO, and
|
// processing. We'll call this once per module optimized through ThinLTO, and
|
||||||
// it'll be called concurrently on many threads.
|
// it'll be called concurrently on many threads.
|
||||||
|
@ -465,6 +465,7 @@ impl FromStr for SplitDwarfKind {
|
|||||||
#[derive(Encodable, Decodable)]
|
#[derive(Encodable, Decodable)]
|
||||||
pub enum OutputType {
|
pub enum OutputType {
|
||||||
Bitcode,
|
Bitcode,
|
||||||
|
ThinLinkBitcode,
|
||||||
Assembly,
|
Assembly,
|
||||||
LlvmAssembly,
|
LlvmAssembly,
|
||||||
Mir,
|
Mir,
|
||||||
@ -492,6 +493,7 @@ impl OutputType {
|
|||||||
match *self {
|
match *self {
|
||||||
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
|
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
|
||||||
OutputType::Bitcode
|
OutputType::Bitcode
|
||||||
|
| OutputType::ThinLinkBitcode
|
||||||
| OutputType::Assembly
|
| OutputType::Assembly
|
||||||
| OutputType::LlvmAssembly
|
| OutputType::LlvmAssembly
|
||||||
| OutputType::Mir
|
| OutputType::Mir
|
||||||
@ -502,6 +504,7 @@ impl OutputType {
|
|||||||
pub fn shorthand(&self) -> &'static str {
|
pub fn shorthand(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
OutputType::Bitcode => "llvm-bc",
|
OutputType::Bitcode => "llvm-bc",
|
||||||
|
OutputType::ThinLinkBitcode => "thin-link-bitcode",
|
||||||
OutputType::Assembly => "asm",
|
OutputType::Assembly => "asm",
|
||||||
OutputType::LlvmAssembly => "llvm-ir",
|
OutputType::LlvmAssembly => "llvm-ir",
|
||||||
OutputType::Mir => "mir",
|
OutputType::Mir => "mir",
|
||||||
@ -518,6 +521,7 @@ impl OutputType {
|
|||||||
"llvm-ir" => OutputType::LlvmAssembly,
|
"llvm-ir" => OutputType::LlvmAssembly,
|
||||||
"mir" => OutputType::Mir,
|
"mir" => OutputType::Mir,
|
||||||
"llvm-bc" => OutputType::Bitcode,
|
"llvm-bc" => OutputType::Bitcode,
|
||||||
|
"thin-link-bitcode" => OutputType::ThinLinkBitcode,
|
||||||
"obj" => OutputType::Object,
|
"obj" => OutputType::Object,
|
||||||
"metadata" => OutputType::Metadata,
|
"metadata" => OutputType::Metadata,
|
||||||
"link" => OutputType::Exe,
|
"link" => OutputType::Exe,
|
||||||
@ -528,8 +532,9 @@ impl OutputType {
|
|||||||
|
|
||||||
fn shorthands_display() -> String {
|
fn shorthands_display() -> String {
|
||||||
format!(
|
format!(
|
||||||
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
|
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
|
||||||
OutputType::Bitcode.shorthand(),
|
OutputType::Bitcode.shorthand(),
|
||||||
|
OutputType::ThinLinkBitcode.shorthand(),
|
||||||
OutputType::Assembly.shorthand(),
|
OutputType::Assembly.shorthand(),
|
||||||
OutputType::LlvmAssembly.shorthand(),
|
OutputType::LlvmAssembly.shorthand(),
|
||||||
OutputType::Mir.shorthand(),
|
OutputType::Mir.shorthand(),
|
||||||
@ -543,6 +548,7 @@ impl OutputType {
|
|||||||
pub fn extension(&self) -> &'static str {
|
pub fn extension(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
OutputType::Bitcode => "bc",
|
OutputType::Bitcode => "bc",
|
||||||
|
OutputType::ThinLinkBitcode => "indexing.o",
|
||||||
OutputType::Assembly => "s",
|
OutputType::Assembly => "s",
|
||||||
OutputType::LlvmAssembly => "ll",
|
OutputType::LlvmAssembly => "ll",
|
||||||
OutputType::Mir => "mir",
|
OutputType::Mir => "mir",
|
||||||
@ -559,9 +565,11 @@ impl OutputType {
|
|||||||
| OutputType::LlvmAssembly
|
| OutputType::LlvmAssembly
|
||||||
| OutputType::Mir
|
| OutputType::Mir
|
||||||
| OutputType::DepInfo => true,
|
| OutputType::DepInfo => true,
|
||||||
OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => {
|
OutputType::Bitcode
|
||||||
false
|
| OutputType::ThinLinkBitcode
|
||||||
}
|
| OutputType::Object
|
||||||
|
| OutputType::Metadata
|
||||||
|
| OutputType::Exe => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -644,6 +652,7 @@ impl OutputTypes {
|
|||||||
pub fn should_codegen(&self) -> bool {
|
pub fn should_codegen(&self) -> bool {
|
||||||
self.0.keys().any(|k| match *k {
|
self.0.keys().any(|k| match *k {
|
||||||
OutputType::Bitcode
|
OutputType::Bitcode
|
||||||
|
| OutputType::ThinLinkBitcode
|
||||||
| OutputType::Assembly
|
| OutputType::Assembly
|
||||||
| OutputType::LlvmAssembly
|
| OutputType::LlvmAssembly
|
||||||
| OutputType::Mir
|
| OutputType::Mir
|
||||||
@ -657,6 +666,7 @@ impl OutputTypes {
|
|||||||
pub fn should_link(&self) -> bool {
|
pub fn should_link(&self) -> bool {
|
||||||
self.0.keys().any(|k| match *k {
|
self.0.keys().any(|k| match *k {
|
||||||
OutputType::Bitcode
|
OutputType::Bitcode
|
||||||
|
| OutputType::ThinLinkBitcode
|
||||||
| OutputType::Assembly
|
| OutputType::Assembly
|
||||||
| OutputType::LlvmAssembly
|
| OutputType::LlvmAssembly
|
||||||
| OutputType::Mir
|
| OutputType::Mir
|
||||||
@ -1769,6 +1779,12 @@ fn parse_output_types(
|
|||||||
display = OutputType::shorthands_display(),
|
display = OutputType::shorthands_display(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
if output_type == OutputType::ThinLinkBitcode && !unstable_opts.unstable_options {
|
||||||
|
early_dcx.early_fatal(format!(
|
||||||
|
"{} requested but -Zunstable-options not specified",
|
||||||
|
OutputType::ThinLinkBitcode.shorthand()
|
||||||
|
));
|
||||||
|
}
|
||||||
output_types.insert(output_type, path);
|
output_types.insert(output_type, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user