-
Notifications
You must be signed in to change notification settings - Fork 12.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preliminary work for incremental ThinLTO. #52266
Changes from 3 commits
9df56ca
8dc7ddb
2c5cd9c
f6894eb
94b32ad
dd3f445
e045a6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,16 +20,23 @@ use rustc::hir::def_id::LOCAL_CRATE; | |
use rustc::middle::exported_symbols::SymbolExportLevel; | ||
use rustc::session::config::{self, Lto}; | ||
use rustc::util::common::time_ext; | ||
use rustc_data_structures::fx::FxHashMap; | ||
use time_graph::Timeline; | ||
use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource}; | ||
|
||
use libc; | ||
|
||
use std::ffi::CString; | ||
use std::ffi::{CString, CStr}; | ||
use std::fs::File; | ||
use std::io; | ||
use std::mem; | ||
use std::path::Path; | ||
use std::ptr; | ||
use std::slice; | ||
use std::sync::Arc; | ||
|
||
pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-imports.bin"; | ||
|
||
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { | ||
match crate_type { | ||
config::CrateTypeExecutable | | ||
|
@@ -193,7 +200,7 @@ pub(crate) fn run(cgcx: &CodegenContext, | |
} | ||
Lto::Thin | | ||
Lto::ThinLocal => { | ||
thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) | ||
thin_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) | ||
} | ||
Lto::No => unreachable!(), | ||
} | ||
|
@@ -346,7 +353,8 @@ impl Drop for Linker { | |
/// calculating the *index* for ThinLTO. This index will then be shared amongst | ||
/// all of the `LtoModuleCodegen` units returned below and destroyed once | ||
/// they all go out of scope. | ||
fn thin_lto(diag_handler: &Handler, | ||
fn thin_lto(cgcx: &CodegenContext, | ||
diag_handler: &Handler, | ||
modules: Vec<ModuleCodegen>, | ||
serialized_modules: Vec<(SerializedModule, CString)>, | ||
symbol_white_list: &[*const libc::c_char], | ||
|
@@ -424,6 +432,18 @@ fn thin_lto(diag_handler: &Handler, | |
let msg = format!("failed to prepare thin LTO context"); | ||
return Err(write::llvm_err(&diag_handler, msg)) | ||
} | ||
|
||
// Save the ThinLTO import information for incremental compilation. | ||
if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir { | ||
let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME); | ||
let imports = ThinLTOImports::from_thin_lto_data(data); | ||
if let Err(err) = imports.save_to_file(&path) { | ||
let msg = format!("Error while writing ThinLTO import data: {}", | ||
err); | ||
return Err(write::llvm_err(&diag_handler, msg)); | ||
} | ||
} | ||
|
||
let data = ThinData(data); | ||
info!("thin LTO data created"); | ||
timeline.record("data"); | ||
|
@@ -776,3 +796,129 @@ impl ThinModule { | |
Ok(module) | ||
} | ||
} | ||
|
||
|
||
#[derive(Debug)] | ||
pub struct ThinLTOImports { | ||
// key = llvm name of importing module, value = list of modules it imports from | ||
imports: FxHashMap<String, Vec<String>>, | ||
} | ||
|
||
impl ThinLTOImports { | ||
|
||
pub fn new_empty() -> ThinLTOImports { | ||
ThinLTOImports { | ||
imports: FxHashMap(), | ||
} | ||
} | ||
|
||
/// Load the ThinLTO import map from ThinLTOData. | ||
unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports { | ||
let raw_data: *const llvm::ThinLTOModuleImports = | ||
llvm::LLVMRustGetThinLTOModuleImports(data); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels sort of overly nasty in terms of converting a type from C++ to Rust, but then again I'm not sure of a better way to do this! In any case I do agree though that we'll want to free the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I don't like it either I thought I could keep the complex data structure on the C++ and provide a C interface but that quickly seemed like a lot of infrastructure for just passing a map around. I'll just implement a |
||
|
||
assert!(!raw_data.is_null()); | ||
|
||
let mut imports = FxHashMap(); | ||
let mut module_ptr = raw_data; | ||
let mut module_index = 0; | ||
|
||
loop { | ||
let mut entry_ptr: *const llvm::ThinLTOModuleName = *module_ptr; | ||
|
||
if entry_ptr.is_null() { | ||
break; | ||
} | ||
|
||
let importing_module_name = CStr::from_ptr(*entry_ptr) | ||
.to_str() | ||
.expect("Non-utf8 LLVM module name encountered") | ||
.to_owned(); | ||
|
||
entry_ptr = entry_ptr.offset(1); | ||
|
||
let mut imported_modules = vec![]; | ||
|
||
loop { | ||
let imported_module_name = *entry_ptr; | ||
|
||
if imported_module_name.is_null() { | ||
break | ||
} | ||
|
||
let imported_module_name = CStr::from_ptr(imported_module_name) | ||
.to_str() | ||
.expect("Non-utf8 LLVM module name encountered") | ||
.to_owned(); | ||
|
||
imported_modules.push(imported_module_name); | ||
entry_ptr = entry_ptr.offset(1); | ||
} | ||
|
||
imports.insert(importing_module_name, imported_modules); | ||
|
||
module_ptr = module_ptr.offset(1); | ||
module_index += 1; | ||
} | ||
|
||
assert_eq!(module_index, imports.len()); | ||
|
||
ThinLTOImports { | ||
imports | ||
} | ||
} | ||
|
||
pub fn save_to_file(&self, path: &Path) -> io::Result<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This + There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's still a JSON serializer in rustc-serialize but I don't really trust it. Serde would be great. |
||
use std::io::Write; | ||
|
||
let file = File::create(path)?; | ||
let mut writer = io::BufWriter::new(file); | ||
|
||
for (importing_module_name, imported_modules) in &self.imports { | ||
writeln!(writer, "{}", importing_module_name)?; | ||
|
||
for imported_module in imported_modules { | ||
writeln!(writer, " {}", imported_module)?; | ||
} | ||
|
||
writeln!(writer)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn load_from_file(path: &Path) -> io::Result<ThinLTOImports> { | ||
use std::io::BufRead; | ||
|
||
let mut imports = FxHashMap(); | ||
let mut current_module = None; | ||
let mut current_imports = vec![]; | ||
|
||
let file = File::open(path)?; | ||
|
||
for line in io::BufReader::new(file).lines() { | ||
let line = line?; | ||
|
||
if line.is_empty() { | ||
let importing_module = current_module | ||
.take() | ||
.expect("Importing module not set"); | ||
|
||
imports.insert(importing_module, | ||
mem::replace(&mut current_imports, vec![])); | ||
} else if line.starts_with(" ") { | ||
// This is an imported module | ||
assert_ne!(current_module, None); | ||
current_imports.push(line.trim().to_string()); | ||
} else { | ||
// This is the beginning of a new module | ||
assert_eq!(current_module, None); | ||
current_module = Some(line.trim().to_string()); | ||
} | ||
} | ||
|
||
Ok(ThinLTOImports { | ||
imports | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stylistically this could also be named
new
likeHashMap::new()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to be as explicit as possible. But I don't mind changing it to just
new()
.