Skip to content

Commit 3cd3bbe

Browse files
committed
Auto merge of #90617 - tmiasko:time-trace-threads, r=wesleywiser
Initialize LLVM time trace profiler on each code generation thread In https://reviews.llvm.org/D71059 LLVM 11, the time trace profiler was extended to support multiple threads. `timeTraceProfilerInitialize` creates a thread local profiler instance. When a thread finishes `timeTraceProfilerFinishThread` moves a thread local instance into a global collection of instances. Finally when all codegen work is complete `timeTraceProfilerWrite` writes data from the current thread local instance and the instances in global collection of instances. Previously, the profiler was intialized on a single thread only. Since this thread performs no code generation on its own, the resulting profile was empty. Update LLVM codegen to initialize & finish time trace profiler on each code generation thread. cc `@tmandry` r? `@wesleywiser`
2 parents 7276a6a + 5a09e12 commit 3cd3bbe

File tree

6 files changed

+128
-56
lines changed

6 files changed

+128
-56
lines changed

compiler/rustc_codegen_llvm/src/lib.rs

+49
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ mod value;
7676
#[derive(Clone)]
7777
pub struct LlvmCodegenBackend(());
7878

79+
struct TimeTraceProfiler {
80+
enabled: bool,
81+
}
82+
83+
impl TimeTraceProfiler {
84+
fn new(enabled: bool) -> Self {
85+
if enabled {
86+
unsafe { llvm::LLVMTimeTraceProfilerInitialize() }
87+
}
88+
TimeTraceProfiler { enabled }
89+
}
90+
}
91+
92+
impl Drop for TimeTraceProfiler {
93+
fn drop(&mut self) {
94+
if self.enabled {
95+
unsafe { llvm::LLVMTimeTraceProfilerFinishThread() }
96+
}
97+
}
98+
}
99+
79100
impl ExtraBackendMethods for LlvmCodegenBackend {
80101
fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm {
81102
ModuleLlvm::new_metadata(tcx, mod_name)
@@ -119,6 +140,34 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
119140
fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str> {
120141
llvm_util::tune_cpu(sess)
121142
}
143+
144+
fn spawn_thread<F, T>(time_trace: bool, f: F) -> std::thread::JoinHandle<T>
145+
where
146+
F: FnOnce() -> T,
147+
F: Send + 'static,
148+
T: Send + 'static,
149+
{
150+
std::thread::spawn(move || {
151+
let _profiler = TimeTraceProfiler::new(time_trace);
152+
f()
153+
})
154+
}
155+
156+
fn spawn_named_thread<F, T>(
157+
time_trace: bool,
158+
name: String,
159+
f: F,
160+
) -> std::io::Result<std::thread::JoinHandle<T>>
161+
where
162+
F: FnOnce() -> T,
163+
F: Send + 'static,
164+
T: Send + 'static,
165+
{
166+
std::thread::Builder::new().name(name).spawn(move || {
167+
let _profiler = TimeTraceProfiler::new(time_trace);
168+
f()
169+
})
170+
}
122171
}
123172

124173
impl WriteBackendMethods for LlvmCodegenBackend {

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,8 @@ extern "C" {
17371737

17381738
pub fn LLVMTimeTraceProfilerInitialize();
17391739

1740+
pub fn LLVMTimeTraceProfilerFinishThread();
1741+
17401742
pub fn LLVMTimeTraceProfilerFinish(FileName: *const c_char);
17411743

17421744
pub fn LLVMAddAnalysisPasses(T: &'a TargetMachine, PM: &PassManager<'a>);

compiler/rustc_codegen_llvm/src/llvm_util.rs

-5
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ unsafe fn configure_llvm(sess: &Session) {
113113
}
114114

115115
if sess.opts.debugging_opts.llvm_time_trace {
116-
// time-trace is not thread safe and running it in parallel will cause seg faults.
117-
if !sess.opts.debugging_opts.no_parallel_llvm {
118-
bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm")
119-
}
120-
121116
llvm::LLVMTimeTraceProfilerInitialize();
122117
}
123118

compiler/rustc_codegen_ssa/src/back/write.rs

+51-51
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
310310
pub no_landing_pads: bool,
311311
pub save_temps: bool,
312312
pub fewer_names: bool,
313+
pub time_trace: bool,
313314
pub exported_symbols: Option<Arc<ExportedSymbols>>,
314315
pub opts: Arc<config::Options>,
315316
pub crate_types: Vec<CrateType>,
@@ -1039,6 +1040,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
10391040
no_landing_pads: sess.panic_strategy() == PanicStrategy::Abort,
10401041
fewer_names: sess.fewer_names(),
10411042
save_temps: sess.opts.cg.save_temps,
1043+
time_trace: sess.opts.debugging_opts.llvm_time_trace,
10421044
opts: Arc::new(sess.opts.clone()),
10431045
prof: sess.prof.clone(),
10441046
exported_symbols,
@@ -1198,7 +1200,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
11981200
// Each LLVM module is automatically sent back to the coordinator for LTO if
11991201
// necessary. There's already optimizations in place to avoid sending work
12001202
// back to the coordinator if LTO isn't requested.
1201-
return thread::spawn(move || {
1203+
return B::spawn_thread(cgcx.time_trace, move || {
12021204
let mut worker_id_counter = 0;
12031205
let mut free_worker_ids = Vec::new();
12041206
let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| {
@@ -1615,59 +1617,57 @@ fn start_executing_work<B: ExtraBackendMethods>(
16151617
pub struct WorkerFatalError;
16161618

16171619
fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>) {
1618-
let builder = thread::Builder::new().name(work.short_description());
1619-
builder
1620-
.spawn(move || {
1621-
// Set up a destructor which will fire off a message that we're done as
1622-
// we exit.
1623-
struct Bomb<B: ExtraBackendMethods> {
1624-
coordinator_send: Sender<Box<dyn Any + Send>>,
1625-
result: Option<Result<WorkItemResult<B>, FatalError>>,
1626-
worker_id: usize,
1627-
}
1628-
impl<B: ExtraBackendMethods> Drop for Bomb<B> {
1629-
fn drop(&mut self) {
1630-
let worker_id = self.worker_id;
1631-
let msg = match self.result.take() {
1632-
Some(Ok(WorkItemResult::Compiled(m))) => {
1633-
Message::Done::<B> { result: Ok(m), worker_id }
1634-
}
1635-
Some(Ok(WorkItemResult::NeedsLink(m))) => {
1636-
Message::NeedsLink::<B> { module: m, worker_id }
1637-
}
1638-
Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
1639-
Message::NeedsFatLTO::<B> { result: m, worker_id }
1640-
}
1641-
Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
1642-
Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
1643-
}
1644-
Some(Err(FatalError)) => {
1645-
Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id }
1646-
}
1647-
None => Message::Done::<B> { result: Err(None), worker_id },
1648-
};
1649-
drop(self.coordinator_send.send(Box::new(msg)));
1650-
}
1620+
B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || {
1621+
// Set up a destructor which will fire off a message that we're done as
1622+
// we exit.
1623+
struct Bomb<B: ExtraBackendMethods> {
1624+
coordinator_send: Sender<Box<dyn Any + Send>>,
1625+
result: Option<Result<WorkItemResult<B>, FatalError>>,
1626+
worker_id: usize,
1627+
}
1628+
impl<B: ExtraBackendMethods> Drop for Bomb<B> {
1629+
fn drop(&mut self) {
1630+
let worker_id = self.worker_id;
1631+
let msg = match self.result.take() {
1632+
Some(Ok(WorkItemResult::Compiled(m))) => {
1633+
Message::Done::<B> { result: Ok(m), worker_id }
1634+
}
1635+
Some(Ok(WorkItemResult::NeedsLink(m))) => {
1636+
Message::NeedsLink::<B> { module: m, worker_id }
1637+
}
1638+
Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
1639+
Message::NeedsFatLTO::<B> { result: m, worker_id }
1640+
}
1641+
Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
1642+
Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
1643+
}
1644+
Some(Err(FatalError)) => {
1645+
Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id }
1646+
}
1647+
None => Message::Done::<B> { result: Err(None), worker_id },
1648+
};
1649+
drop(self.coordinator_send.send(Box::new(msg)));
16511650
}
1651+
}
16521652

1653-
let mut bomb = Bomb::<B> {
1654-
coordinator_send: cgcx.coordinator_send.clone(),
1655-
result: None,
1656-
worker_id: cgcx.worker,
1657-
};
1653+
let mut bomb = Bomb::<B> {
1654+
coordinator_send: cgcx.coordinator_send.clone(),
1655+
result: None,
1656+
worker_id: cgcx.worker,
1657+
};
16581658

1659-
// Execute the work itself, and if it finishes successfully then flag
1660-
// ourselves as a success as well.
1661-
//
1662-
// Note that we ignore any `FatalError` coming out of `execute_work_item`,
1663-
// as a diagnostic was already sent off to the main thread - just
1664-
// surface that there was an error in this worker.
1665-
bomb.result = {
1666-
let _prof_timer = work.start_profiling(&cgcx);
1667-
Some(execute_work_item(&cgcx, work))
1668-
};
1669-
})
1670-
.expect("failed to spawn thread");
1659+
// Execute the work itself, and if it finishes successfully then flag
1660+
// ourselves as a success as well.
1661+
//
1662+
// Note that we ignore any `FatalError` coming out of `execute_work_item`,
1663+
// as a diagnostic was already sent off to the main thread - just
1664+
// surface that there was an error in this worker.
1665+
bomb.result = {
1666+
let _prof_timer = work.start_profiling(&cgcx);
1667+
Some(execute_work_item(&cgcx, work))
1668+
};
1669+
})
1670+
.expect("failed to spawn thread");
16711671
}
16721672

16731673
enum SharedEmitterMessage {

compiler/rustc_codegen_ssa/src/traits/backend.rs

+22
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,26 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
142142
) -> TargetMachineFactoryFn<Self>;
143143
fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str;
144144
fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>;
145+
146+
fn spawn_thread<F, T>(_time_trace: bool, f: F) -> std::thread::JoinHandle<T>
147+
where
148+
F: FnOnce() -> T,
149+
F: Send + 'static,
150+
T: Send + 'static,
151+
{
152+
std::thread::spawn(f)
153+
}
154+
155+
fn spawn_named_thread<F, T>(
156+
_time_trace: bool,
157+
name: String,
158+
f: F,
159+
) -> std::io::Result<std::thread::JoinHandle<T>>
160+
where
161+
F: FnOnce() -> T,
162+
F: Send + 'static,
163+
T: Send + 'static,
164+
{
165+
std::thread::Builder::new().name(name).spawn(f)
166+
}
145167
}

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ extern "C" void LLVMTimeTraceProfilerInitialize() {
7575
/* ProcName */ "rustc");
7676
}
7777

78+
extern "C" void LLVMTimeTraceProfilerFinishThread() {
79+
timeTraceProfilerFinishThread();
80+
}
81+
7882
extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) {
7983
StringRef FN(FileName);
8084
std::error_code EC;

0 commit comments

Comments
 (0)