Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: remove noirc_abi dependency from noirc_evaluator #3492

Merged
merged 11 commits into from
Nov 21, 2023
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