1use std::collections::BTreeMap;
64
65use acir::{
66 AcirField,
67 circuit::{
68 Circuit, Opcode,
69 brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs},
70 opcodes::{BlackBoxFuncCall, FunctionInput},
71 },
72 native_types::{Expression, Witness},
73};
74use indexmap::IndexMap;
75
76mod csat;
77mod merge_expressions;
78
79use csat::CSatTransformer;
80use merge_expressions::MergeExpressionsOptimizer;
81
82use tracing::info;
83
84use super::RangeOptimizer;
85
86const DEFAULT_MAX_TRANSFORMER_PASSES: usize = 3;
88const DEFAULT_EXPRESSION_WIDTH: usize = 4;
89
90#[tracing::instrument(level = "trace", name = "transform_acir", skip(acir, acir_opcode_positions))]
100pub(super) fn transform_internal<F: AcirField>(
101 mut acir: Circuit<F>,
102 mut acir_opcode_positions: Vec<usize>,
103 brillig_side_effects: &BTreeMap<BrilligFunctionId, bool>,
104 max_transformer_passes_or_default: Option<usize>,
105) -> (Circuit<F>, Vec<usize>, bool) {
106 if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::BrilligCall { .. }) {
107 info!("Program is fully unconstrained, skipping transformation pass");
108 return (acir, acir_opcode_positions, true);
109 }
110
111 let mut prev_opcode_count = acir.opcodes.len();
117
118 let mut opcode_count_stabilized = false;
119
120 let max_transformer_passes =
121 max_transformer_passes_or_default.unwrap_or(DEFAULT_MAX_TRANSFORMER_PASSES);
122
123 for _ in 0..max_transformer_passes {
126 info!("Number of opcodes {}", acir.opcodes.len());
127 let (new_acir, new_acir_opcode_positions) =
128 transform_internal_once(acir, acir_opcode_positions, brillig_side_effects);
129
130 acir = new_acir;
131 acir_opcode_positions = new_acir_opcode_positions;
132
133 let new_opcode_count = acir.opcodes.len();
134
135 if new_opcode_count == prev_opcode_count {
136 opcode_count_stabilized = true;
137 break;
138 }
139 prev_opcode_count = new_opcode_count;
140 }
141
142 (acir, acir_opcode_positions, opcode_count_stabilized)
143}
144
145#[tracing::instrument(
154 level = "trace",
155 name = "transform_acir_once",
156 skip(acir, acir_opcode_positions)
157)]
158fn transform_internal_once<F: AcirField>(
159 mut acir: Circuit<F>,
160 acir_opcode_positions: Vec<usize>,
161 brillig_side_effects: &BTreeMap<BrilligFunctionId, bool>,
162) -> (Circuit<F>, Vec<usize>) {
163 let csat_span = tracing::trace_span!("csat_transformer").entered();
168 let mut transformer = CSatTransformer::new(DEFAULT_EXPRESSION_WIDTH);
169 for value in acir.circuit_arguments() {
170 transformer.mark_solvable(value);
171 }
172
173 let mut new_acir_opcode_positions: Vec<usize> = Vec::with_capacity(acir_opcode_positions.len());
174 let mut transformed_opcodes = Vec::new();
177
178 let mut next_witness_index = max_witness(&acir).witness_index() + 1;
179 let mut intermediate_variables: IndexMap<Expression<F>, (F, Witness)> = IndexMap::new();
182 for (index, opcode) in acir.opcodes.into_iter().enumerate() {
183 match opcode {
184 Opcode::AssertZero(arith_expr) => {
185 let len = intermediate_variables.len();
186
187 let arith_expr = transformer.transform(
188 arith_expr,
189 &mut intermediate_variables,
190 &mut next_witness_index,
191 );
192
193 let mut new_opcodes = Vec::new();
194 for (g, (norm, w)) in intermediate_variables.iter().skip(len) {
195 let mut intermediate_opcode = g * *norm;
197 intermediate_opcode.linear_combinations.push((-F::one(), *w));
199 intermediate_opcode.sort();
200 new_opcodes.push(intermediate_opcode);
201 }
202 new_opcodes.push(arith_expr);
203 for opcode in new_opcodes {
204 new_acir_opcode_positions.push(acir_opcode_positions[index]);
205 transformed_opcodes.push(Opcode::AssertZero(opcode));
206 }
207 }
208 Opcode::BlackBoxFuncCall(ref func) => {
209 for witness in func.get_outputs_vec() {
210 transformer.mark_solvable(witness);
211 }
212
213 new_acir_opcode_positions.push(acir_opcode_positions[index]);
214 transformed_opcodes.push(opcode);
215 }
216 Opcode::MemoryInit { .. } => {
217 new_acir_opcode_positions.push(acir_opcode_positions[index]);
219 transformed_opcodes.push(opcode);
220 }
221 Opcode::MemoryOp { ref op, .. } => {
222 transformer.mark_solvable(op.value);
223 new_acir_opcode_positions.push(acir_opcode_positions[index]);
224 transformed_opcodes.push(opcode);
225 }
226 Opcode::BrilligCall { ref outputs, .. } => {
227 for output in outputs {
228 match output {
229 BrilligOutputs::Simple(witness) => transformer.mark_solvable(*witness),
230 BrilligOutputs::Array(witnesses) => {
231 for witness in witnesses {
232 transformer.mark_solvable(*witness);
233 }
234 }
235 }
236 }
237
238 new_acir_opcode_positions.push(acir_opcode_positions[index]);
239 transformed_opcodes.push(opcode);
240 }
241 Opcode::Call { ref outputs, .. } => {
242 for witness in outputs {
243 transformer.mark_solvable(*witness);
244 }
245
246 new_acir_opcode_positions.push(acir_opcode_positions[index]);
249 transformed_opcodes.push(opcode);
250 }
251 }
252 }
253
254 acir = Circuit {
255 opcodes: transformed_opcodes,
256 ..acir
258 };
259 drop(csat_span);
260
261 let mut merge_optimizer = MergeExpressionsOptimizer::new();
263
264 let (opcodes, new_acir_opcode_positions) =
265 merge_optimizer.eliminate_intermediate_variable(&acir, new_acir_opcode_positions);
266
267 acir = Circuit {
268 opcodes,
269 ..acir
271 };
272
273 let range_optimizer = RangeOptimizer::new(acir, brillig_side_effects);
277 let (acir, new_acir_opcode_positions) =
278 range_optimizer.replace_redundant_ranges(new_acir_opcode_positions);
279
280 (acir, new_acir_opcode_positions)
281}
282
283fn max_witness<F: AcirField>(circuit: &Circuit<F>) -> Witness {
285 let mut witnesses = WitnessFolder::new(Witness::default(), |state, witness| {
286 *state = witness.max(*state);
287 });
288 witnesses.fold_circuit(circuit);
289 witnesses.into_state()
290}
291
292struct WitnessFolder<S, A> {
294 state: S,
295 accumulate: A,
296}
297
298impl<S, A> WitnessFolder<S, A>
299where
300 A: Fn(&mut S, Witness),
301{
302 fn new(init: S, accumulate: A) -> Self {
304 Self { state: init, accumulate }
305 }
306
307 fn into_state(self) -> S {
309 self.state
310 }
311
312 fn fold_circuit<F: AcirField>(&mut self, circuit: &Circuit<F>) {
314 self.fold_many(circuit.private_parameters.iter());
315 self.fold_many(circuit.public_parameters.0.iter());
316 self.fold_many(circuit.return_values.0.iter());
317 for opcode in &circuit.opcodes {
318 self.fold_opcode(opcode);
319 }
320 }
321
322 fn fold(&mut self, witness: Witness) {
324 (self.accumulate)(&mut self.state, witness);
325 }
326
327 fn fold_many<'w, I: Iterator<Item = &'w Witness>>(&mut self, witnesses: I) {
329 for witness in witnesses {
330 self.fold(*witness);
331 }
332 }
333
334 fn fold_opcode<F: AcirField>(&mut self, opcode: &Opcode<F>) {
336 match opcode {
337 Opcode::AssertZero(expr) => {
338 self.fold_expr(expr);
339 }
340 Opcode::BlackBoxFuncCall(call) => self.fold_blackbox(call),
341 Opcode::MemoryOp { block_id: _, op } => {
342 self.fold(op.index);
343 self.fold(op.value);
344 }
345 Opcode::MemoryInit { block_id: _, init, block_type: _ } => {
346 for witness in init {
347 self.fold(*witness);
348 }
349 }
350 Opcode::BrilligCall { id: _, inputs, outputs, predicate } => {
353 self.fold_expr(predicate);
354 self.fold_brillig_inputs(inputs);
355 self.fold_brillig_outputs(outputs);
356 }
357 Opcode::Call { id: _, inputs, outputs, predicate } => {
358 self.fold_expr(predicate);
359 self.fold_many(inputs.iter());
360 self.fold_many(outputs.iter());
361 }
362 }
363 }
364
365 fn fold_expr<F: AcirField>(&mut self, expr: &Expression<F>) {
366 for i in &expr.mul_terms {
367 self.fold(i.1);
368 self.fold(i.2);
369 }
370 for i in &expr.linear_combinations {
371 self.fold(i.1);
372 }
373 }
374
375 fn fold_brillig_inputs<F: AcirField>(&mut self, inputs: &[BrilligInputs<F>]) {
376 for input in inputs {
377 match input {
378 BrilligInputs::Single(expr) => {
379 self.fold_expr(expr);
380 }
381 BrilligInputs::Array(exprs) => {
382 for expr in exprs {
383 self.fold_expr(expr);
384 }
385 }
386 BrilligInputs::MemoryArray(_) => {}
387 }
388 }
389 }
390
391 fn fold_brillig_outputs(&mut self, outputs: &[BrilligOutputs]) {
392 for output in outputs {
393 match output {
394 BrilligOutputs::Simple(witness) => {
395 self.fold(*witness);
396 }
397 BrilligOutputs::Array(witnesses) => self.fold_many(witnesses.iter()),
398 }
399 }
400 }
401
402 fn fold_blackbox<F: AcirField>(&mut self, call: &BlackBoxFuncCall<F>) {
403 match call {
404 BlackBoxFuncCall::AES128Encrypt { inputs, iv, key, outputs } => {
405 self.fold_inputs(inputs.as_slice());
406 self.fold_inputs(iv.as_slice());
407 self.fold_inputs(key.as_slice());
408 self.fold_many(outputs.iter());
409 }
410 BlackBoxFuncCall::AND { lhs, rhs, output, .. } => {
411 self.fold_input(lhs);
412 self.fold_input(rhs);
413 self.fold(*output);
414 }
415 BlackBoxFuncCall::XOR { lhs, rhs, output, .. } => {
416 self.fold_input(lhs);
417 self.fold_input(rhs);
418 self.fold(*output);
419 }
420 BlackBoxFuncCall::RANGE { input, .. } => {
421 self.fold_input(input);
422 }
423 BlackBoxFuncCall::Blake2s { inputs, outputs } => {
424 self.fold_inputs(inputs.as_slice());
425 self.fold_many(outputs.iter());
426 }
427 BlackBoxFuncCall::Blake3 { inputs, outputs } => {
428 self.fold_inputs(inputs.as_slice());
429 self.fold_many(outputs.iter());
430 }
431 BlackBoxFuncCall::EcdsaSecp256k1 {
432 public_key_x,
433 public_key_y,
434 signature,
435 hashed_message,
436 output,
437 predicate,
438 } => {
439 self.fold_inputs(public_key_x.as_slice());
440 self.fold_inputs(public_key_y.as_slice());
441 self.fold_inputs(signature.as_slice());
442 self.fold_inputs(hashed_message.as_slice());
443 self.fold(*output);
444 self.fold_input(predicate);
445 }
446 BlackBoxFuncCall::EcdsaSecp256r1 {
447 public_key_x,
448 public_key_y,
449 signature,
450 hashed_message,
451 output,
452 predicate,
453 } => {
454 self.fold_inputs(public_key_x.as_slice());
455 self.fold_inputs(public_key_y.as_slice());
456 self.fold_inputs(signature.as_slice());
457 self.fold_inputs(hashed_message.as_slice());
458 self.fold(*output);
459 self.fold_input(predicate);
460 }
461 BlackBoxFuncCall::MultiScalarMul { points, scalars, predicate, outputs } => {
462 self.fold_inputs(points.as_slice());
463 self.fold_inputs(scalars.as_slice());
464 self.fold_input(predicate);
465 let (x, y) = outputs;
466 self.fold(*x);
467 self.fold(*y);
468 }
469 BlackBoxFuncCall::EmbeddedCurveAdd { input1, input2, predicate, outputs } => {
470 self.fold_inputs(input1.as_slice());
471 self.fold_inputs(input2.as_slice());
472 self.fold_input(predicate);
473 let (x, y) = outputs;
474 self.fold(*x);
475 self.fold(*y);
476 }
477 BlackBoxFuncCall::Keccakf1600 { inputs, outputs } => {
478 self.fold_inputs(inputs.as_slice());
479 self.fold_many(outputs.iter());
480 }
481 BlackBoxFuncCall::RecursiveAggregation {
482 verification_key,
483 proof,
484 public_inputs,
485 key_hash,
486 proof_type: _,
487 predicate,
488 } => {
489 self.fold_inputs(verification_key.as_slice());
490 self.fold_inputs(proof.as_slice());
491 self.fold_inputs(public_inputs.as_slice());
492 self.fold_input(key_hash);
493 self.fold_input(predicate);
494 }
495 BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs } => {
496 self.fold_inputs(inputs.as_slice());
497 self.fold_many(outputs.iter());
498 }
499 BlackBoxFuncCall::Sha256Compression { inputs, hash_values, outputs } => {
500 self.fold_inputs(inputs.as_slice());
501 self.fold_inputs(hash_values.as_slice());
502 self.fold_many(outputs.iter());
503 }
504 }
505 }
506
507 fn fold_inputs<F: AcirField>(&mut self, inputs: &[FunctionInput<F>]) {
508 for input in inputs {
509 self.fold_input(input);
510 }
511 }
512
513 fn fold_input<F: AcirField>(&mut self, input: &FunctionInput<F>) {
514 if let FunctionInput::Witness(witness) = input {
515 self.fold(*witness);
516 }
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::transform_internal;
523 use crate::compiler::CircuitSimulator;
524 use acir::FieldElement;
525 use acir::circuit::{Circuit, Opcode, brillig::BrilligFunctionId};
526 use std::collections::BTreeMap;
527
528 #[test]
529 fn assert_zero_solving_for_a_multiplication_unknown_is_kept_intact() {
530 let src = r#"private parameters: [w0, w2, w3, w4, w5]
539 public parameters: []
540 return values: [w1]
541 ASSERT w0*w1 = w2 + w3 + w4 + w5
542 "#;
543 let acir = Circuit::<FieldElement>::from_str(src).unwrap();
544 assert!(CircuitSimulator::check_circuit(&acir).is_none());
545
546 let acir_opcode_positions = (0..acir.opcodes.len()).collect();
547 let (transformed, _, _) =
548 transform_internal(acir, acir_opcode_positions, &BTreeMap::new(), None);
549
550 assert_eq!(transformed.opcodes.len(), 1);
552 let Opcode::AssertZero(expr) = &transformed.opcodes[0] else {
553 panic!("expected a single AssertZero opcode");
554 };
555 assert_eq!(expr.mul_terms.len(), 1, "the multiplication term must be preserved");
556 assert!(expr.width() > 4, "the opcode is wider than the target width, as expected");
557
558 assert!(CircuitSimulator::check_circuit(&transformed).is_none());
560 }
561
562 #[test]
563 fn test_max_transformer_passes() {
564 let formatted_acir = r#"private parameters: [w0]
565 public parameters: []
566 return values: [w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30, w31]
567 BRILLIG CALL func: 0, predicate: 1, inputs: [w0, 31, 256], outputs: [w32, w33, w34, w35, w36, w37, w38, w39, w40, w41, w42, w43, w44, w45, w46, w47, w48, w49, w50, w51, w52, w53, w54, w55, w56, w57, w58, w59, w60, w61, w62]
568 BLACKBOX::RANGE input: w35, bits: 8
569 BLACKBOX::RANGE input: w36, bits: 8
570 BLACKBOX::RANGE input: w37, bits: 8
571 BLACKBOX::RANGE input: w38, bits: 8
572 BLACKBOX::RANGE input: w39, bits: 8
573 BLACKBOX::RANGE input: w40, bits: 8
574 BLACKBOX::RANGE input: w41, bits: 8
575 BLACKBOX::RANGE input: w42, bits: 8
576 BLACKBOX::RANGE input: w43, bits: 8
577 BLACKBOX::RANGE input: w44, bits: 8
578 BLACKBOX::RANGE input: w45, bits: 8
579 BLACKBOX::RANGE input: w46, bits: 8
580 BLACKBOX::RANGE input: w47, bits: 8
581 BLACKBOX::RANGE input: w48, bits: 8
582 BLACKBOX::RANGE input: w49, bits: 8
583 BLACKBOX::RANGE input: w50, bits: 8
584 BLACKBOX::RANGE input: w51, bits: 8
585 BLACKBOX::RANGE input: w52, bits: 8
586 BLACKBOX::RANGE input: w53, bits: 8
587 BLACKBOX::RANGE input: w54, bits: 8
588 BLACKBOX::RANGE input: w55, bits: 8
589 BLACKBOX::RANGE input: w56, bits: 8
590 BLACKBOX::RANGE input: w57, bits: 8
591 BLACKBOX::RANGE input: w58, bits: 8
592 BLACKBOX::RANGE input: w59, bits: 8
593 BLACKBOX::RANGE input: w60, bits: 8
594 BLACKBOX::RANGE input: w61, bits: 8
595 BLACKBOX::RANGE input: w62, bits: 8
596 ASSERT w32 = w0 - 256*w33 - 65536*w34 - 16777216*w35 - 4294967296*w36 - 1099511627776*w37 - 281474976710656*w38 - 72057594037927936*w39 - 18446744073709551616*w40 - 4722366482869645213696*w41 - 1208925819614629174706176*w42 - 309485009821345068724781056*w43 - 79228162514264337593543950336*w44 - 20282409603651670423947251286016*w45 - 5192296858534827628530496329220096*w46 - 1329227995784915872903807060280344576*w47 - 340282366920938463463374607431768211456*w48 - 87112285931760246646623899502532662132736*w49 - 22300745198530623141535718272648361505980416*w50 - 5708990770823839524233143877797980545530986496*w51 - 1461501637330902918203684832716283019655932542976*w52 - 374144419156711147060143317175368453031918731001856*w53 - 95780971304118053647396689196894323976171195136475136*w54 - 24519928653854221733733552434404946937899825954937634816*w55 - 6277101735386680763835789423207666416102355444464034512896*w56 - 1606938044258990275541962092341162602522202993782792835301376*w57 - 411376139330301510538742295639337626245683966408394965837152256*w58 - 105312291668557186697918027683670432318895095400549111254310977536*w59 - 26959946667150639794667015087019630673637144422540572481103610249216*w60 - 6901746346790563787434755862277025452451108972170386555162524223799296*w61 - 1766847064778384329583297500742918515827483896875618958121606201292619776*w62
597 ASSERT w32 = 60
598 ASSERT w33 = 33
599 ASSERT w34 = 31
600 ASSERT w0 = 16777216*w35 + 4294967296*w36 + 1099511627776*w37 + 281474976710656*w38 + 72057594037927936*w39 + 18446744073709551616*w40 + 4722366482869645213696*w41 + 1208925819614629174706176*w42 + 309485009821345068724781056*w43 + 79228162514264337593543950336*w44 + 20282409603651670423947251286016*w45 + 5192296858534827628530496329220096*w46 + 1329227995784915872903807060280344576*w47 + 340282366920938463463374607431768211456*w48 + 87112285931760246646623899502532662132736*w49 + 22300745198530623141535718272648361505980416*w50 + 5708990770823839524233143877797980545530986496*w51 + 1461501637330902918203684832716283019655932542976*w52 + 374144419156711147060143317175368453031918731001856*w53 + 95780971304118053647396689196894323976171195136475136*w54 + 24519928653854221733733552434404946937899825954937634816*w55 + 6277101735386680763835789423207666416102355444464034512896*w56 + 1606938044258990275541962092341162602522202993782792835301376*w57 + 411376139330301510538742295639337626245683966408394965837152256*w58 + 105312291668557186697918027683670432318895095400549111254310977536*w59 + 26959946667150639794667015087019630673637144422540572481103610249216*w60 + 6901746346790563787434755862277025452451108972170386555162524223799296*w61 + 1766847064778384329583297500742918515827483896875618958121606201292619776*w62 + 2040124
601 ASSERT w62 = w1
602 ASSERT w61 = w2
603 ASSERT w60 = w3
604 ASSERT w59 = w4
605 ASSERT w58 = w5
606 ASSERT w57 = w6
607 ASSERT w56 = w7
608 ASSERT w55 = w8
609 ASSERT w54 = w9
610 ASSERT w53 = w10
611 ASSERT w52 = w11
612 ASSERT w51 = w12
613 ASSERT w50 = w13
614 ASSERT w49 = w14
615 ASSERT w48 = w15
616 ASSERT w47 = w16
617 ASSERT w46 = w17
618 ASSERT w45 = w18
619 ASSERT w44 = w19
620 ASSERT w43 = w20
621 ASSERT w42 = w21
622 ASSERT w41 = w22
623 ASSERT w40 = w23
624 ASSERT w39 = w24
625 ASSERT w38 = w25
626 ASSERT w37 = w26
627 ASSERT w36 = w27
628 ASSERT w35 = w28
629 ASSERT w29 = 31
630 ASSERT w30 = 33
631 ASSERT w31 = 60
632 "#;
633
634 let acir = Circuit::from_str(formatted_acir).unwrap();
635 assert!(CircuitSimulator::check_circuit(&acir).is_none());
636
637 let acir_opcode_positions = vec![
638 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
639 24, 25, 26, 27, 28, 29, 29, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
640 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
641 ];
642 let mut brillig_side_effects = BTreeMap::new();
643 brillig_side_effects.insert(BrilligFunctionId::new(0), false);
644
645 let (_, _, opcode_count_stabilized) =
646 transform_internal(acir, acir_opcode_positions, &brillig_side_effects, None);
647 assert!(!opcode_count_stabilized);
648 }
649}