Skip to content

Commit

Permalink
Merge branch 'master' into 6304-sha-msg-block-size-16
Browse files Browse the repository at this point in the history
  • Loading branch information
aakoshh authored Oct 25, 2024
2 parents 77c1bc9 + 600ffeb commit 116dcb6
Show file tree
Hide file tree
Showing 77 changed files with 1,250 additions and 742 deletions.
2 changes: 1 addition & 1 deletion .aztec-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ab0c80d7493e6bdbc58dcd517b248de6ddd6fd67
07d6dc29db2eb04154b8f0c66bd1efa74c0e8b9d
4 changes: 0 additions & 4 deletions .github/scripts/backend-barretenberg-build.sh

This file was deleted.

4 changes: 0 additions & 4 deletions .github/scripts/backend-barretenberg-test.sh

This file was deleted.

11 changes: 4 additions & 7 deletions .github/workflows/test-js-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: noirc_abi_wasm
path: |
path: |
./tooling/noirc_abi_wasm/nodejs
./tooling/noirc_abi_wasm/web
retention-days: 10
Expand Down Expand Up @@ -263,9 +263,6 @@ jobs:
- name: Build noir_js_types
run: yarn workspace @noir-lang/types build

- name: Build barretenberg wrapper
run: yarn workspace @noir-lang/backend_barretenberg build

- name: Run noir_js tests
run: |
yarn workspace @noir-lang/noir_js build
Expand Down Expand Up @@ -416,7 +413,7 @@ jobs:
- name: Setup `integration-tests`
run: |
# Note the lack of spaces between package names.
PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js"
PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/noir_js"
yarn workspaces foreach -vtp --from "{$PACKAGES_TO_BUILD}" run build
- name: Run `integration-tests`
Expand Down Expand Up @@ -461,7 +458,7 @@ jobs:
- name: Setup `integration-tests`
run: |
# Note the lack of spaces between package names.
PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js"
PACKAGES_TO_BUILD="@noir-lang/types,@noir-lang/noir_js"
yarn workspaces foreach -vtp --from "{$PACKAGES_TO_BUILD}" run build
- name: Run `integration-tests`
Expand Down Expand Up @@ -565,7 +562,7 @@ jobs:
runs-on: ubuntu-latest
# We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping.
if: ${{ always() }}
needs:
needs:
- test-acvm_js-node
- test-acvm_js-browser
- test-noirc-abi
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions acvm-repo/acir/codegen/acir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ namespace Program {
};

struct Keccakf1600 {
Program::HeapVector message;
Program::HeapArray input;
Program::HeapArray output;

friend bool operator==(const Keccakf1600&, const Keccakf1600&);
Expand Down Expand Up @@ -424,8 +424,8 @@ namespace Program {
};

struct Sha256Compression {
Program::HeapVector input;
Program::HeapVector hash_values;
Program::HeapArray input;
Program::HeapArray hash_values;
Program::HeapArray output;

friend bool operator==(const Sha256Compression&, const Sha256Compression&);
Expand Down Expand Up @@ -3498,7 +3498,7 @@ Program::BlackBoxOp::Blake3 serde::Deserializable<Program::BlackBoxOp::Blake3>::
namespace Program {

inline bool operator==(const BlackBoxOp::Keccakf1600 &lhs, const BlackBoxOp::Keccakf1600 &rhs) {
if (!(lhs.message == rhs.message)) { return false; }
if (!(lhs.input == rhs.input)) { return false; }
if (!(lhs.output == rhs.output)) { return false; }
return true;
}
Expand All @@ -3523,15 +3523,15 @@ namespace Program {
template <>
template <typename Serializer>
void serde::Serializable<Program::BlackBoxOp::Keccakf1600>::serialize(const Program::BlackBoxOp::Keccakf1600 &obj, Serializer &serializer) {
serde::Serializable<decltype(obj.message)>::serialize(obj.message, serializer);
serde::Serializable<decltype(obj.input)>::serialize(obj.input, serializer);
serde::Serializable<decltype(obj.output)>::serialize(obj.output, serializer);
}

template <>
template <typename Deserializer>
Program::BlackBoxOp::Keccakf1600 serde::Deserializable<Program::BlackBoxOp::Keccakf1600>::deserialize(Deserializer &deserializer) {
Program::BlackBoxOp::Keccakf1600 obj;
obj.message = serde::Deserializable<decltype(obj.message)>::deserialize(deserializer);
obj.input = serde::Deserializable<decltype(obj.input)>::deserialize(deserializer);
obj.output = serde::Deserializable<decltype(obj.output)>::deserialize(deserializer);
return obj;
}
Expand Down
12 changes: 12 additions & 0 deletions acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashSet;

use crate::native_types::Witness;
use crate::{AcirField, BlackBoxFunc};

Expand Down Expand Up @@ -389,6 +391,16 @@ impl<F: Copy> BlackBoxFuncCall<F> {
BlackBoxFuncCall::BigIntToLeBytes { outputs, .. } => outputs.to_vec(),
}
}

pub fn get_input_witnesses(&self) -> HashSet<Witness> {
let mut result = HashSet::new();
for input in self.get_inputs_vec() {
if let ConstantOrWitnessEnum::Witness(w) = input.input() {
result.insert(w);
}
}
result
}
}

const ABBREVIATION_LIMIT: usize = 5;
Expand Down
202 changes: 202 additions & 0 deletions acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use std::collections::{HashMap, HashSet};

use acir::{
circuit::{brillig::BrilligInputs, directives::Directive, opcodes::BlockId, Circuit, Opcode},
native_types::{Expression, Witness},
AcirField,
};

pub(crate) struct MergeExpressionsOptimizer {
resolved_blocks: HashMap<BlockId, HashSet<Witness>>,
}

impl MergeExpressionsOptimizer {
pub(crate) fn new() -> Self {
MergeExpressionsOptimizer { resolved_blocks: HashMap::new() }
}
/// This pass analyzes the circuit and identifies intermediate variables that are
/// only used in two gates. It then merges the gate that produces the
/// intermediate variable into the second one that uses it
/// Note: This pass is only relevant for backends that can handle unlimited width
pub(crate) fn eliminate_intermediate_variable<F: AcirField>(
&mut self,
circuit: &Circuit<F>,
acir_opcode_positions: Vec<usize>,
) -> (Vec<Opcode<F>>, Vec<usize>) {
// Keep track, for each witness, of the gates that use it
let circuit_inputs = circuit.circuit_arguments();
self.resolved_blocks = HashMap::new();
let mut used_witness: HashMap<Witness, HashSet<usize>> = HashMap::new();
for (i, opcode) in circuit.opcodes.iter().enumerate() {
let witnesses = self.witness_inputs(opcode);
if let Opcode::MemoryInit { block_id, .. } = opcode {
self.resolved_blocks.insert(*block_id, witnesses.clone());
}
for w in witnesses {
// We do not simplify circuit inputs
if !circuit_inputs.contains(&w) {
used_witness.entry(w).or_default().insert(i);
}
}
}

let mut modified_gates: HashMap<usize, Opcode<F>> = HashMap::new();
let mut new_circuit = Vec::new();
let mut new_acir_opcode_positions = Vec::new();
// For each opcode, try to get a target opcode to merge with
for (i, opcode) in circuit.opcodes.iter().enumerate() {
if !matches!(opcode, Opcode::AssertZero(_)) {
new_circuit.push(opcode.clone());
new_acir_opcode_positions.push(acir_opcode_positions[i]);
continue;
}
let opcode = modified_gates.get(&i).unwrap_or(opcode).clone();
let mut to_keep = true;
let input_witnesses = self.witness_inputs(&opcode);
for w in input_witnesses.clone() {
let empty_gates = HashSet::new();
let gates_using_w = used_witness.get(&w).unwrap_or(&empty_gates);
// We only consider witness which are used in exactly two arithmetic gates
if gates_using_w.len() == 2 {
let gates_using_w: Vec<_> = gates_using_w.iter().collect();
let mut b = *gates_using_w[1];
if b == i {
b = *gates_using_w[0];
} else {
// sanity check
assert!(i == *gates_using_w[0]);
}
let second_gate = modified_gates.get(&b).unwrap_or(&circuit.opcodes[b]).clone();
if let (Opcode::AssertZero(expr_define), Opcode::AssertZero(expr_use)) =
(opcode.clone(), second_gate)
{
if let Some(expr) = Self::merge(&expr_use, &expr_define, w) {
// sanity check
assert!(i < b);
modified_gates.insert(b, Opcode::AssertZero(expr));
to_keep = false;
// Update the 'used_witness' map to account for the merge.
for w2 in Self::expr_wit(&expr_define) {
if !circuit_inputs.contains(&w2) {
let mut v = used_witness[&w2].clone();
v.insert(b);
v.remove(&i);
used_witness.insert(w2, v);
}
}
// We need to stop here and continue with the next opcode
// because the merge invalidate the current opcode
break;
}
}
}
}

if to_keep {
if modified_gates.contains_key(&i) {
new_circuit.push(modified_gates[&i].clone());
} else {
new_circuit.push(opcode.clone());
}
new_acir_opcode_positions.push(acir_opcode_positions[i]);
}
}
(new_circuit, new_acir_opcode_positions)
}

fn expr_wit<F>(expr: &Expression<F>) -> HashSet<Witness> {
let mut result = HashSet::new();
result.extend(expr.mul_terms.iter().flat_map(|i| vec![i.1, i.2]));
result.extend(expr.linear_combinations.iter().map(|i| i.1));
result
}

fn brillig_input_wit<F>(&self, input: &BrilligInputs<F>) -> HashSet<Witness> {
let mut result = HashSet::new();
match input {
BrilligInputs::Single(expr) => {
result.extend(Self::expr_wit(expr));
}
BrilligInputs::Array(exprs) => {
for expr in exprs {
result.extend(Self::expr_wit(expr));
}
}
BrilligInputs::MemoryArray(block_id) => {
let witnesses = self.resolved_blocks.get(block_id).expect("Unknown block id");
result.extend(witnesses);
}
}
result
}

// Returns the input witnesses used by the opcode
fn witness_inputs<F: AcirField>(&self, opcode: &Opcode<F>) -> HashSet<Witness> {
let mut witnesses = HashSet::new();
match opcode {
Opcode::AssertZero(expr) => Self::expr_wit(expr),
Opcode::BlackBoxFuncCall(bb_func) => bb_func.get_input_witnesses(),
Opcode::Directive(Directive::ToLeRadix { a, .. }) => Self::expr_wit(a),
Opcode::MemoryOp { block_id: _, op, predicate } => {
//index et value, et predicate
let mut witnesses = HashSet::new();
witnesses.extend(Self::expr_wit(&op.index));
witnesses.extend(Self::expr_wit(&op.value));
if let Some(p) = predicate {
witnesses.extend(Self::expr_wit(p));
}
witnesses
}

Opcode::MemoryInit { block_id: _, init, block_type: _ } => {
init.iter().cloned().collect()
}
Opcode::BrilligCall { inputs, .. } => {
for i in inputs {
witnesses.extend(self.brillig_input_wit(i));
}
witnesses
}
Opcode::Call { id: _, inputs, outputs: _, predicate } => {
for i in inputs {
witnesses.insert(*i);
}
if let Some(p) = predicate {
witnesses.extend(Self::expr_wit(p));
}
witnesses
}
}
}

// Merge 'expr' into 'target' via Gaussian elimination on 'w'
// Returns None if the expressions cannot be merged
fn merge<F: AcirField>(
target: &Expression<F>,
expr: &Expression<F>,
w: Witness,
) -> Option<Expression<F>> {
// Check that the witness is not part of multiplication terms
for m in &target.mul_terms {
if m.1 == w || m.2 == w {
return None;
}
}
for m in &expr.mul_terms {
if m.1 == w || m.2 == w {
return None;
}
}

for k in &target.linear_combinations {
if k.1 == w {
for i in &expr.linear_combinations {
if i.1 == w {
return Some(target.add_mul(-(k.0 / i.0), expr));
}
}
}
}
None
}
}
2 changes: 2 additions & 0 deletions acvm-repo/acvm/src/compiler/optimizers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use acir::{

// mod constant_backpropagation;
mod general;
mod merge_expressions;
mod redundant_range;
mod unused_memory;

pub(crate) use general::GeneralOptimizer;
pub(crate) use merge_expressions::MergeExpressionsOptimizer;
pub(crate) use redundant_range::RangeOptimizer;
use tracing::info;

Expand Down
16 changes: 14 additions & 2 deletions acvm-repo/acvm/src/compiler/transformers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ mod csat;
pub(crate) use csat::CSatTransformer;
pub use csat::MIN_EXPRESSION_WIDTH;

use super::{transform_assert_messages, AcirTransformationMap};
use super::{
optimizers::MergeExpressionsOptimizer, transform_assert_messages, AcirTransformationMap,
};

/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`].
pub fn transform<F: AcirField>(
Expand Down Expand Up @@ -166,6 +168,16 @@ pub(super) fn transform_internal<F: AcirField>(
// The transformer does not add new public inputs
..acir
};

let mut merge_optimizer = MergeExpressionsOptimizer::new();
let (opcodes, new_acir_opcode_positions) =
merge_optimizer.eliminate_intermediate_variable(&acir, new_acir_opcode_positions);
// n.b. we do not update current_witness_index after the eliminate_intermediate_variable pass, the real index could be less.
let acir = Circuit {
current_witness_index,
expression_width,
opcodes,
// The optimizer does not add new public inputs
..acir
};
(acir, new_acir_opcode_positions)
}
Loading

0 comments on commit 116dcb6

Please sign in to comment.