Skip to content

Commit 6774224

Browse files
committed
Switch to thread local
1 parent 91c86e0 commit 6774224

4 files changed

Lines changed: 57 additions & 77 deletions

File tree

kani-compiler/src/codegen_aeneas_llbc/compiler_interface.rs

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::kani_middle::codegen_units::{CodegenUnit, CodegenUnits};
1111
use crate::kani_middle::provide;
1212
use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_items};
1313
use crate::kani_middle::transform::{BodyTransformation, GlobalPasses};
14-
use crate::kani_queries::QueryDb;
14+
use crate::kani_queries::QUERY_DB;
1515
use charon_lib::ast::{AnyTransId, TranslatedCrate, meta::ItemOpacity::*, meta::Span};
1616
use charon_lib::errors::ErrorCtx;
1717
use charon_lib::name_matcher::NamePattern;
@@ -43,22 +43,15 @@ use rustc_target::spec::Arch;
4343
use std::any::Any;
4444
use std::fs::File;
4545
use std::path::Path;
46-
use std::sync::{Arc, Mutex};
4746
use std::time::Instant;
4847
use tracing::{debug, info, trace};
4948

5049
#[derive(Clone)]
51-
pub struct LlbcCodegenBackend {
52-
/// The query is shared with `KaniCompiler` and it is initialized as part of `rustc`
53-
/// initialization, which may happen after this object is created.
54-
/// Since we don't have any guarantees on when the compiler creates the Backend object, neither
55-
/// in which thread it will be used, we prefer to explicitly synchronize any query access.
56-
queries: Arc<Mutex<QueryDb>>,
57-
}
50+
pub struct LlbcCodegenBackend {}
5851

5952
impl LlbcCodegenBackend {
60-
pub fn new(queries: Arc<Mutex<QueryDb>>) -> Self {
61-
LlbcCodegenBackend { queries }
53+
pub fn new() -> Self {
54+
LlbcCodegenBackend {}
6255
}
6356

6457
/// Generate code that is reachable from the given starting points.
@@ -91,16 +84,18 @@ impl LlbcCodegenBackend {
9184
.collect();
9285

9386
// Apply all transformation passes, including global passes.
94-
let mut global_passes = GlobalPasses::new(&self.queries.lock().unwrap(), tcx);
95-
global_passes.run_global_passes(
96-
&mut transformer,
97-
tcx,
98-
starting_items,
99-
instances,
100-
call_graph,
101-
);
87+
QUERY_DB.with(|db| {
88+
let mut global_passes = GlobalPasses::new(&db.borrow(), tcx);
89+
global_passes.run_global_passes(
90+
&mut transformer,
91+
tcx,
92+
starting_items,
93+
instances,
94+
call_graph,
95+
);
96+
});
10297

103-
let queries = self.queries.lock().unwrap().clone();
98+
let queries = QUERY_DB.with(|db| db.borrow().clone());
10499
check_reachable_items(tcx, &queries, &items);
105100

106101
// Follow rustc naming convention (cx is abbrev for context).
@@ -192,7 +187,7 @@ impl LlbcCodegenBackend {
192187

193188
impl CodegenBackend for LlbcCodegenBackend {
194189
fn provide(&self, providers: &mut Providers) {
195-
provide::provide(providers, &self.queries.lock().unwrap());
190+
QUERY_DB.with(|db| provide::provide(providers, &db.borrow()));
196191
}
197192

198193
fn print_version(&self) {
@@ -211,7 +206,7 @@ impl CodegenBackend for LlbcCodegenBackend {
211206
fn codegen_crate(&self, tcx: TyCtxt) -> Box<dyn Any> {
212207
let ret_val = rustc_internal::run(tcx, || {
213208
// Queries shouldn't change today once codegen starts.
214-
let queries = self.queries.lock().unwrap().clone();
209+
let queries = QUERY_DB.with(|db| db.borrow().clone());
215210

216211
// Codegen all items that need to be processed according to the selected reachability mode:
217212
//

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::kani_middle::codegen_units::{CodegenUnit, CodegenUnits};
1414
use crate::kani_middle::provide;
1515
use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_items};
1616
use crate::kani_middle::transform::{BodyTransformation, GlobalPasses};
17-
use crate::kani_queries::QueryDb;
17+
use crate::kani_queries::QUERY_DB;
1818
use cbmc::goto_program::Location;
1919
use cbmc::{InternedString, MachineModel};
2020
use cbmc::{RoundingMode, WithInterner};
@@ -52,7 +52,6 @@ use std::fs::File;
5252
use std::io::BufWriter;
5353
use std::num::NonZero;
5454
use std::path::Path;
55-
use std::sync::{Arc, Mutex};
5655
use std::thread::available_parallelism;
5756
use std::time::Instant;
5857
use tracing::{debug, info};
@@ -66,17 +65,11 @@ const MAX_SENSIBLE_FILE_EXPORT_THREADS: usize = 4;
6665

6766
pub type UnsupportedConstructs = FxHashMap<InternedString, Vec<Location>>;
6867

69-
pub struct GotocCodegenBackend {
70-
/// The query is shared with `KaniCompiler` and it is initialized as part of `rustc`
71-
/// initialization, which may happen after this object is created.
72-
/// Since we don't have any guarantees on when the compiler creates the Backend object, neither
73-
/// in which thread it will be used, we prefer to explicitly synchronize any query access.
74-
queries: Arc<Mutex<QueryDb>>,
75-
}
68+
pub struct GotocCodegenBackend {}
7669

7770
impl GotocCodegenBackend {
78-
pub fn new(queries: Arc<Mutex<QueryDb>>) -> Self {
79-
GotocCodegenBackend { queries }
71+
pub fn new() -> Self {
72+
GotocCodegenBackend {}
8073
}
8174

8275
/// Generate code that is reachable from the given starting points.
@@ -140,7 +133,7 @@ impl GotocCodegenBackend {
140133
// Follow rustc naming convention (cx is abbrev for context).
141134
// https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions
142135
let mut gcx =
143-
GotocCtx::new(tcx, (*self.queries.lock().unwrap()).clone(), machine_model, transformer);
136+
QUERY_DB.with(|db| GotocCtx::new(tcx, db.borrow().clone(), machine_model, transformer));
144137
check_reachable_items(gcx.tcx, &gcx.queries, &items);
145138

146139
let contract_info = with_timer(
@@ -221,7 +214,7 @@ impl GotocCodegenBackend {
221214

222215
// No output should be generated if user selected no_codegen.
223216
if !tcx.sess.opts.unstable_opts.no_codegen && tcx.sess.opts.output_types.should_codegen() {
224-
let pretty = self.queries.lock().unwrap().args().output_pretty_json;
217+
let pretty = QUERY_DB.with(|db| db.borrow().args().output_pretty_json);
225218

226219
// Save all the data needed to write this goto file
227220
// so another thread can handle it in parallel.
@@ -283,7 +276,7 @@ impl GotocCodegenBackend {
283276

284277
impl CodegenBackend for GotocCodegenBackend {
285278
fn provide(&self, providers: &mut Providers) {
286-
provide::provide(providers, &self.queries.lock().unwrap());
279+
QUERY_DB.with(|db| provide::provide(providers, &db.borrow()));
287280
}
288281

289282
fn print_version(&self) {
@@ -341,7 +334,7 @@ impl CodegenBackend for GotocCodegenBackend {
341334
// needed for generating code to the given crate.
342335
// The cached information must not outlive the stable-mir `run` scope.
343336
// See [QueryDb::kani_functions] for more information.
344-
let queries = self.queries.lock().unwrap().clone();
337+
let queries = QUERY_DB.with(|db| db.borrow().clone());
345338

346339
check_target(tcx.sess);
347340
check_options(tcx.sess);

kani-compiler/src/kani_compiler.rs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::codegen_aeneas_llbc::LlbcCodegenBackend;
2121
#[cfg(feature = "cprover")]
2222
use crate::codegen_cprover_gotoc::GotocCodegenBackend;
2323
use crate::kani_middle::check_crate_items;
24-
use crate::kani_queries::QueryDb;
24+
use crate::kani_queries::QUERY_DB;
2525
use crate::session::init_session;
2626
use clap::Parser;
2727
use rustc_codegen_ssa::traits::CodegenBackend;
@@ -30,7 +30,6 @@ use rustc_interface::Config;
3030
use rustc_middle::ty::TyCtxt;
3131
use rustc_public::rustc_internal;
3232
use rustc_session::config::ErrorOutputType;
33-
use std::sync::{Arc, Mutex};
3433
use tracing::debug;
3534

3635
/// Run the Kani flavour of the compiler.
@@ -42,54 +41,55 @@ pub fn run(args: Vec<String>) {
4241

4342
/// Configure the LLBC backend (Aeneas's IR).
4443
#[allow(unused)]
45-
fn llbc_backend(_queries: Arc<Mutex<QueryDb>>) -> Box<dyn CodegenBackend> {
44+
fn llbc_backend(args: Arguments) -> Box<dyn CodegenBackend> {
4645
#[cfg(feature = "llbc")]
47-
return Box::new(LlbcCodegenBackend::new(_queries));
46+
{
47+
QUERY_DB.with(|db| db.borrow_mut().set_args(args));
48+
return Box::new(LlbcCodegenBackend::new());
49+
}
4850
#[cfg(not(feature = "llbc"))]
4951
unreachable!()
5052
}
5153

5254
/// Configure the cprover backend that generates goto-programs.
5355
#[allow(unused)]
54-
fn cprover_backend(_queries: Arc<Mutex<QueryDb>>) -> Box<dyn CodegenBackend> {
56+
fn cprover_backend(args: Arguments) -> Box<dyn CodegenBackend> {
5557
#[cfg(feature = "cprover")]
56-
return Box::new(GotocCodegenBackend::new(_queries));
58+
{
59+
QUERY_DB.with(|db| db.borrow_mut().set_args(args));
60+
return Box::new(GotocCodegenBackend::new());
61+
}
5762
#[cfg(not(feature = "cprover"))]
5863
unreachable!()
5964
}
6065

6166
#[cfg(any(feature = "cprover", feature = "llbc"))]
62-
fn backend(queries: Arc<Mutex<QueryDb>>) -> Box<dyn CodegenBackend> {
63-
let backend = queries.lock().unwrap().args().backend;
67+
fn backend(args: Arguments) -> Box<dyn CodegenBackend> {
68+
let backend = args.backend;
6469
match backend {
6570
#[cfg(feature = "cprover")]
66-
BackendOption::CProver => cprover_backend(queries),
71+
BackendOption::CProver => cprover_backend(args),
6772
#[cfg(feature = "llbc")]
68-
BackendOption::Llbc => llbc_backend(queries),
73+
BackendOption::Llbc => llbc_backend(args),
6974
}
7075
}
7176

7277
/// Fallback backend. It will trigger an error if no backend has been enabled.
7378
#[cfg(not(any(feature = "cprover", feature = "llbc")))]
74-
fn backend(queries: Arc<Mutex<QueryDb>>) -> Box<CodegenBackend> {
79+
fn backend(args: Arguments) -> Box<CodegenBackend> {
7580
compile_error!("No backend is available. Use `cprover` or `llbc`.");
7681
}
7782

7883
/// This object controls the compiler behavior.
7984
///
8085
/// It is responsible for initializing the query database, as well as controlling the compiler
8186
/// state machine.
82-
struct KaniCompiler {
83-
/// Store the query database. The queries should be initialized as part of `config` when the
84-
/// compiler state is Init.
85-
/// Note that we need to share the queries with the backend before `config` is called.
86-
pub queries: Arc<Mutex<QueryDb>>,
87-
}
87+
struct KaniCompiler {}
8888

8989
impl KaniCompiler {
9090
/// Create a new [KaniCompiler] instance.
9191
pub fn new() -> KaniCompiler {
92-
KaniCompiler { queries: QueryDb::new() }
92+
KaniCompiler {}
9393
}
9494

9595
/// Compile the current crate with the given arguments.
@@ -107,19 +107,17 @@ impl Callbacks for KaniCompiler {
107107
/// Configure the [KaniCompiler] `self` object during the [CompilationStage::Init].
108108
fn config(&mut self, config: &mut Config) {
109109
let mut args = vec!["kani-compiler".to_string()];
110-
config.make_codegen_backend = Some(Box::new({
111-
let queries = self.queries.clone();
112-
move |_cfg| backend(queries)
113-
}));
114110
// `kani-driver` passes the `kani-compiler` specific arguments through llvm-args, so extract them here.
115111
args.extend(config.opts.cg.llvm_args.iter().cloned());
116112
let args = Arguments::parse_from(args);
117113
init_session(&args, matches!(config.opts.error_format, ErrorOutputType::Json { .. }));
118114

119-
// Configure queries.
120-
let queries = &mut (*self.queries.lock().unwrap());
121-
queries.set_args(args);
122-
debug!(?queries, "config end");
115+
// Capture args in the closure so they're available when the backend is created
116+
// (potentially on a different thread).
117+
config.make_codegen_backend = Some(Box::new({
118+
let args = args.clone();
119+
move |_cfg| backend(args)
120+
}));
123121
}
124122

125123
/// After analysis, we check the crate items for Kani API misuse or configuration issues.
@@ -129,7 +127,8 @@ impl Callbacks for KaniCompiler {
129127
tcx: TyCtxt<'_>,
130128
) -> Compilation {
131129
rustc_internal::run(tcx, || {
132-
check_crate_items(tcx, self.queries.lock().unwrap().args().ignore_global_asm);
130+
let ignore_global_asm = QUERY_DB.with(|db| db.borrow().args().ignore_global_asm);
131+
check_crate_items(tcx, ignore_global_asm);
133132
})
134133
.unwrap();
135134
Compilation::Continue

kani-compiler/src/kani_queries/mod.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ use crate::kani_middle::kani_functions::{
77
KaniFunction, find_kani_functions, validate_kani_functions,
88
};
99
use rustc_public::ty::FnDef;
10-
use std::cell::OnceCell;
10+
use std::cell::{OnceCell, RefCell};
1111
use std::collections::HashMap;
12-
use std::sync::{Arc, Mutex};
1312

14-
/// This structure should only be used behind a synchronized reference or a snapshot.
13+
thread_local! {
14+
pub static QUERY_DB: RefCell<QueryDb> = RefCell::new(QueryDb::default());
15+
}
16+
17+
/// This structure is only accessed via thread_local storage to ensure thread safety.
1518
///
1619
/// TODO: Merge this with arguments
1720
#[derive(Debug, Default, Clone)]
@@ -20,17 +23,7 @@ pub struct QueryDb {
2023
kani_functions: OnceCell<HashMap<KaniFunction, FnDef>>,
2124
}
2225

23-
// SAFETY: QueryDb is always used behind Arc<Mutex<>> which provides proper synchronization.
24-
// The FnDef type from rustc_public is not Send due to ThreadLocalIndex, but we ensure
25-
// it's only accessed within the appropriate rustc_public context and never actually sent
26-
// between threads.
27-
unsafe impl Send for QueryDb {}
28-
2926
impl QueryDb {
30-
pub fn new() -> Arc<Mutex<QueryDb>> {
31-
Arc::new(Mutex::new(QueryDb::default()))
32-
}
33-
3427
pub fn set_args(&mut self, args: Arguments) {
3528
self.args = Some(args);
3629
}

0 commit comments

Comments
 (0)