Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,49 @@ use num_bigint::BigUint;
/// id is the identifier of the big integer number, and
/// modulus is the identifier of the big integer size
#[derive(Default, Clone, Copy, Debug)]
pub(crate) struct BigIntId {
pub(crate) bigint_id: u32,
pub(crate) modulus_id: u32,
pub(super) struct BigIntId {
pub(super) bigint_id: u32,
pub(super) modulus_id: u32,
}

impl BigIntId {
pub(crate) fn bigint_id<F: From<u128>>(&self) -> F {
pub(super) fn bigint_id<F: From<u128>>(&self) -> F {
F::from(self.bigint_id as u128)
}

pub(crate) fn modulus_id<F: From<u128>>(&self) -> F {
pub(super) fn modulus_id<F: From<u128>>(&self) -> F {
F::from(self.modulus_id as u128)
}
}

/// BigIntContext is used to generate identifiers for big integers and their modulus
#[derive(Default, Debug)]
pub(crate) struct BigIntContext {
pub(super) struct BigIntContext {
modulus: Vec<BigUint>,
big_integers: Vec<BigIntId>,
}

impl BigIntContext {
/// Creates a new BigIntId for the given modulus identifier and returns it.
pub(crate) fn new_big_int<F: AcirField>(&mut self, modulus_id: F) -> BigIntId {
pub(super) fn new_big_int<F: AcirField>(&mut self, modulus_id: F) -> BigIntId {
let id = self.big_integers.len() as u32;
let result = BigIntId { bigint_id: id, modulus_id: modulus_id.to_u128() as u32 };
self.big_integers.push(result);
result
}

/// Returns the modulus corresponding to the given modulus index
pub(crate) fn modulus<F: AcirField>(&self, idx: F) -> BigUint {
pub(super) fn modulus<F: AcirField>(&self, idx: F) -> BigUint {
self.modulus[idx.to_u128() as usize].clone()
}

/// Returns the BigIntId corresponding to the given identifier
pub(crate) fn get<F: AcirField>(&self, id: F) -> BigIntId {
pub(super) fn get<F: AcirField>(&self, id: F) -> BigIntId {
self.big_integers[id.to_u128() as usize]
}

/// Adds a modulus to the context (if it is not already present)
pub(crate) fn get_or_insert_modulus(&mut self, modulus: BigUint) -> u32 {
pub(super) fn get_or_insert_modulus(&mut self, modulus: BigUint) -> u32 {
if let Some(pos) = self.modulus.iter().position(|x| x == &modulus) {
return pos as u32;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ use num_bigint::BigUint;

use crate::errors::{InternalError, RuntimeError};

use super::{
AcirValue,
acir_variable::{AcirContext, AcirVar},
};
use super::{AcirContext, AcirValue, AcirVar};

impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
/// Calls a Blackbox function on the given inputs and returns a given set of outputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ use acvm::{
};
use iter_extended::{try_vecmap, vecmap};

use crate::brillig::brillig_ir::artifact::GeneratedBrillig;
use crate::errors::{InternalError, RuntimeError};
use crate::{acir::acir_variable::AcirContext, brillig::brillig_ir::artifact::GeneratedBrillig};

use super::acir_variable::{AcirType, AcirVar};
use super::generated_acir::BrilligStdlibFunc;
use super::{AcirDynamicArray, AcirValue};
use super::{AcirContext, AcirDynamicArray, AcirType, AcirValue, AcirVar};

impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
#[allow(clippy::too_many_arguments)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
//! `GeneratedAcir` is constructed as part of the `acir_gen` pass to accumulate all of the ACIR
//! program as it is being converted from SSA form.
//! The `generated_acir` module is responsible for the lowest level tracking of ACIR-gen.
//!
//! This module defines the `GeneratedAcir` which is constructed as part of the ACIR-gen pass
//! to accumulate all of the ACIR. This tracks properties such as which witnesses form part of the circuit interface
//! and defines common snippets of ACIR to perform simple operations. Notably it has no concept of SSA or any types
//! for the variables it works with, it instead only works on the [`Expression`]s and [`Witness`]es which will be
//! reflected in the final circuit.

use std::collections::BTreeMap;

use acvm::acir::{
Expand All @@ -12,7 +18,6 @@ use acvm::acir::{
native_types::{Expression, Witness},
};

use super::brillig_directive;
use crate::{
ErrorType,
brillig::brillig_ir::artifact::GeneratedBrillig,
Expand All @@ -24,11 +29,13 @@ use iter_extended::vecmap;
use noirc_errors::debug_info::ProcedureDebugId;
use num_bigint::BigUint;

mod brillig_directive;

/// Brillig calls such as for the Brillig std lib are resolved only after code generation is finished.
/// This index should be used when adding a Brillig call during code generation.
/// Code generation should then keep track of that unresolved call opcode which will be resolved with the
/// correct function index after code generation.
pub(crate) const PLACEHOLDER_BRILLIG_INDEX: BrilligFunctionId = BrilligFunctionId(0);
pub(super) const PLACEHOLDER_BRILLIG_INDEX: BrilligFunctionId = BrilligFunctionId(0);

#[derive(Debug, Default)]
/// The output of the Acir-gen pass, which should only be produced for entry point Acir functions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
//! The `acir_context` module acts as a high-level wrapper around [`generated_acir`], maintaining a small amount
//! of type information from SSA in order to allow efficient ACIR-gen.
//!
//! The [`AcirContext`] struct defines how to translate SSA instructions into equivalent ACIR constructs. This
//! layer of ACIR-gen is aware of a small amount of SSA type information in order to ensure that the generated ACIR
//! is correct and for optimizations.
//!
//! [`AcirContext`] also tracks [`Expression`]s which have been simplified into a [`Witness`], or constant witnesses
//! allowing these to be reused later in the program.

use acvm::{
BlackBoxFunctionSolver,
acir::{
Expand All @@ -12,92 +22,24 @@ use acvm::{
use fxhash::FxHashMap as HashMap;
use iter_extended::{try_vecmap, vecmap};
use num_bigint::BigUint;
use std::cmp::Ordering;
use std::{borrow::Cow, hash::Hash};
use std::{borrow::Cow, cmp::Ordering};

use crate::errors::{InternalBug, InternalError, RuntimeError, SsaReport};
use crate::ssa::ir::{
call_stack::CallStack, instruction::Endian, types::NumericType, types::Type as SsaType,
};

use super::big_int::BigIntContext;
use super::generated_acir::{BrilligStdlibFunc, GeneratedAcir, PLACEHOLDER_BRILLIG_INDEX};
use super::{AcirDynamicArray, AcirValue, brillig_directive};
use crate::ssa::ir::{call_stack::CallStack, instruction::Endian, types::NumericType};

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
/// High level Type descriptor for Variables.
///
/// One can think of Expression/Witness/Const
/// as low level types which can represent high level types.
///
/// An Expression can represent a u32 for example.
/// We could store this information when we do a range constraint
/// but this information is readily available by the caller so
/// we allow the user to pass it in.
pub(crate) enum AcirType {
NumericType(NumericType),
Array(Vec<AcirType>, usize),
}

impl AcirType {
pub(crate) fn new(typ: NumericType) -> Self {
Self::NumericType(typ)
}

/// Returns the bit size of the underlying type
pub(crate) fn bit_size<F: AcirField>(&self) -> u32 {
match self {
AcirType::NumericType(numeric_type) => match numeric_type {
NumericType::Signed { bit_size } => *bit_size,
NumericType::Unsigned { bit_size } => *bit_size,
NumericType::NativeField => F::max_num_bits(),
},
AcirType::Array(_, _) => unreachable!("cannot fetch bit size of array type"),
}
}

/// Returns a field type
pub(crate) fn field() -> Self {
AcirType::NumericType(NumericType::NativeField)
}

/// Returns an unsigned type of the specified bit size
pub(crate) fn unsigned(bit_size: u32) -> Self {
AcirType::NumericType(NumericType::Unsigned { bit_size })
}

pub(crate) fn to_numeric_type(&self) -> NumericType {
match self {
AcirType::NumericType(numeric_type) => *numeric_type,
AcirType::Array(_, _) => unreachable!("cannot fetch a numeric type for an array type"),
}
}
}
mod big_int;
mod black_box;
mod brillig_call;
mod generated_acir;

impl From<SsaType> for AcirType {
fn from(value: SsaType) -> Self {
AcirType::from(&value)
}
}

impl From<&SsaType> for AcirType {
fn from(value: &SsaType) -> Self {
match value {
SsaType::Numeric(numeric_type) => AcirType::NumericType(*numeric_type),
SsaType::Array(elements, size) => {
let elements = elements.iter().map(|e| e.into()).collect();
AcirType::Array(elements, *size as usize)
}
_ => unreachable!("The type {value} cannot be represented in ACIR"),
}
}
}
use super::{
AcirDynamicArray, AcirValue,
types::{AcirType, AcirVar},
};
use big_int::BigIntContext;
use generated_acir::PLACEHOLDER_BRILLIG_INDEX;

impl From<NumericType> for AcirType {
fn from(value: NumericType) -> Self {
AcirType::NumericType(value)
}
}
pub(crate) use generated_acir::{BrilligStdlibFunc, GeneratedAcir};

#[derive(Debug, Default)]
/// Context object which holds the relationship between
Expand All @@ -120,11 +62,11 @@ pub(crate) struct AcirContext<F: AcirField, B: BlackBoxFunctionSolver<F>> {
pub(super) acir_ir: GeneratedAcir<F>,

/// The BigIntContext, used to generate identifiers for BigIntegers
pub(super) big_int_ctx: BigIntContext,
big_int_ctx: BigIntContext,

expression_width: ExpressionWidth,

pub(crate) warnings: Vec<SsaReport>,
pub(super) warnings: Vec<SsaReport>,
}

impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
Expand Down Expand Up @@ -332,7 +274,7 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
}

// Compute the inverse with brillig code
let inverse_code = brillig_directive::directive_invert();
let inverse_code = BrilligStdlibFunc::Inverse.get_generated_brillig();

let results = self.brillig_call(
predicate,
Expand Down Expand Up @@ -859,7 +801,7 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
let [q_value, r_value]: [AcirValue; 2] = self
.brillig_call(
predicate,
&brillig_directive::directive_quotient(),
&BrilligStdlibFunc::Quotient.get_generated_brillig(),
vec![
AcirValue::Var(lhs, AcirType::unsigned(bit_size)),
AcirValue::Var(rhs, AcirType::unsigned(bit_size)),
Expand Down Expand Up @@ -1416,7 +1358,7 @@ impl<F: AcirField, B: BlackBoxFunctionSolver<F>> AcirContext<F, B> {
/// We use a two-way map so that it is efficient to lookup
/// either the key or the value.
fn add_data(&mut self, data: AcirVarData<F>) -> AcirVar {
let id = AcirVar(self.vars.len());
let id = AcirVar::new(self.vars.len());
self.vars.insert(id, data);
id
}
Expand Down Expand Up @@ -1604,31 +1546,13 @@ pub(super) fn power_of_two<F: AcirField>(power: u32) -> F {

/// Enum representing the possible values that a
/// Variable can be given.
#[derive(Debug, Eq, Clone)]
#[derive(Debug, Eq, Clone, PartialEq)]
enum AcirVarData<F> {
Witness(Witness),
Expr(Expression<F>),
Const(F),
}

impl<F: PartialEq> PartialEq for AcirVarData<F> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Witness(l0), Self::Witness(r0)) => l0 == r0,
(Self::Expr(l0), Self::Expr(r0)) => l0 == r0,
(Self::Const(l0), Self::Const(r0)) => l0 == r0,
_ => false,
}
}
}

// TODO: check/test this hash impl
impl<F> Hash for AcirVarData<F> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
}
}

impl<F> AcirVarData<F> {
/// Returns a FieldElement, if the underlying `AcirVarData`
/// represents a constant.
Expand Down Expand Up @@ -1687,10 +1611,6 @@ fn fits_in_one_identity<F: AcirField>(expr: &Expression<F>, width: ExpressionWid
expr.width() <= width
}

/// A Reference to an `AcirVarData`
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct AcirVar(usize);

#[cfg(test)]
mod test {
use acvm::{AcirField, FieldElement};
Expand Down
Loading
Loading