Skip to content

Commit

Permalink
feat: Implement poseidon2_permutation in comptime interpreter (#5590)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves another of the many builtin/foreign functions that still need
to be implemented in the interpreter

## Summary\*



## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
jfecher authored Jul 23, 2024
1 parent 0a987c7 commit 89dfbbf
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions compiler/noirc_frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license.workspace = true

[dependencies]
acvm.workspace = true
bn254_blackbox_solver.workspace = true
noirc_arena.workspace = true
noirc_errors.workspace = true
noirc_printable_type.workspace = true
Expand Down
19 changes: 11 additions & 8 deletions compiler/noirc_frontend/src/hir/comptime/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
token::Tokens,
Type,
};
use acvm::{acir::AcirField, FieldElement};
use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement};
use fm::FileId;
use iter_extended::vecmap;
use noirc_errors::{CustomDiagnostic, Location};
Expand Down Expand Up @@ -53,13 +53,11 @@ pub enum InterpreterError {
NoImpl { location: Location },
NoMatchingImplFound { error: NoMatchingImplFoundError, file: FileId },
ImplMethodTypeMismatch { expected: Type, actual: Type, location: Location },

Unimplemented { item: String, location: Location },

// Perhaps this should be unreachable! due to type checking also preventing this error?
// Currently it and the Continue variant are the only interpreter errors without a Location field
BreakNotInLoop { location: Location },
ContinueNotInLoop { location: Location },
BlackBoxError(BlackBoxResolutionError, Location),

Unimplemented { item: String, location: Location },

// These cases are not errors, they are just used to prevent us from running more code
// until the loop can be resumed properly. These cases will never be displayed to users.
Expand Down Expand Up @@ -118,9 +116,11 @@ impl InterpreterError {
| InterpreterError::Unimplemented { location, .. }
| InterpreterError::NoImpl { location, .. }
| InterpreterError::ImplMethodTypeMismatch { location, .. }
| InterpreterError::DebugEvaluateComptime { location, .. }
| InterpreterError::BlackBoxError(_, location)
| InterpreterError::BreakNotInLoop { location, .. }
| InterpreterError::DebugEvaluateComptime { location, .. } => *location,
InterpreterError::ContinueNotInLoop { location, .. } => *location,
| InterpreterError::ContinueNotInLoop { location, .. } => *location,

InterpreterError::FailedToParseMacro { error, file, .. } => {
Location::new(error.span(), *file)
}
Expand Down Expand Up @@ -370,6 +370,9 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic {
);
CustomDiagnostic::simple_error(msg, String::new(), location.span)
}
InterpreterError::BlackBoxError(error, location) => {
CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span)
}
InterpreterError::NoMatchingImplFound { error, .. } => error.into(),
InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"),
InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"),
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use super::errors::{IResult, InterpreterError};
use super::value::{unwrap_rc, Value};

mod builtin;
mod foreign;
mod unquote;

#[allow(unused)]
Expand Down Expand Up @@ -152,8 +153,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> {
location,
)
} else if let Some(foreign) = func_attrs.foreign() {
let item = format!("Comptime evaluation for foreign functions like {foreign}");
Err(InterpreterError::Unimplemented { item, location })
let foreign = foreign.clone();
foreign::call_foreign(self.elaborator.interner, &foreign, arguments, location)
} else if let Some(oracle) = func_attrs.oracle() {
if oracle == "print" {
self.print_oracle(arguments)
Expand Down
28 changes: 26 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub(super) fn call_builtin(
}
}

fn check_argument_count(
pub(super) fn check_argument_count(
expected: usize,
arguments: &[(Value, Location)],
location: Location,
Expand All @@ -76,6 +76,21 @@ fn failing_constraint<T>(message: impl Into<String>, location: Location) -> IRes
Err(InterpreterError::FailingConstraint { message, location })
}

pub(super) fn get_array(
interner: &NodeInterner,
value: Value,
location: Location,
) -> IResult<(im::Vector<Value>, Type)> {
match value {
Value::Array(values, typ) => Ok((values, typ)),
value => {
let type_var = Box::new(interner.next_type_variable());
let expected = Type::Array(type_var.clone(), type_var);
Err(InterpreterError::TypeMismatch { expected, value, location })
}
}
}

fn get_slice(
interner: &NodeInterner,
value: Value,
Expand All @@ -91,7 +106,16 @@ fn get_slice(
}
}

fn get_u32(value: Value, location: Location) -> IResult<u32> {
pub(super) fn get_field(value: Value, location: Location) -> IResult<FieldElement> {
match value {
Value::Field(value) => Ok(value),
value => {
Err(InterpreterError::TypeMismatch { expected: Type::FieldElement, value, location })
}
}
}

pub(super) fn get_u32(value: Value, location: Location) -> IResult<u32> {
match value {
Value::U32(value) => Ok(value),
value => {
Expand Down
48 changes: 48 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use acvm::BlackBoxFunctionSolver;
use bn254_blackbox_solver::Bn254BlackBoxSolver;
use iter_extended::try_vecmap;
use noirc_errors::Location;

use crate::{
hir::comptime::{errors::IResult, interpreter::builtin::get_field, InterpreterError, Value},
macros_api::NodeInterner,
};

use super::builtin::{check_argument_count, get_array, get_u32};

pub(super) fn call_foreign(
interner: &mut NodeInterner,
name: &str,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
match name {
"poseidon2_permutation" => poseidon2_permutation(interner, arguments, location),
_ => {
let item = format!("Comptime evaluation for builtin function {name}");
Err(InterpreterError::Unimplemented { item, location })
}
}
}

// poseidon2_permutation<let N: u32>(_input: [Field; N], _state_length: u32) -> [Field; N]
fn poseidon2_permutation(
interner: &mut NodeInterner,
mut arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(2, &arguments, location)?;

let state_length = get_u32(arguments.pop().unwrap().0, location)?;
let (input, typ) = get_array(interner, arguments.pop().unwrap().0, location)?;

let input = try_vecmap(input, |integer| get_field(integer, location))?;

// Currently locked to only bn254!
let fields = Bn254BlackBoxSolver
.poseidon2_permutation(&input, state_length)
.map_err(|error| InterpreterError::BlackBoxError(error, location))?;

let array = fields.into_iter().map(Value::Field).collect();
Ok(Value::Array(array, typ))
}

0 comments on commit 89dfbbf

Please sign in to comment.