Skip to content

Commit 927a87b

Browse files
authored
Combine util functions for jit. (#2430)
The JIT compiler used for computing fixed column and the new code for witgen used two different "util" code snippets. Since the field implementation for the new witgen code is more advanced, it is now moved to the generic jit compiler and used by both. It also adds some types that will later be used by prover functions (PilVec, Callable, etc). Those types were present already in the fixed column jit code.
1 parent 978e745 commit 927a87b

File tree

9 files changed

+214
-206
lines changed

9 files changed

+214
-206
lines changed

executor/src/witgen/jit/compiler.rs

Lines changed: 52 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
use std::{cmp::Ordering, ffi::c_void, mem, sync::Arc};
1+
use std::{cmp::Ordering, ffi::c_void, sync::Arc};
22

33
use itertools::Itertools;
44
use libloading::Library;
55
use powdr_ast::{
66
analyzed::{PolyID, PolynomialType},
77
indent,
88
};
9-
use powdr_number::{FieldElement, KnownField};
9+
use powdr_jit_compiler::util_code::util_code;
10+
use powdr_number::FieldElement;
1011

1112
use crate::witgen::{
12-
data_structures::{finalizable_data::CompactDataRef, mutable_state::MutableState},
13+
data_structures::{
14+
finalizable_data::{ColumnLayout, CompactDataRef},
15+
mutable_state::MutableState,
16+
},
1317
machines::{
1418
profiling::{record_end, record_start},
1519
LookupCell,
@@ -84,14 +88,20 @@ extern "C" fn call_machine<T: FieldElement, Q: QueryCallback<T>>(
8488

8589
/// Compile the given inferred effects into machine code and load it.
8690
pub fn compile_effects<T: FieldElement>(
87-
first_column_id: u64,
88-
column_count: usize,
91+
column_layout: ColumnLayout,
8992
known_inputs: &[Variable],
9093
effects: &[Effect<T, Variable>],
9194
) -> Result<WitgenFunction<T>, String> {
92-
let utils = util_code::<T>(first_column_id, column_count)?;
95+
let utils = util_code::<T>()?;
96+
let interface = interface_code(column_layout);
9397
let witgen_code = witgen_code(known_inputs, effects);
94-
let code = format!("{utils}\n//-------------------------------\n{witgen_code}");
98+
let code = format!(
99+
"{utils}\n\
100+
//-------------------------------\n\
101+
{interface}\n\
102+
//-------------------------------\n\
103+
{witgen_code}"
104+
);
95105

96106
record_start("JIT-compilation");
97107
let start = std::time::Instant::now();
@@ -496,62 +506,20 @@ fn format_row_offset(row_offset: i32) -> String {
496506
}
497507
}
498508

499-
/// Returns the rust code containing utility functions given a first column id and a column count
500-
/// that is used to store the column table.
501-
fn util_code<T: FieldElement>(first_column_id: u64, column_count: usize) -> Result<String, String> {
502-
if !(T::has_direct_repr() && (mem::size_of::<T>() == 8 || mem::size_of::<T>() == 4)) {
503-
return Err(format!(
504-
"Field {}not supported",
505-
T::known_field()
506-
.map(|f| format!("{f} "))
507-
.unwrap_or_default()
508-
));
509-
}
510-
511-
let field_impl = match T::known_field() {
512-
Some(KnownField::GoldilocksField) => {
513-
include_str!("includes/field_goldilocks.rs").to_string()
514-
}
515-
_ => {
516-
let int_type = if mem::size_of::<T>() == 8 {
517-
"u64"
518-
} else {
519-
"u32"
520-
};
521-
let double_int_type = if mem::size_of::<T>() == 8 {
522-
"u128"
523-
} else {
524-
"u64"
525-
};
526-
let modulus = T::modulus();
527-
528-
format!(
529-
"\
530-
#[derive(Clone, Copy, Default)]\n\
531-
#[repr(transparent)]\n\
532-
struct FieldElement({int_type});\n\
533-
\n\
534-
type IntType = {int_type};\n\
535-
type DoubleIntType = {double_int_type};\n\
536-
const MODULUS: IntType = {modulus}_{int_type};\n\
537-
{}\
538-
",
539-
include_str!("includes/field_generic_up_to_64.rs")
540-
)
541-
}
542-
};
543-
544-
let interface = format!(
509+
/// Returns the rust code containing functions and data structures used to
510+
/// interface with witgen functions given the layout of the trace table.
511+
fn interface_code(column_layout: ColumnLayout) -> String {
512+
let ColumnLayout {
513+
column_count,
514+
first_column_id,
515+
} = column_layout;
516+
format!(
545517
"\
546518
const column_count: u64 = {column_count};\n\
547519
const first_column_id: u64 = {first_column_id};\n\
548520
{}",
549521
include_str!("includes/interface.rs")
550-
);
551-
552-
Ok(format!(
553-
"#![allow(non_snake_case, unused_parens, unused_variables)]\n{field_impl}\n{interface}"
554-
))
522+
)
555523
}
556524

557525
#[cfg(test)]
@@ -570,9 +538,24 @@ mod tests {
570538

571539
use super::*;
572540

541+
fn compile_effects(
542+
column_count: usize,
543+
known_inputs: &[Variable],
544+
effects: &[Effect<GoldilocksField, Variable>],
545+
) -> Result<WitgenFunction<GoldilocksField>, String> {
546+
super::compile_effects(
547+
ColumnLayout {
548+
column_count,
549+
first_column_id: 0,
550+
},
551+
known_inputs,
552+
effects,
553+
)
554+
}
555+
573556
#[test]
574557
fn compile_util_code_goldilocks() {
575-
compile_effects::<GoldilocksField>(0, 2, &[], &[]).unwrap();
558+
compile_effects(2, &[], &[]).unwrap();
576559
}
577560

578561
// We would like to test the generic field implementation, but
@@ -728,7 +711,7 @@ extern \"C\" fn witgen(
728711
assignment(&x, number(7)),
729712
assignment(&y, symbol(&x) + number(2)),
730713
];
731-
let f = compile_effects(0, 1, &[], &effects).unwrap();
714+
let f = compile_effects(1, &[], &effects).unwrap();
732715
let mut data = vec![GoldilocksField::from(0); 2];
733716
let mut known = vec![0; 1];
734717
(f.function)(witgen_fun_params(&mut data, &mut known));
@@ -751,8 +734,8 @@ extern \"C\" fn witgen(
751734
let row_count = 2;
752735
let column_count = 2;
753736
let data_len = column_count * row_count;
754-
let f1 = compile_effects(0, column_count, &[], &effects1).unwrap();
755-
let f2 = compile_effects(0, column_count, &[], &effects2).unwrap();
737+
let f1 = compile_effects(column_count, &[], &effects1).unwrap();
738+
let f2 = compile_effects(column_count, &[], &effects2).unwrap();
756739
let mut data = vec![GoldilocksField::from(0); data_len];
757740
let mut known = vec![0; row_count];
758741
(f1.function)(witgen_fun_params(&mut data, &mut known));
@@ -788,7 +771,7 @@ extern \"C\" fn witgen(
788771
assignment(&cell("x", 0, 3), number(8).field_div(&-number(2))),
789772
assignment(&cell("x", 0, 4), (-number(8)).field_div(&-number(2))),
790773
];
791-
let f = compile_effects(0, 1, &[], &effects).unwrap();
774+
let f = compile_effects(1, &[], &effects).unwrap();
792775
let mut data = vec![GoldilocksField::from(0); 5];
793776
let mut known = vec![0; 5];
794777
(f.function)(witgen_fun_params(&mut data, &mut known));
@@ -809,7 +792,7 @@ extern \"C\" fn witgen(
809792
let z = cell("z", 2, 0);
810793
let effects = vec![assignment(&x, symbol(&y) * symbol(&z))];
811794
let known_inputs = vec![y.clone(), z.clone()];
812-
let f = compile_effects(0, 3, &known_inputs, &effects).unwrap();
795+
let f = compile_effects(3, &known_inputs, &effects).unwrap();
813796
let mut data = vec![
814797
GoldilocksField::from(0),
815798
GoldilocksField::from(3),
@@ -830,7 +813,7 @@ extern \"C\" fn witgen(
830813
assignment(&z, symbol(&x).integer_div(&-number(10))),
831814
];
832815
let known_inputs = vec![x.clone()];
833-
let f = compile_effects(0, 3, &known_inputs, &effects).unwrap();
816+
let f = compile_effects(3, &known_inputs, &effects).unwrap();
834817
let mut data = vec![
835818
GoldilocksField::from(23),
836819
GoldilocksField::from(0),
@@ -850,7 +833,7 @@ extern \"C\" fn witgen(
850833
let x_val: GoldilocksField = 7.into();
851834
let mut y_val: GoldilocksField = 9.into();
852835
let effects = vec![assignment(&y, symbol(&x) + number(7))];
853-
let f = compile_effects(0, 1, &[x], &effects).unwrap();
836+
let f = compile_effects(1, &[x], &effects).unwrap();
854837
let mut data = vec![];
855838
let mut known = vec![];
856839
let mut params = vec![LookupCell::Input(&x_val), LookupCell::Output(&mut y_val)];
@@ -894,7 +877,7 @@ extern \"C\" fn witgen(
894877
row_offset: 6,
895878
});
896879
let effects = vec![assignment(&a, symbol(&x))];
897-
let f = compile_effects(0, 1, &[], &effects).unwrap();
880+
let f = compile_effects(1, &[], &effects).unwrap();
898881
let mut data = vec![7.into()];
899882
let mut known = vec![0];
900883
let mut params = vec![];
@@ -954,7 +937,7 @@ extern \"C\" fn witgen(
954937
Effect::Assignment(y.clone(), symbol(&r2)),
955938
];
956939
let known_inputs = vec![];
957-
let f = compile_effects(0, 3, &known_inputs, &effects).unwrap();
940+
let f = compile_effects(3, &known_inputs, &effects).unwrap();
958941
let mut data = vec![GoldilocksField::from(0); 3];
959942
let mut known = vec![0; 1];
960943
let params = WitgenFunctionParams {
@@ -987,7 +970,7 @@ extern \"C\" fn witgen(
987970
vec![assignment(&y, symbol(&x) + number(1))],
988971
vec![assignment(&y, symbol(&x) + number(2))],
989972
)];
990-
let f = compile_effects(0, 1, &[x], &effects).unwrap();
973+
let f = compile_effects(1, &[x], &effects).unwrap();
991974
let mut data = vec![];
992975
let mut known = vec![];
993976

executor/src/witgen/jit/function_cache.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,7 @@ impl<'a, T: FieldElement> FunctionCache<'a, T> {
158158
.collect::<Vec<_>>();
159159

160160
log::trace!("Compiling effects...");
161-
let function = compile_effects(
162-
self.column_layout.first_column_id,
163-
self.column_layout.column_count,
164-
&known_inputs,
165-
&code,
166-
)
167-
.unwrap();
161+
let function = compile_effects(self.column_layout.clone(), &known_inputs, &code).unwrap();
168162
log::trace!("Compilation done.");
169163

170164
Some(CacheEntry {

jit-compiler/src/compiler.rs

Lines changed: 5 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,16 @@ use powdr_ast::{
1313
types::{FunctionType, Type, TypeScheme},
1414
},
1515
};
16-
use powdr_number::{FieldElement, LargeInt};
16+
use powdr_number::FieldElement;
1717

18-
use crate::{codegen::escape_symbol, CompiledPIL, FixedColFunction};
18+
use crate::{codegen::escape_symbol, util_code::util_code, CompiledPIL, FixedColFunction};
1919

2020
pub fn generate_glue_code<T: FieldElement>(
2121
symbols: &[(&str, String)],
2222
analyzed: &Analyzed<T>,
2323
) -> Result<String, String> {
24-
if T::BITS > 64 {
25-
return Err(format!(
26-
"Fields with more than 64 bits not supported, requested {}",
27-
T::BITS,
28-
));
29-
}
24+
let utils = util_code::<T>()?;
25+
3026
let mut glue = String::new();
3127
let int_int_fun: TypeScheme = Type::Function(FunctionType {
3228
params: vec![Type::Int],
@@ -54,128 +50,7 @@ pub fn generate_glue_code<T: FieldElement>(
5450
));
5551
}
5652

57-
Ok(format!(
58-
"{PREAMBLE}\n{}\n{glue}\n",
59-
field_specific_preamble::<T>()
60-
))
61-
}
62-
63-
const PREAMBLE: &str = r#"
64-
#![allow(unused_parens, unused_variables)]
65-
66-
static DEGREE: std::sync::RwLock<Option<ibig::IBig>> = std::sync::RwLock::new(None);
67-
68-
#[no_mangle]
69-
pub extern "C" fn __set_degree(degree: u64) {
70-
*DEGREE.write().unwrap() = Some(ibig::IBig::from(degree));
71-
}
72-
73-
#[derive(Clone, Copy)]
74-
struct FieldElement(u64);
75-
impl From<FieldElement> for u64 {
76-
fn from(x: FieldElement) -> u64 {
77-
x.0
78-
}
79-
}
80-
impl From<ibig::IBig> for FieldElement {
81-
fn from(x: ibig::IBig) -> Self {
82-
FieldElement(u64::try_from(x).unwrap())
83-
}
84-
}
85-
impl From<FieldElement> for ibig::IBig {
86-
fn from(x: FieldElement) -> Self {
87-
ibig::IBig::from(x.0)
88-
}
89-
}
90-
91-
#[derive(Clone)]
92-
enum Callable<Args, Ret> {
93-
Fn(fn(Args) -> Ret),
94-
Closure(std::sync::Arc<dyn Fn(Args) -> Ret + Send + Sync>),
95-
}
96-
impl<Args, Ret> Callable<Args, Ret> {
97-
#[inline(always)]
98-
fn call(&self, args: Args) -> Ret {
99-
match self {
100-
Callable::Fn(f) => f(args),
101-
Callable::Closure(f) => f(args),
102-
}
103-
}
104-
}
105-
106-
#[derive(Clone)]
107-
struct PilVec<T>(std::sync::Arc<Vec<T>>);
108-
109-
impl<T> PilVec<T> {
110-
fn len(&self) -> usize {
111-
self.0.len()
112-
}
113-
}
114-
impl<T> From<Vec<T>> for PilVec<T> {
115-
fn from(v: Vec<T>) -> Self {
116-
PilVec(std::sync::Arc::new(v))
117-
}
118-
}
119-
impl<T> std::ops::Index<usize> for PilVec<T> {
120-
type Output = T;
121-
122-
#[inline]
123-
fn index(&self, index: usize) -> &T {
124-
&self.0[index]
125-
}
126-
}
127-
128-
129-
trait Add {
130-
fn add(a: Self, b: Self) -> Self;
131-
}
132-
133-
impl Add for ibig::IBig {
134-
fn add(a: Self, b: Self) -> Self { a + b }
135-
}
136-
137-
impl<T: Clone> Add for PilVec<T> {
138-
fn add(a: Self, b: Self) -> Self {
139-
// TODO for a regular "push" or array::map this is very slow.
140-
// We could optimize this by sharing a larger backing vector
141-
// across prefix instances, allowing to extend the backing vector if
142-
// our view is the full vector.
143-
PilVec(std::sync::Arc::new(
144-
a.0.as_ref().iter().chain(b.0.as_ref()).cloned().collect::<Vec<_>>()
145-
))
146-
}
147-
}
148-
149-
trait FromLiteral {
150-
fn from_u64(x: u64) -> Self;
151-
}
152-
impl FromLiteral for ibig::IBig {
153-
fn from_u64(x: u64) -> Self { ibig::IBig::from(x) }
154-
}
155-
impl FromLiteral for FieldElement {
156-
fn from_u64(x: u64) -> Self { FieldElement::from(x) }
157-
}
158-
159-
"#;
160-
161-
fn field_specific_preamble<T: FieldElement>() -> String {
162-
let modulus = u64::try_from(T::modulus().to_arbitrary_integer()).unwrap();
163-
format!(
164-
r#"
165-
impl From<u64> for FieldElement {{
166-
fn from(x: u64) -> Self {{
167-
// TODO this is inefficient.
168-
FieldElement(x % {modulus}_u64)
169-
}}
170-
}}
171-
impl Add for FieldElement {{
172-
fn add(a: Self, b: Self) -> Self {{
173-
// TODO this is inefficient.
174-
Self(u64::try_from((u128::from(a.0) + u128::from(b.0)) % u128::from({modulus}_u64)).unwrap())
175-
}}
176-
}}
177-
"#
178-
)
53+
Ok(format!("{utils}\n{glue}\n",))
17954
}
18055

18156
const CARGO_TOML: &str = r#"

0 commit comments

Comments
 (0)