Skip to content

Commit 694adee

Browse files
committed
Auto merge of #42682 - alexcrichton:jobserver, r=michaelwoerister
Integrate jobserver support to parallel codegen This commit integrates the `jobserver` crate into the compiler. The crate was previously integrated in to Cargo as part of rust-lang/cargo#4110. The purpose here is to two-fold: * Primarily the compiler can cooperate with Cargo on parallelism. When you run `cargo build -j4` then this'll make sure that the entire build process between Cargo/rustc won't use more than 4 cores, whereas today you'd get 4 rustc instances which may all try to spawn lots of threads. * Secondarily rustc/Cargo can now integrate with a foreign GNU `make` jobserver. This means that if you call cargo/rustc from `make` or another jobserver-compatible implementation it'll use foreign parallelism settings instead of creating new ones locally. As the number of parallel codegen instances in the compiler continues to grow over time with the advent of incremental compilation it's expected that this'll become more of a problem, so this is intended to nip concurrent concerns in the bud by having all the tools to cooperate! Note that while rustc has support for itself creating a jobserver it's far more likely that rustc will always use the jobserver configured by Cargo. Cargo today will now set a jobserver unconditionally for rustc to use.
2 parents 622e7e6 + 201f069 commit 694adee

19 files changed

+514
-311
lines changed

src/Cargo.lock

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/libcore/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ path = "lib.rs"
99
test = false
1010
bench = false
1111

12+
[dev-dependencies]
13+
rand = { path = "../librand" }
14+
1215
[[test]]
1316
name = "coretests"
1417
path = "../libcore/tests/lib.rs"

src/librustc/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ crate-type = ["dylib"]
1212
arena = { path = "../libarena" }
1313
fmt_macros = { path = "../libfmt_macros" }
1414
graphviz = { path = "../libgraphviz" }
15+
jobserver = "0.1"
1516
log = "0.3"
1617
owning_ref = "0.3.3"
1718
rustc_back = { path = "../librustc_back" }

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern crate rustc_errors as errors;
6161
#[macro_use] extern crate syntax;
6262
extern crate syntax_pos;
6363
#[macro_use] #[no_link] extern crate rustc_bitflags;
64+
extern crate jobserver;
6465

6566
extern crate serialize as rustc_serialize; // used by deriving
6667

src/librustc/session/mod.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,16 @@ use syntax_pos::{Span, MultiSpan};
3838
use rustc_back::{LinkerFlavor, PanicStrategy};
3939
use rustc_back::target::Target;
4040
use rustc_data_structures::flock;
41+
use jobserver::Client;
4142

42-
use std::path::{Path, PathBuf};
4343
use std::cell::{self, Cell, RefCell};
4444
use std::collections::HashMap;
4545
use std::env;
46+
use std::fmt;
4647
use std::io::Write;
48+
use std::path::{Path, PathBuf};
4749
use std::rc::Rc;
48-
use std::fmt;
50+
use std::sync::{Once, ONCE_INIT};
4951
use std::time::Duration;
5052

5153
mod code_stats;
@@ -134,6 +136,10 @@ pub struct Session {
134136
pub print_fuel_crate: Option<String>,
135137
/// Always set to zero and incremented so that we can print fuel expended by a crate.
136138
pub print_fuel: Cell<u64>,
139+
140+
/// Loaded up early on in the initialization of this `Session` to avoid
141+
/// false positives about a job server in our environment.
142+
pub jobserver_from_env: Option<Client>,
137143
}
138144

139145
pub struct PerfStats {
@@ -697,6 +703,24 @@ pub fn build_session_(sopts: config::Options,
697703
print_fuel_crate: print_fuel_crate,
698704
print_fuel: print_fuel,
699705
out_of_fuel: Cell::new(false),
706+
707+
// Note that this is unsafe because it may misinterpret file descriptors
708+
// on Unix as jobserver file descriptors. We hopefully execute this near
709+
// the beginning of the process though to ensure we don't get false
710+
// positives, or in other words we try to execute this before we open
711+
// any file descriptors ourselves.
712+
//
713+
// Also note that we stick this in a global because there could be
714+
// multiple `Session` instances in this process, and the jobserver is
715+
// per-process.
716+
jobserver_from_env: unsafe {
717+
static mut GLOBAL_JOBSERVER: *mut Option<Client> = 0 as *mut _;
718+
static INIT: Once = ONCE_INIT;
719+
INIT.call_once(|| {
720+
GLOBAL_JOBSERVER = Box::into_raw(Box::new(Client::from_env()));
721+
});
722+
(*GLOBAL_JOBSERVER).clone()
723+
},
700724
};
701725

702726
sess

src/librustc_trans/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ crate-type = ["dylib"]
1010
test = false
1111

1212
[dependencies]
13+
crossbeam = "0.2"
1314
flate2 = "0.2"
15+
jobserver = "0.1.5"
1416
log = "0.3"
1517
owning_ref = "0.3.3"
1618
rustc = { path = "../librustc" }

src/librustc_trans/back/link.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -329,34 +329,38 @@ pub fn filename_for_input(sess: &Session,
329329
}
330330

331331
pub fn each_linked_rlib(sess: &Session,
332-
f: &mut FnMut(CrateNum, &Path)) {
332+
f: &mut FnMut(CrateNum, &Path)) -> Result<(), String> {
333333
let crates = sess.cstore.used_crates(LinkagePreference::RequireStatic).into_iter();
334334
let fmts = sess.dependency_formats.borrow();
335335
let fmts = fmts.get(&config::CrateTypeExecutable)
336336
.or_else(|| fmts.get(&config::CrateTypeStaticlib))
337337
.or_else(|| fmts.get(&config::CrateTypeCdylib))
338338
.or_else(|| fmts.get(&config::CrateTypeProcMacro));
339-
let fmts = fmts.unwrap_or_else(|| {
340-
bug!("could not find formats for rlibs");
341-
});
339+
let fmts = match fmts {
340+
Some(f) => f,
341+
None => return Err(format!("could not find formats for rlibs"))
342+
};
342343
for (cnum, path) in crates {
343-
match fmts[cnum.as_usize() - 1] {
344-
Linkage::NotLinked | Linkage::IncludedFromDylib => continue,
345-
_ => {}
344+
match fmts.get(cnum.as_usize() - 1) {
345+
Some(&Linkage::NotLinked) |
346+
Some(&Linkage::IncludedFromDylib) => continue,
347+
Some(_) => {}
348+
None => return Err(format!("could not find formats for rlibs"))
346349
}
347350
let name = sess.cstore.crate_name(cnum).clone();
348351
let path = match path {
349352
LibSource::Some(p) => p,
350353
LibSource::MetadataOnly => {
351-
sess.fatal(&format!("could not find rlib for: `{}`, found rmeta (metadata) file",
352-
name));
354+
return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file",
355+
name))
353356
}
354357
LibSource::None => {
355-
sess.fatal(&format!("could not find rlib for: `{}`", name));
358+
return Err(format!("could not find rlib for: `{}`", name))
356359
}
357360
};
358361
f(cnum, &path);
359362
}
363+
Ok(())
360364
}
361365

362366
fn out_filename(sess: &Session,
@@ -669,7 +673,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
669673
let mut ab = link_rlib(sess, None, objects, out_filename, tempdir);
670674
let mut all_native_libs = vec![];
671675

672-
each_linked_rlib(sess, &mut |cnum, path| {
676+
let res = each_linked_rlib(sess, &mut |cnum, path| {
673677
let name = sess.cstore.crate_name(cnum);
674678
let native_libs = sess.cstore.native_libraries(cnum);
675679

@@ -694,6 +698,9 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
694698

695699
all_native_libs.extend(sess.cstore.native_libraries(cnum));
696700
});
701+
if let Err(e) = res {
702+
sess.fatal(&e);
703+
}
697704

698705
ab.update_symbols();
699706
ab.build();

src/librustc_trans/back/lto.rs

+50-47
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010

1111
use back::link;
1212
use back::write;
13-
use back::symbol_export::{self, ExportedSymbols};
14-
use rustc::session::{self, config};
13+
use back::symbol_export;
14+
use rustc::session::config;
15+
use errors::FatalError;
1516
use llvm;
1617
use llvm::archive_ro::ArchiveRO;
1718
use llvm::{ModuleRef, TargetMachineRef, True, False};
1819
use rustc::util::common::time;
1920
use rustc::util::common::path2cstr;
2021
use rustc::hir::def_id::LOCAL_CRATE;
21-
use back::write::{ModuleConfig, with_llvm_pmb};
22+
use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext};
2223

2324
use libc;
2425
use flate2::read::ZlibDecoder;
@@ -39,30 +40,31 @@ pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
3940
}
4041
}
4142

42-
pub fn run(sess: &session::Session,
43+
pub fn run(cgcx: &CodegenContext,
4344
llmod: ModuleRef,
4445
tm: TargetMachineRef,
45-
exported_symbols: &ExportedSymbols,
4646
config: &ModuleConfig,
47-
temp_no_opt_bc_filename: &Path) {
48-
if sess.opts.cg.prefer_dynamic {
49-
sess.struct_err("cannot prefer dynamic linking when performing LTO")
47+
temp_no_opt_bc_filename: &Path) -> Result<(), FatalError> {
48+
let handler = cgcx.handler;
49+
if cgcx.opts.cg.prefer_dynamic {
50+
handler.struct_err("cannot prefer dynamic linking when performing LTO")
5051
.note("only 'staticlib', 'bin', and 'cdylib' outputs are \
5152
supported with LTO")
5253
.emit();
53-
sess.abort_if_errors();
54+
return Err(FatalError)
5455
}
5556

5657
// Make sure we actually can run LTO
57-
for crate_type in sess.crate_types.borrow().iter() {
58+
for crate_type in cgcx.crate_types.iter() {
5859
if !crate_type_allows_lto(*crate_type) {
59-
sess.fatal("lto can only be run for executables, cdylibs and \
60-
static library outputs");
60+
let e = handler.fatal("lto can only be run for executables, cdylibs and \
61+
static library outputs");
62+
return Err(e)
6163
}
6264
}
6365

6466
let export_threshold =
65-
symbol_export::crates_export_threshold(&sess.crate_types.borrow());
67+
symbol_export::crates_export_threshold(&cgcx.crate_types);
6668

6769
let symbol_filter = &|&(ref name, level): &(String, _)| {
6870
if symbol_export::is_below_threshold(level, export_threshold) {
@@ -74,7 +76,7 @@ pub fn run(sess: &session::Session,
7476
}
7577
};
7678

77-
let mut symbol_white_list: Vec<CString> = exported_symbols
79+
let mut symbol_white_list: Vec<CString> = cgcx.exported_symbols
7880
.exported_symbols(LOCAL_CRATE)
7981
.iter()
8082
.filter_map(symbol_filter)
@@ -83,16 +85,11 @@ pub fn run(sess: &session::Session,
8385
// For each of our upstream dependencies, find the corresponding rlib and
8486
// load the bitcode from the archive. Then merge it into the current LLVM
8587
// module that we've got.
86-
link::each_linked_rlib(sess, &mut |cnum, path| {
87-
// `#![no_builtins]` crates don't participate in LTO.
88-
if sess.cstore.is_no_builtins(cnum) {
89-
return;
90-
}
91-
88+
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
9289
symbol_white_list.extend(
93-
exported_symbols.exported_symbols(cnum)
94-
.iter()
95-
.filter_map(symbol_filter));
90+
cgcx.exported_symbols.exported_symbols(cnum)
91+
.iter()
92+
.filter_map(symbol_filter));
9693

9794
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
9895
let bytecodes = archive.iter().filter_map(|child| {
@@ -102,7 +99,7 @@ pub fn run(sess: &session::Session,
10299
let bc_encoded = data.data();
103100

104101
let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
105-
time(sess.time_passes(), &format!("decode {}", name), || {
102+
time(cgcx.time_passes, &format!("decode {}", name), || {
106103
// Read the version
107104
let version = extract_bytecode_format_version(bc_encoded);
108105

@@ -117,44 +114,49 @@ pub fn run(sess: &session::Session,
117114
let res = ZlibDecoder::new(compressed_data)
118115
.read_to_end(&mut inflated);
119116
if res.is_err() {
120-
sess.fatal(&format!("failed to decompress bc of `{}`",
121-
name))
117+
let msg = format!("failed to decompress bc of `{}`",
118+
name);
119+
Err(handler.fatal(&msg))
120+
} else {
121+
Ok(inflated)
122122
}
123-
inflated
124123
} else {
125-
sess.fatal(&format!("Unsupported bytecode format version {}",
126-
version))
124+
Err(handler.fatal(&format!("Unsupported bytecode format version {}",
125+
version)))
127126
}
128-
})
127+
})?
129128
} else {
130-
time(sess.time_passes(), &format!("decode {}", name), || {
129+
time(cgcx.time_passes, &format!("decode {}", name), || {
131130
// the object must be in the old, pre-versioning format, so
132131
// simply inflate everything and let LLVM decide if it can
133132
// make sense of it
134133
let mut inflated = Vec::new();
135134
let res = ZlibDecoder::new(bc_encoded)
136135
.read_to_end(&mut inflated);
137136
if res.is_err() {
138-
sess.fatal(&format!("failed to decompress bc of `{}`",
139-
name))
137+
let msg = format!("failed to decompress bc of `{}`",
138+
name);
139+
Err(handler.fatal(&msg))
140+
} else {
141+
Ok(inflated)
140142
}
141-
inflated
142-
})
143+
})?
143144
};
144145

145146
let ptr = bc_decoded.as_ptr();
146147
debug!("linking {}", name);
147-
time(sess.time_passes(), &format!("ll link {}", name), || unsafe {
148-
if !llvm::LLVMRustLinkInExternalBitcode(llmod,
149-
ptr as *const libc::c_char,
150-
bc_decoded.len() as libc::size_t) {
151-
write::llvm_err(sess.diagnostic(),
152-
format!("failed to load bc of `{}`",
153-
name));
148+
time(cgcx.time_passes, &format!("ll link {}", name), || unsafe {
149+
if llvm::LLVMRustLinkInExternalBitcode(llmod,
150+
ptr as *const libc::c_char,
151+
bc_decoded.len() as libc::size_t) {
152+
Ok(())
153+
} else {
154+
let msg = format!("failed to load bc of `{}`", name);
155+
Err(write::llvm_err(handler, msg))
154156
}
155-
});
157+
})?;
156158
}
157-
});
159+
}
158160

159161
// Internalize everything but the exported symbols of the current module
160162
let arr: Vec<*const libc::c_char> = symbol_white_list.iter()
@@ -167,13 +169,13 @@ pub fn run(sess: &session::Session,
167169
arr.len() as libc::size_t);
168170
}
169171

170-
if sess.no_landing_pads() {
172+
if cgcx.no_landing_pads {
171173
unsafe {
172174
llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
173175
}
174176
}
175177

176-
if sess.opts.cg.save_temps {
178+
if cgcx.opts.cg.save_temps {
177179
let cstr = path2cstr(temp_no_opt_bc_filename);
178180
unsafe {
179181
llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
@@ -203,12 +205,13 @@ pub fn run(sess: &session::Session,
203205
assert!(!pass.is_null());
204206
llvm::LLVMRustAddPass(pm, pass);
205207

206-
time(sess.time_passes(), "LTO passes", ||
208+
time(cgcx.time_passes, "LTO passes", ||
207209
llvm::LLVMRunPassManager(pm, llmod));
208210

209211
llvm::LLVMDisposePassManager(pm);
210212
}
211213
debug!("lto done");
214+
Ok(())
212215
}
213216

214217
fn is_versioned_bytecode_format(bc: &[u8]) -> bool {

0 commit comments

Comments
 (0)