Skip to content

Commit

Permalink
chore: remove noirc_abi dependency from noirc_evaluator (#3492)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Nov 21, 2023
1 parent 7fe0a51 commit 008a431
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 160 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

141 changes: 141 additions & 0 deletions compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use std::collections::BTreeMap;

use acvm::acir::native_types::Witness;
use iter_extended::{btree_map, vecmap};
use noirc_abi::{Abi, AbiParameter, AbiType};
use noirc_frontend::{
hir::Context,
hir_def::{function::Param, stmt::HirPattern},
node_interner::{FuncId, NodeInterner},
};
use std::ops::Range;

/// Arranges a function signature and a generated circuit's return witnesses into a
/// `noirc_abi::Abi`.
pub(super) fn gen_abi(
context: &Context,
func_id: &FuncId,
input_witnesses: Vec<Witness>,
return_witnesses: Vec<Witness>,
) -> Abi {
let (parameters, return_type) = compute_function_abi(context, func_id);
let param_witnesses = param_witnesses_from_abi_param(&parameters, input_witnesses);
Abi { parameters, return_type, param_witnesses, return_witnesses }
}

pub(super) fn compute_function_abi(
context: &Context,
func_id: &FuncId,
) -> (Vec<AbiParameter>, Option<AbiType>) {
let func_meta = context.def_interner.function_meta(func_id);

let (parameters, return_type) = func_meta.into_function_signature();
let parameters = into_abi_params(context, parameters);
let return_type = return_type.map(|typ| AbiType::from_type(context, &typ));
(parameters, return_type)
}

/// Attempts to retrieve the name of this parameter. Returns None
/// if this parameter is a tuple or struct pattern.
fn get_param_name<'a>(pattern: &HirPattern, interner: &'a NodeInterner) -> Option<&'a str> {
match pattern {
HirPattern::Identifier(ident) => Some(interner.definition_name(ident.id)),
HirPattern::Mutable(pattern, _) => get_param_name(pattern, interner),
HirPattern::Tuple(_, _) => None,
HirPattern::Struct(_, _, _) => None,
}
}

fn into_abi_params(context: &Context, params: Vec<Param>) -> Vec<AbiParameter> {
vecmap(params, |(pattern, typ, vis)| {
let param_name = get_param_name(&pattern, &context.def_interner)
.expect("Abi for tuple and struct parameters is unimplemented")
.to_owned();
let as_abi = AbiType::from_type(context, &typ);
AbiParameter { name: param_name, typ: as_abi, visibility: vis.into() }
})
}

// Takes each abi parameter and shallowly maps to the expected witness range in which the
// parameter's constituent values live.
fn param_witnesses_from_abi_param(
abi_params: &Vec<AbiParameter>,
input_witnesses: Vec<Witness>,
) -> BTreeMap<String, Vec<Range<Witness>>> {
let mut idx = 0_usize;
if input_witnesses.is_empty() {
return BTreeMap::new();
}

btree_map(abi_params, |param| {
let num_field_elements_needed = param.typ.field_count() as usize;
let param_witnesses = &input_witnesses[idx..idx + num_field_elements_needed];

// It's likely that `param_witnesses` will consist of mostly incrementing witness indices.
// We then want to collapse these into `Range`s to save space.
let param_witnesses = collapse_ranges(param_witnesses);
idx += num_field_elements_needed;
(param.name.clone(), param_witnesses)
})
}

/// Takes a vector of [`Witnesses`][`Witness`] and collapses it into a vector of [`Range`]s of [`Witnesses`][`Witness`].
fn collapse_ranges(witnesses: &[Witness]) -> Vec<Range<Witness>> {
if witnesses.is_empty() {
return Vec::new();
}
let mut wit = Vec::new();
let mut last_wit: Witness = witnesses[0];

for (i, witness) in witnesses.iter().enumerate() {
if i == 0 {
continue;
};
let witness_index = witness.witness_index();
let prev_witness_index = witnesses[i - 1].witness_index();
if witness_index != prev_witness_index + 1 {
wit.push(last_wit..Witness(prev_witness_index + 1));
last_wit = *witness;
};
}

let last_witness = witnesses.last().unwrap().witness_index();
wit.push(last_wit..Witness(last_witness + 1));

wit
}

#[cfg(test)]
mod test {
use std::ops::Range;

use acvm::acir::native_types::Witness;

use super::collapse_ranges;

#[test]
fn collapses_single_range() {
let witnesses: Vec<_> = vec![1, 2, 3].into_iter().map(Witness::from).collect();

let collapsed_witnesses = collapse_ranges(&witnesses);

assert_eq!(collapsed_witnesses, vec![Range { start: Witness(1), end: Witness(4) },])
}

#[test]
fn collapse_ranges_correctly() {
let witnesses: Vec<_> =
vec![1, 2, 3, 5, 6, 2, 3, 4].into_iter().map(Witness::from).collect();

let collapsed_witnesses = collapse_ranges(&witnesses);

assert_eq!(
collapsed_witnesses,
vec![
Range { start: Witness(1), end: Witness(4) },
Range { start: Witness(5), end: Witness(7) },
Range { start: Witness(2), end: Witness(5) }
]
)
}
}
15 changes: 6 additions & 9 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use fm::FileId;
use iter_extended::vecmap;
use noirc_abi::{AbiParameter, AbiType, ContractEvent};
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
use noirc_evaluator::create_circuit;
use noirc_evaluator::errors::RuntimeError;
use noirc_evaluator::{create_circuit, into_abi_params};
use noirc_frontend::graph::{CrateId, CrateName};
use noirc_frontend::hir::def_map::{Contract, CrateDefMap};
use noirc_frontend::hir::Context;
Expand All @@ -18,6 +18,7 @@ use noirc_frontend::node_interner::FuncId;
use serde::{Deserialize, Serialize};
use std::path::Path;

mod abi_gen;
mod contract;
mod debug;
mod program;
Expand Down Expand Up @@ -140,12 +141,7 @@ pub fn compute_function_abi(
) -> Option<(Vec<AbiParameter>, Option<AbiType>)> {
let main_function = context.get_main_function(crate_id)?;

let func_meta = context.def_interner.function_meta(&main_function);

let (parameters, return_type) = func_meta.into_function_signature();
let parameters = into_abi_params(context, parameters);
let return_type = return_type.map(|typ| AbiType::from_type(context, &typ));
Some((parameters, return_type))
Some(abi_gen::compute_function_abi(context, &main_function))
}

/// Run the frontend to check the crate for errors then compile the main function if there were none
Expand Down Expand Up @@ -345,9 +341,10 @@ pub fn compile_no_check(
return Ok(cached_program.expect("cache must exist for hashes to match"));
}

let (circuit, debug, abi, warnings) =
create_circuit(context, program, options.show_ssa, options.show_brillig)?;
let (circuit, debug, input_witnesses, return_witnesses, warnings) =
create_circuit(program, options.show_ssa, options.show_brillig)?;

let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses);
let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager);

Ok(CompiledProgram {
Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_evaluator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ license.workspace = true
[dependencies]
noirc_frontend.workspace = true
noirc_errors.workspace = true
noirc_abi.workspace = true
acvm.workspace = true
fxhash.workspace = true
iter-extended.workspace = true
Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ pub mod ssa;

pub mod brillig;

pub use ssa::abi_gen::into_abi_params;
pub use ssa::create_circuit;
78 changes: 46 additions & 32 deletions compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
//! This module heavily borrows from Cranelift
#![allow(dead_code)]

use std::{
collections::{BTreeMap, BTreeSet},
ops::Range,
};
use std::collections::BTreeSet;

use crate::{
brillig::Brillig,
Expand All @@ -23,13 +20,12 @@ use acvm::acir::{

use noirc_errors::debug_info::DebugInfo;

use noirc_abi::Abi;

use noirc_frontend::{hir::Context, monomorphization::ast::Program};
use noirc_frontend::{
hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility,
};

use self::{abi_gen::gen_abi, acir_gen::GeneratedAcir, ssa_gen::Ssa};
use self::{acir_gen::GeneratedAcir, ssa_gen::Ssa};

pub mod abi_gen;
mod acir_gen;
pub(super) mod function_builder;
pub mod ir;
Expand Down Expand Up @@ -81,12 +77,12 @@ pub(crate) fn optimize_into_acir(
/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit].
///
/// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation.
#[allow(clippy::type_complexity)]
pub fn create_circuit(
context: &Context,
program: Program,
enable_ssa_logging: bool,
enable_brillig_logging: bool,
) -> Result<(Circuit, DebugInfo, Abi, Vec<SsaReport>), RuntimeError> {
) -> Result<(Circuit, DebugInfo, Vec<Witness>, Vec<Witness>, Vec<SsaReport>), RuntimeError> {
let func_sig = program.main_function_signature.clone();
let mut generated_acir =
optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?;
Expand All @@ -101,15 +97,11 @@ pub fn create_circuit(
..
} = generated_acir;

let abi = gen_abi(context, func_sig, input_witnesses, return_witnesses.clone());
let public_abi = abi.clone().public_abi();

let public_parameters = PublicInputs(tree_to_set(&public_abi.param_witnesses));
let (public_parameter_witnesses, private_parameters) =
split_public_and_private_inputs(&func_sig, &input_witnesses);

let all_parameters: BTreeSet<Witness> = tree_to_set(&abi.param_witnesses);
let private_parameters = all_parameters.difference(&public_parameters.0).copied().collect();

let return_values = PublicInputs(return_witnesses.into_iter().collect());
let public_parameters = PublicInputs(public_parameter_witnesses);
let return_values = PublicInputs(return_witnesses.iter().copied().collect());

let circuit = Circuit {
current_witness_index,
Expand All @@ -132,7 +124,41 @@ pub fn create_circuit(
let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit);
debug_info.update_acir(transformation_map);

Ok((optimized_circuit, debug_info, abi, warnings))
Ok((optimized_circuit, debug_info, input_witnesses, return_witnesses, warnings))
}

// Takes each function argument and partitions the circuit's inputs witnesses according to its visibility.
fn split_public_and_private_inputs(
func_sig: &FunctionSignature,
input_witnesses: &[Witness],
) -> (BTreeSet<Witness>, BTreeSet<Witness>) {
let mut idx = 0_usize;
if input_witnesses.is_empty() {
return (BTreeSet::new(), BTreeSet::new());
}

func_sig
.0
.iter()
.map(|(_, typ, visibility)| {
let num_field_elements_needed = typ.field_count() as usize;
let witnesses = input_witnesses[idx..idx + num_field_elements_needed].to_vec();
idx += num_field_elements_needed;
(visibility, witnesses)
})
.fold((BTreeSet::new(), BTreeSet::new()), |mut acc, (vis, witnesses)| {
// Split witnesses into sets based on their visibility.
if *vis == Visibility::Public {
for witness in witnesses {
acc.0.insert(witness);
}
} else {
for witness in witnesses {
acc.1.insert(witness);
}
}
(acc.0, acc.1)
})
}

// This is just a convenience object to bundle the ssa with `print_ssa_passes` for debug printing.
Expand Down Expand Up @@ -178,15 +204,3 @@ impl SsaBuilder {
self
}
}

// Flatten the witnesses in the map into a BTreeSet
fn tree_to_set(input: &BTreeMap<String, Vec<Range<Witness>>>) -> BTreeSet<Witness> {
let mut result = BTreeSet::new();
for range in input.values().flatten() {
for i in range.start.witness_index()..range.end.witness_index() {
result.insert(Witness(i));
}
}

result
}
Loading

0 comments on commit 008a431

Please sign in to comment.