1use core::fmt::Debug;
2use std::marker::PhantomData;
3
4use openvm_stark_backend::p3_field::{Field, FieldExtensionAlgebra, PrimeField};
5use serde::{Deserialize, Serialize};
6
7use self::opcodes::ConstraintOpcode;
8use crate::{
9 ir::{Config, DslIr},
10 prelude::TracedVec,
11};
12
13#[cfg(feature = "halo2-compiler")]
14pub mod halo2;
15
16pub mod opcodes;
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Constraint {
21 pub opcode: ConstraintOpcode,
22 pub args: Vec<Vec<String>>,
23}
24
25#[derive(Debug, Clone, Default)]
27pub struct ConstraintCompiler<C: Config> {
28 pub allocator: usize,
29 pub phantom: PhantomData<C>,
30}
31
32impl<C: Config + Debug> ConstraintCompiler<C> {
33 pub fn alloc_id(&mut self) -> String {
35 let id = self.allocator;
36 self.allocator += 1;
37 format!("backend{}", id)
38 }
39
40 pub fn alloc_v(&mut self, constraints: &mut Vec<Constraint>, value: C::N) -> String {
42 let tmp_id = self.alloc_id();
43 constraints.push(Constraint {
44 opcode: ConstraintOpcode::ImmV,
45 args: vec![
46 vec![tmp_id.clone()],
47 vec![value.as_canonical_biguint().to_string()],
48 ],
49 });
50 tmp_id
51 }
52
53 pub fn alloc_f(&mut self, constraints: &mut Vec<Constraint>, value: C::F) -> String {
55 let tmp_id = self.alloc_id();
56 constraints.push(Constraint {
57 opcode: ConstraintOpcode::ImmF,
58 args: vec![
59 vec![tmp_id.clone()],
60 vec![value.as_canonical_biguint().to_string()],
61 ],
62 });
63 tmp_id
64 }
65
66 pub fn alloc_e(&mut self, constraints: &mut Vec<Constraint>, value: C::EF) -> String {
68 let tmp_id = self.alloc_id();
69 constraints.push(Constraint {
70 opcode: ConstraintOpcode::ImmE,
71 args: vec![
72 vec![tmp_id.clone()],
73 value
74 .as_base_slice()
75 .iter()
76 .map(|x| x.as_canonical_biguint().to_string())
77 .collect(),
78 ],
79 });
80 tmp_id
81 }
82
83 pub fn emit(&mut self, operations: TracedVec<DslIr<C>>) -> Vec<Constraint> {
85 let mut constraints: Vec<Constraint> = Vec::new();
86 for (instruction, _) in operations {
87 match instruction {
88 DslIr::ImmV(a, b) => constraints.push(Constraint {
89 opcode: ConstraintOpcode::ImmV,
90 args: vec![vec![a.id()], vec![b.as_canonical_biguint().to_string()]],
91 }),
92 DslIr::ImmF(a, b) => constraints.push(Constraint {
93 opcode: ConstraintOpcode::ImmF,
94 args: vec![vec![a.id()], vec![b.as_canonical_biguint().to_string()]],
95 }),
96 DslIr::ImmE(a, b) => constraints.push(Constraint {
97 opcode: ConstraintOpcode::ImmE,
98 args: vec![
99 vec![a.id()],
100 b.as_base_slice()
101 .iter()
102 .map(|x| x.as_canonical_biguint().to_string())
103 .collect(),
104 ],
105 }),
106 DslIr::AddV(a, b, c) => constraints.push(Constraint {
107 opcode: ConstraintOpcode::AddV,
108 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
109 }),
110 DslIr::AddVI(a, b, c) => {
111 let tmp = self.alloc_v(&mut constraints, c);
112 constraints.push(Constraint {
113 opcode: ConstraintOpcode::AddV,
114 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
115 });
116 }
117 DslIr::AddF(a, b, c) => constraints.push(Constraint {
118 opcode: ConstraintOpcode::AddF,
119 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
120 }),
121 DslIr::AddFI(a, b, c) => {
122 let tmp = self.alloc_f(&mut constraints, c);
123 constraints.push(Constraint {
124 opcode: ConstraintOpcode::AddF,
125 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
126 });
127 }
128 DslIr::AddE(a, b, c) => constraints.push(Constraint {
129 opcode: ConstraintOpcode::AddE,
130 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
131 }),
132 DslIr::AddEF(a, b, c) => constraints.push(Constraint {
133 opcode: ConstraintOpcode::AddEF,
134 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
135 }),
136 DslIr::AddEFI(a, b, c) => {
137 let tmp = self.alloc_f(&mut constraints, c);
138 constraints.push(Constraint {
139 opcode: ConstraintOpcode::AddEF,
140 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
141 });
142 }
143 DslIr::AddEI(a, b, c) => {
144 let tmp = self.alloc_e(&mut constraints, c);
145 constraints.push(Constraint {
146 opcode: ConstraintOpcode::AddE,
147 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
148 });
149 }
150 DslIr::AddEFFI(a, b, c) => {
151 let tmp = self.alloc_e(&mut constraints, c);
152 constraints.push(Constraint {
153 opcode: ConstraintOpcode::AddEF,
154 args: vec![vec![a.id()], vec![tmp], vec![b.id()]],
155 });
156 }
157 DslIr::SubV(a, b, c) => constraints.push(Constraint {
158 opcode: ConstraintOpcode::SubV,
159 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
160 }),
161 DslIr::SubF(a, b, c) => constraints.push(Constraint {
162 opcode: ConstraintOpcode::SubF,
163 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
164 }),
165 DslIr::SubE(a, b, c) => constraints.push(Constraint {
166 opcode: ConstraintOpcode::SubE,
167 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
168 }),
169 DslIr::SubEF(a, b, c) => constraints.push(Constraint {
170 opcode: ConstraintOpcode::SubEF,
171 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
172 }),
173 DslIr::SubEI(a, b, c) => {
174 let tmp = self.alloc_e(&mut constraints, c);
175 constraints.push(Constraint {
176 opcode: ConstraintOpcode::SubE,
177 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
178 });
179 }
180 DslIr::SubEIN(a, b, c) => {
181 let tmp = self.alloc_e(&mut constraints, b);
182 constraints.push(Constraint {
183 opcode: ConstraintOpcode::SubE,
184 args: vec![vec![a.id()], vec![tmp], vec![c.id()]],
185 });
186 }
187 DslIr::MulV(a, b, c) => constraints.push(Constraint {
188 opcode: ConstraintOpcode::MulV,
189 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
190 }),
191 DslIr::MulVI(a, b, c) => {
192 let tmp = self.alloc_v(&mut constraints, c);
193 constraints.push(Constraint {
194 opcode: ConstraintOpcode::MulV,
195 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
196 });
197 }
198 DslIr::MulF(a, b, c) => constraints.push(Constraint {
199 opcode: ConstraintOpcode::MulF,
200 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
201 }),
202 DslIr::MulE(a, b, c) => constraints.push(Constraint {
203 opcode: ConstraintOpcode::MulE,
204 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
205 }),
206 DslIr::MulEI(a, b, c) => {
207 let tmp = self.alloc_e(&mut constraints, c);
208 constraints.push(Constraint {
209 opcode: ConstraintOpcode::MulE,
210 args: vec![vec![a.id()], vec![b.id()], vec![tmp]],
211 });
212 }
213 DslIr::MulEF(a, b, c) => constraints.push(Constraint {
214 opcode: ConstraintOpcode::MulEF,
215 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
216 }),
217 DslIr::DivFIN(a, b, c) => {
218 let tmp = self.alloc_f(&mut constraints, b.inverse());
219 constraints.push(Constraint {
220 opcode: ConstraintOpcode::MulF,
221 args: vec![vec![a.id()], vec![tmp], vec![c.id()]],
222 });
223 }
224 DslIr::DivE(a, b, c) => constraints.push(Constraint {
225 opcode: ConstraintOpcode::DivE,
226 args: vec![vec![a.id()], vec![b.id()], vec![c.id()]],
227 }),
228 DslIr::DivEIN(a, b, c) => {
229 let tmp = self.alloc_e(&mut constraints, b);
230 constraints.push(Constraint {
231 opcode: ConstraintOpcode::DivE,
232 args: vec![vec![a.id()], vec![tmp], vec![c.id()]],
233 });
234 }
235 DslIr::NegE(a, b) => constraints.push(Constraint {
236 opcode: ConstraintOpcode::NegE,
237 args: vec![vec![a.id()], vec![b.id()]],
238 }),
239 DslIr::CircuitNum2BitsF(value, output) => constraints.push(Constraint {
240 opcode: ConstraintOpcode::Num2BitsF,
241 args: vec![output.iter().map(|x| x.id()).collect(), vec![value.id()]],
242 }),
243 DslIr::CircuitPoseidon2Permute(state) => constraints.push(Constraint {
244 opcode: ConstraintOpcode::Permute,
245 args: state.iter().map(|x| vec![x.id()]).collect(),
246 }),
247 DslIr::CircuitSelectV(cond, a, b, out) => {
248 constraints.push(Constraint {
249 opcode: ConstraintOpcode::SelectV,
250 args: vec![vec![out.id()], vec![cond.id()], vec![a.id()], vec![b.id()]],
251 });
252 }
253 DslIr::CircuitSelectF(cond, a, b, out) => {
254 constraints.push(Constraint {
255 opcode: ConstraintOpcode::SelectF,
256 args: vec![vec![out.id()], vec![cond.id()], vec![a.id()], vec![b.id()]],
257 });
258 }
259 DslIr::CircuitSelectE(cond, a, b, out) => {
260 constraints.push(Constraint {
261 opcode: ConstraintOpcode::SelectE,
262 args: vec![vec![out.id()], vec![cond.id()], vec![a.id()], vec![b.id()]],
263 });
264 }
265 DslIr::CircuitExt2Felt(a, b) => {
266 constraints.push(Constraint {
267 opcode: ConstraintOpcode::Ext2Felt,
268 args: vec![
269 vec![a[0].id()],
270 vec![a[1].id()],
271 vec![a[2].id()],
272 vec![a[3].id()],
273 vec![b.id()],
274 ],
275 });
276 }
277 DslIr::AssertEqV(a, b) => constraints.push(Constraint {
278 opcode: ConstraintOpcode::AssertEqV,
279 args: vec![vec![a.id()], vec![b.id()]],
280 }),
281 DslIr::AssertEqVI(a, b) => {
282 let tmp = self.alloc_v(&mut constraints, b);
283 constraints.push(Constraint {
284 opcode: ConstraintOpcode::AssertEqV,
285 args: vec![vec![a.id()], vec![tmp]],
286 });
287 }
288 DslIr::AssertEqF(a, b) => constraints.push(Constraint {
289 opcode: ConstraintOpcode::AssertEqF,
290 args: vec![vec![a.id()], vec![b.id()]],
291 }),
292 DslIr::AssertEqFI(a, b) => {
293 let tmp = self.alloc_f(&mut constraints, b);
294 constraints.push(Constraint {
295 opcode: ConstraintOpcode::AssertEqF,
296 args: vec![vec![a.id()], vec![tmp]],
297 });
298 }
299 DslIr::AssertEqE(a, b) => constraints.push(Constraint {
300 opcode: ConstraintOpcode::AssertEqE,
301 args: vec![vec![a.id()], vec![b.id()]],
302 }),
303 DslIr::AssertEqEI(a, b) => {
304 let tmp = self.alloc_e(&mut constraints, b);
305 constraints.push(Constraint {
306 opcode: ConstraintOpcode::AssertEqE,
307 args: vec![vec![a.id()], vec![tmp]],
308 });
309 }
310 DslIr::PrintV(a) => constraints.push(Constraint {
311 opcode: ConstraintOpcode::PrintV,
312 args: vec![vec![a.id()]],
313 }),
314 DslIr::PrintF(a) => constraints.push(Constraint {
315 opcode: ConstraintOpcode::PrintF,
316 args: vec![vec![a.id()]],
317 }),
318 DslIr::PrintE(a) => constraints.push(Constraint {
319 opcode: ConstraintOpcode::PrintE,
320 args: vec![vec![a.id()]],
321 }),
322 DslIr::WitnessVar(a, b) => constraints.push(Constraint {
323 opcode: ConstraintOpcode::WitnessV,
324 args: vec![vec![a.id()], vec![b.to_string()]],
325 }),
326 DslIr::WitnessFelt(a, b) => constraints.push(Constraint {
327 opcode: ConstraintOpcode::WitnessF,
328 args: vec![vec![a.id()], vec![b.to_string()]],
329 }),
330 DslIr::WitnessExt(a, b) => constraints.push(Constraint {
331 opcode: ConstraintOpcode::WitnessE,
332 args: vec![vec![a.id()], vec![b.to_string()]],
333 }),
334 DslIr::CircuitFelts2Ext(a, b) => constraints.push(Constraint {
335 opcode: ConstraintOpcode::CircuitFelts2Ext,
336 args: vec![
337 vec![b.id()],
338 vec![a[0].id()],
339 vec![a[1].id()],
340 vec![a[2].id()],
341 vec![a[3].id()],
342 ],
343 }),
344 _ => panic!("unsupported {:?}", instruction),
345 };
346 }
347 constraints
348 }
349}