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