Skip to content

Commit ee8f8bf

Browse files
authored
Merge pull request #1271 from bjorn3/parallel_comp_support
Support compiling codegen units in parallel
2 parents b1e9d2e + 072fd2b commit ee8f8bf

File tree

5 files changed

+297
-78
lines changed

5 files changed

+297
-78
lines changed

src/base.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ use rustc_index::vec::IndexVec;
55
use rustc_middle::ty::adjustment::PointerCast;
66
use rustc_middle::ty::layout::FnAbiOf;
77
use rustc_middle::ty::print::with_no_trimmed_paths;
8-
use rustc_middle::ty::SymbolName;
98

109
use crate::constant::ConstantCx;
1110
use crate::debuginfo::FunctionDebugContext;
1211
use crate::prelude::*;
1312
use crate::pretty_clif::CommentWriter;
1413

15-
struct CodegenedFunction<'tcx> {
16-
symbol_name: SymbolName<'tcx>,
14+
pub(crate) struct CodegenedFunction {
15+
symbol_name: String,
1716
func_id: FuncId,
1817
func: Function,
1918
clif_comments: CommentWriter,
2019
func_debug_cx: Option<FunctionDebugContext>,
2120
}
2221

22+
#[cfg_attr(not(feature = "jit"), allow(dead_code))]
2323
pub(crate) fn codegen_and_compile_fn<'tcx>(
2424
tcx: TyCtxt<'tcx>,
2525
cx: &mut crate::CodegenCx,
@@ -36,13 +36,13 @@ pub(crate) fn codegen_and_compile_fn<'tcx>(
3636
compile_fn(cx, cached_context, module, codegened_func);
3737
}
3838

39-
fn codegen_fn<'tcx>(
39+
pub(crate) fn codegen_fn<'tcx>(
4040
tcx: TyCtxt<'tcx>,
4141
cx: &mut crate::CodegenCx,
4242
cached_func: Function,
4343
module: &mut dyn Module,
4444
instance: Instance<'tcx>,
45-
) -> CodegenedFunction<'tcx> {
45+
) -> CodegenedFunction {
4646
debug_assert!(!instance.substs.needs_infer());
4747

4848
let mir = tcx.instance_mir(instance.def);
@@ -56,9 +56,9 @@ fn codegen_fn<'tcx>(
5656
});
5757

5858
// Declare function
59-
let symbol_name = tcx.symbol_name(instance);
59+
let symbol_name = tcx.symbol_name(instance).name.to_string();
6060
let sig = get_function_sig(tcx, module.isa().triple(), instance);
61-
let func_id = module.declare_function(symbol_name.name, Linkage::Local, &sig).unwrap();
61+
let func_id = module.declare_function(&symbol_name, Linkage::Local, &sig).unwrap();
6262

6363
// Make the FunctionBuilder
6464
let mut func_ctx = FunctionBuilderContext::new();
@@ -81,7 +81,7 @@ fn codegen_fn<'tcx>(
8181
let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance);
8282

8383
let func_debug_cx = if let Some(debug_context) = &mut cx.debug_context {
84-
Some(debug_context.define_function(tcx, symbol_name.name, mir.span))
84+
Some(debug_context.define_function(tcx, &symbol_name, mir.span))
8585
} else {
8686
None
8787
};
@@ -113,6 +113,7 @@ fn codegen_fn<'tcx>(
113113
tcx.sess.time("codegen clif ir", || codegen_fn_body(&mut fx, start_block));
114114

115115
// Recover all necessary data from fx, before accessing func will prevent future access to it.
116+
let symbol_name = fx.symbol_name;
116117
let clif_comments = fx.clif_comments;
117118
let func_debug_cx = fx.func_debug_cx;
118119

@@ -121,7 +122,7 @@ fn codegen_fn<'tcx>(
121122
if cx.should_write_ir {
122123
crate::pretty_clif::write_clif_file(
123124
tcx.output_filenames(()),
124-
symbol_name.name,
125+
&symbol_name,
125126
"unopt",
126127
module.isa(),
127128
&func,
@@ -135,11 +136,11 @@ fn codegen_fn<'tcx>(
135136
CodegenedFunction { symbol_name, func_id, func, clif_comments, func_debug_cx }
136137
}
137138

138-
fn compile_fn<'tcx>(
139+
pub(crate) fn compile_fn(
139140
cx: &mut crate::CodegenCx,
140141
cached_context: &mut Context,
141142
module: &mut dyn Module,
142-
codegened_func: CodegenedFunction<'tcx>,
143+
codegened_func: CodegenedFunction,
143144
) {
144145
let clif_comments = codegened_func.clif_comments;
145146

@@ -195,7 +196,7 @@ fn compile_fn<'tcx>(
195196
// Write optimized function to file for debugging
196197
crate::pretty_clif::write_clif_file(
197198
&cx.output_filenames,
198-
codegened_func.symbol_name.name,
199+
&codegened_func.symbol_name,
199200
"opt",
200201
module.isa(),
201202
&context.func,
@@ -205,7 +206,7 @@ fn compile_fn<'tcx>(
205206
if let Some(disasm) = &context.compiled_code().unwrap().disasm {
206207
crate::pretty_clif::write_ir_file(
207208
&cx.output_filenames,
208-
&format!("{}.vcode", codegened_func.symbol_name.name),
209+
&format!("{}.vcode", codegened_func.symbol_name),
209210
|file| file.write_all(disasm.as_bytes()),
210211
)
211212
}

src/common.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use rustc_index::vec::IndexVec;
66
use rustc_middle::ty::layout::{
77
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers,
88
};
9-
use rustc_middle::ty::SymbolName;
109
use rustc_span::SourceFile;
1110
use rustc_target::abi::call::FnAbi;
1211
use rustc_target::abi::{Integer, Primitive};
@@ -246,7 +245,7 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> {
246245
pub(crate) func_debug_cx: Option<FunctionDebugContext>,
247246

248247
pub(crate) instance: Instance<'tcx>,
249-
pub(crate) symbol_name: SymbolName<'tcx>,
248+
pub(crate) symbol_name: String,
250249
pub(crate) mir: &'tcx Body<'tcx>,
251250
pub(crate) fn_abi: Option<&'tcx FnAbi<'tcx, Ty<'tcx>>>,
252251

src/concurrency_limiter.rs

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
use std::sync::{Arc, Condvar, Mutex};
2+
3+
use rustc_session::Session;
4+
5+
use jobserver::HelperThread;
6+
7+
// FIXME don't panic when a worker thread panics
8+
9+
pub(super) struct ConcurrencyLimiter {
10+
helper_thread: Option<HelperThread>,
11+
state: Arc<Mutex<state::ConcurrencyLimiterState>>,
12+
available_token_condvar: Arc<Condvar>,
13+
}
14+
15+
impl ConcurrencyLimiter {
16+
pub(super) fn new(sess: &Session, pending_jobs: usize) -> Self {
17+
let state = Arc::new(Mutex::new(state::ConcurrencyLimiterState::new(pending_jobs)));
18+
let available_token_condvar = Arc::new(Condvar::new());
19+
20+
let state_helper = state.clone();
21+
let available_token_condvar_helper = available_token_condvar.clone();
22+
let helper_thread = sess
23+
.jobserver
24+
.clone()
25+
.into_helper_thread(move |token| {
26+
let mut state = state_helper.lock().unwrap();
27+
state.add_new_token(token.unwrap());
28+
available_token_condvar_helper.notify_one();
29+
})
30+
.unwrap();
31+
ConcurrencyLimiter {
32+
helper_thread: Some(helper_thread),
33+
state,
34+
available_token_condvar: Arc::new(Condvar::new()),
35+
}
36+
}
37+
38+
pub(super) fn acquire(&mut self) -> ConcurrencyLimiterToken {
39+
let mut state = self.state.lock().unwrap();
40+
loop {
41+
state.assert_invariants();
42+
43+
if state.try_start_job() {
44+
return ConcurrencyLimiterToken {
45+
state: self.state.clone(),
46+
available_token_condvar: self.available_token_condvar.clone(),
47+
};
48+
}
49+
50+
self.helper_thread.as_mut().unwrap().request_token();
51+
state = self.available_token_condvar.wait(state).unwrap();
52+
}
53+
}
54+
55+
pub(super) fn job_already_done(&mut self) {
56+
let mut state = self.state.lock().unwrap();
57+
state.job_already_done();
58+
}
59+
}
60+
61+
impl Drop for ConcurrencyLimiter {
62+
fn drop(&mut self) {
63+
//
64+
self.helper_thread.take();
65+
66+
// Assert that all jobs have finished
67+
let state = Mutex::get_mut(Arc::get_mut(&mut self.state).unwrap()).unwrap();
68+
state.assert_done();
69+
}
70+
}
71+
72+
#[derive(Debug)]
73+
pub(super) struct ConcurrencyLimiterToken {
74+
state: Arc<Mutex<state::ConcurrencyLimiterState>>,
75+
available_token_condvar: Arc<Condvar>,
76+
}
77+
78+
impl Drop for ConcurrencyLimiterToken {
79+
fn drop(&mut self) {
80+
let mut state = self.state.lock().unwrap();
81+
state.job_finished();
82+
self.available_token_condvar.notify_one();
83+
}
84+
}
85+
86+
mod state {
87+
use jobserver::Acquired;
88+
89+
#[derive(Debug)]
90+
pub(super) struct ConcurrencyLimiterState {
91+
pending_jobs: usize,
92+
active_jobs: usize,
93+
94+
// None is used to represent the implicit token, Some to represent explicit tokens
95+
tokens: Vec<Option<Acquired>>,
96+
}
97+
98+
impl ConcurrencyLimiterState {
99+
pub(super) fn new(pending_jobs: usize) -> Self {
100+
ConcurrencyLimiterState { pending_jobs, active_jobs: 0, tokens: vec![None] }
101+
}
102+
103+
pub(super) fn assert_invariants(&self) {
104+
// There must be no excess active jobs
105+
assert!(self.active_jobs <= self.pending_jobs);
106+
107+
// There may not be more active jobs than there are tokens
108+
assert!(self.active_jobs <= self.tokens.len());
109+
}
110+
111+
pub(super) fn assert_done(&self) {
112+
assert_eq!(self.pending_jobs, 0);
113+
assert_eq!(self.active_jobs, 0);
114+
}
115+
116+
pub(super) fn add_new_token(&mut self, token: Acquired) {
117+
self.tokens.push(Some(token));
118+
self.drop_excess_capacity();
119+
}
120+
121+
pub(super) fn try_start_job(&mut self) -> bool {
122+
if self.active_jobs < self.tokens.len() {
123+
// Using existing token
124+
self.job_started();
125+
return true;
126+
}
127+
128+
false
129+
}
130+
131+
pub(super) fn job_started(&mut self) {
132+
self.assert_invariants();
133+
self.active_jobs += 1;
134+
self.drop_excess_capacity();
135+
self.assert_invariants();
136+
}
137+
138+
pub(super) fn job_finished(&mut self) {
139+
self.assert_invariants();
140+
self.pending_jobs -= 1;
141+
self.active_jobs -= 1;
142+
self.assert_invariants();
143+
self.drop_excess_capacity();
144+
self.assert_invariants();
145+
}
146+
147+
pub(super) fn job_already_done(&mut self) {
148+
self.assert_invariants();
149+
self.pending_jobs -= 1;
150+
self.assert_invariants();
151+
self.drop_excess_capacity();
152+
self.assert_invariants();
153+
}
154+
155+
fn drop_excess_capacity(&mut self) {
156+
self.assert_invariants();
157+
158+
// Drop all tokens that can never be used anymore
159+
self.tokens.truncate(std::cmp::max(self.pending_jobs, 1));
160+
161+
// Keep some excess tokens to satisfy requests faster
162+
const MAX_EXTRA_CAPACITY: usize = 2;
163+
self.tokens.truncate(std::cmp::max(self.active_jobs + MAX_EXTRA_CAPACITY, 1));
164+
165+
self.assert_invariants();
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)