Skip to content

Commit ca20f6d

Browse files
committed
Block machine: only use JIT on Goldilocks
1 parent ce10fe7 commit ca20f6d

File tree

3 files changed

+44
-24
lines changed

3 files changed

+44
-24
lines changed

executor/src/witgen/eval_result.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub enum IncompleteCause<K = usize> {
4747
SymbolicEvaluationOfChallenge,
4848
/// Some knowledge was learnt, but not a concrete value. Example: `Y = X` if we know that `Y` is boolean. We learn that `X` is boolean, but not its exact value.
4949
NotConcrete,
50+
/// The JIT compiler was not able to generate a function that computes a unique witness.
51+
JitCompilationFailed,
5052
Multiple(Vec<IncompleteCause<K>>),
5153
}
5254

executor/src/witgen/jit/function_cache.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ pub struct FunctionCache<'a, T: FieldElement> {
3636
/// The processor that generates the JIT code
3737
processor: BlockMachineProcessor<'a, T>,
3838
/// The cache of JIT functions and the returned range constraints.
39-
/// If the entry is None, we attempted to generate the function but failed.
40-
witgen_functions: HashMap<CacheKey<T>, Option<CacheEntry<T>>>,
39+
/// If the entry is Err, we attempted to generate the function but failed.
40+
witgen_functions: HashMap<CacheKey<T>, Result<CacheEntry<T>, CompilationError>>,
4141
column_layout: ColumnLayout,
4242
block_size: usize,
4343
machine_name: String,
@@ -49,6 +49,12 @@ pub struct CacheEntry<T: FieldElement> {
4949
pub range_constraints: Vec<RangeConstraint<T>>,
5050
}
5151

52+
#[derive(Debug)]
53+
pub enum CompilationError {
54+
UnsupportedField,
55+
Other(String),
56+
}
57+
5258
impl<'a, T: FieldElement> FunctionCache<'a, T> {
5359
pub fn new(
5460
fixed_data: &'a FixedData<'a, T>,
@@ -81,15 +87,15 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
8187
identity_id: u64,
8288
known_args: &BitVec,
8389
known_concrete: Option<(usize, T)>,
84-
) -> &Option<CacheEntry<T>> {
90+
) -> &Result<CacheEntry<T>, CompilationError> {
8591
// First try the generic version, then the specific.
8692
let mut key = CacheKey {
8793
identity_id,
8894
known_args: known_args.clone(),
8995
known_concrete: None,
9096
};
9197

92-
if self.ensure_cache(can_process.clone(), &key).is_none() && known_concrete.is_some() {
98+
if self.ensure_cache(can_process.clone(), &key).is_err() && known_concrete.is_some() {
9399
key = CacheKey {
94100
identity_id,
95101
known_args: known_args.clone(),
@@ -104,15 +110,15 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
104110
&mut self,
105111
can_process: impl CanProcessCall<T>,
106112
cache_key: &CacheKey<T>,
107-
) -> &Option<CacheEntry<T>> {
113+
) -> &Result<CacheEntry<T>, CompilationError> {
108114
if !self.witgen_functions.contains_key(cache_key) {
109115
record_start("Auto-witgen code derivation");
110116
let f = match T::known_field() {
111117
// Currently, we only support the Goldilocks fields
112118
Some(KnownField::GoldilocksField) => {
113119
self.compile_witgen_function(can_process, cache_key)
114120
}
115-
_ => None,
121+
_ => Err(CompilationError::UnsupportedField),
116122
};
117123
assert!(self.witgen_functions.insert(cache_key.clone(), f).is_none());
118124
record_end("Auto-witgen code derivation");
@@ -124,7 +130,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
124130
&self,
125131
can_process: impl CanProcessCall<T>,
126132
cache_key: &CacheKey<T>,
127-
) -> Option<CacheEntry<T>> {
133+
) -> Result<CacheEntry<T>, CompilationError> {
128134
log::debug!(
129135
"Compiling JIT function for\n Machine: {}\n Connection: {}\n Inputs: {:?}{}",
130136
self.machine_name,
@@ -154,10 +160,10 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
154160
// These errors can be pretty verbose and are quite common currently.
155161
log::debug!(
156162
"=> Error generating JIT code: {}\n...",
157-
e.to_string().lines().take(5).join("\n")
163+
e.lines().take(5).join("\n")
158164
);
159-
})
160-
.ok()?;
165+
CompilationError::Other(e)
166+
})?;
161167

162168
log::debug!("=> Success!");
163169
let out_of_bounds_vars = code
@@ -198,7 +204,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
198204
.unwrap();
199205
log::trace!("Compilation done.");
200206

201-
Some(CacheEntry {
207+
Ok(CacheEntry {
202208
function,
203209
range_constraints,
204210
})

executor/src/witgen/machines/block_machine.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::witgen::data_structures::caller_data::CallerData;
1313
use crate::witgen::data_structures::finalizable_data::FinalizableData;
1414
use crate::witgen::data_structures::mutable_state::MutableState;
1515
use crate::witgen::global_constraints::RangeConstraintSet;
16-
use crate::witgen::jit::function_cache::FunctionCache;
16+
use crate::witgen::jit::function_cache::{CompilationError, FunctionCache};
1717
use crate::witgen::jit::witgen_inference::CanProcessCall;
1818
use crate::witgen::processor::{OuterQuery, Processor, SolverState};
1919
use crate::witgen::range_constraints::RangeConstraint;
@@ -183,8 +183,8 @@ impl<'a, T: FieldElement> Machine<'a, T> for BlockMachine<'a, T> {
183183
known_arguments,
184184
fixed_first_input,
185185
) {
186-
Some(entry) => (true, entry.range_constraints.clone()),
187-
None => (false, range_constraints),
186+
Ok(entry) => (true, entry.range_constraints.clone()),
187+
Err(_) => (false, range_constraints),
188188
}
189189
}
190190

@@ -451,16 +451,28 @@ impl<'a, T: FieldElement> BlockMachine<'a, T> {
451451
let fixed_first_input = arguments
452452
.first()
453453
.and_then(|a| a.constant_value().map(|v| (0, v)));
454-
if self
455-
.function_cache
456-
.compile_cached(mutable_state, identity_id, &known_inputs, fixed_first_input)
457-
.is_some()
458-
{
459-
let caller_data = CallerData::new(arguments, range_constraints);
460-
let updates = self.process_lookup_via_jit(mutable_state, identity_id, caller_data)?;
461-
assert!(updates.is_complete());
462-
self.block_count_jit += 1;
463-
return Ok(updates);
454+
match self.function_cache.compile_cached(
455+
mutable_state,
456+
identity_id,
457+
&known_inputs,
458+
fixed_first_input,
459+
) {
460+
Ok(_) => {
461+
let caller_data = CallerData::new(arguments, range_constraints);
462+
let updates =
463+
self.process_lookup_via_jit(mutable_state, identity_id, caller_data)?;
464+
assert!(updates.is_complete());
465+
self.block_count_jit += 1;
466+
return Ok(updates);
467+
}
468+
Err(CompilationError::Other(_e)) => {
469+
// Assuming the JIT compiler is feature-complete, this means that the witness is not
470+
// unique, which could happen e.g. if not all required arguments are provided.
471+
return Ok(EvalValue::incomplete(IncompleteCause::JitCompilationFailed));
472+
}
473+
// If we're on an unsupported field, this won't be fixed in future invocations.
474+
// Fall back to run-time witgen.
475+
Err(CompilationError::UnsupportedField) => {}
464476
}
465477

466478
let outer_query = OuterQuery::new(

0 commit comments

Comments
 (0)