Skip to content

Commit

Permalink
feat: add comptime support for modulus_* compiler builtins (#5530)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## Summary\*

This PR changes the function `std::compat::is_bn254` to run in a
`comptime` context. To do this I've had to implement a few compiler
builtins within the interpreter.

I've had to mangle `std::compat::is_bn254` a bit to get around
restrictions on when a function can be called in a `comptime` context
however.

This works around around the loop unrolling issue experienced in #4535.

## 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.
- [ ] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
TomAFrench authored Jul 17, 2024
1 parent e318f45 commit 5bbce79
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 7 deletions.
70 changes: 70 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{
rc::Rc,
};

use acvm::{AcirField, FieldElement};
use chumsky::Parser;
use noirc_errors::Location;

Expand All @@ -25,6 +26,11 @@ pub(super) fn call_builtin(
"array_len" => array_len(interner, arguments, location),
"as_slice" => as_slice(interner, arguments, location),
"is_unconstrained" => Ok(Value::Bool(true)),
"modulus_be_bits" => modulus_be_bits(interner, arguments, location),
"modulus_be_bytes" => modulus_be_bytes(interner, arguments, location),
"modulus_le_bits" => modulus_le_bits(interner, arguments, location),
"modulus_le_bytes" => modulus_le_bytes(interner, arguments, location),
"modulus_num_bits" => modulus_num_bits(interner, arguments, location),
"slice_insert" => slice_insert(interner, arguments, location),
"slice_pop_back" => slice_pop_back(interner, arguments, location),
"slice_pop_front" => slice_pop_front(interner, arguments, location),
Expand Down Expand Up @@ -386,3 +392,67 @@ fn trait_constraint_eq(

Ok(Value::Bool(constraint_a == constraint_b))
}

fn modulus_be_bits(
_interner: &mut NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(0, &arguments, location)?;

let bits = FieldElement::modulus().to_radix_be(2);
let bits_vector = bits.into_iter().map(|bit| Value::U1(bit != 0)).collect();

let int_type = Type::Integer(crate::ast::Signedness::Unsigned, IntegerBitSize::One);
let typ = Type::Slice(Box::new(int_type));
Ok(Value::Slice(bits_vector, typ))
}

fn modulus_be_bytes(
_interner: &mut NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(0, &arguments, location)?;

let bytes = FieldElement::modulus().to_bytes_be();
let bytes_vector = bytes.into_iter().map(Value::U8).collect();

let int_type = Type::Integer(crate::ast::Signedness::Unsigned, IntegerBitSize::Eight);
let typ = Type::Slice(Box::new(int_type));
Ok(Value::Slice(bytes_vector, typ))
}

fn modulus_le_bits(
interner: &mut NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let Value::Slice(bits, typ) = modulus_be_bits(interner, arguments, location)? else {
unreachable!("modulus_be_bits must return slice")
};
let reversed_bits = bits.into_iter().rev().collect();
Ok(Value::Slice(reversed_bits, typ))
}

fn modulus_le_bytes(
interner: &mut NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
let Value::Slice(bytes, typ) = modulus_be_bytes(interner, arguments, location)? else {
unreachable!("modulus_be_bytes must return slice")
};
let reversed_bytes = bytes.into_iter().rev().collect();
Ok(Value::Slice(reversed_bytes, typ))
}

fn modulus_num_bits(
_interner: &mut NodeInterner,
arguments: Vec<(Value, Location)>,
location: Location,
) -> IResult<Value> {
check_argument_count(0, &arguments, location)?;
let bits = FieldElement::max_num_bits().into();
Ok(Value::U64(bits))
}
9 changes: 9 additions & 0 deletions compiler/noirc_frontend/src/hir/comptime/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum Value {
I16(i16),
I32(i32),
I64(i64),
U1(bool),
U8(u8),
U16(u16),
U32(u32),
Expand Down Expand Up @@ -62,6 +63,7 @@ impl Value {
Value::I16(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Sixteen),
Value::I32(_) => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo),
Value::I64(_) => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour),
Value::U1(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::One),
Value::U8(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight),
Value::U16(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Sixteen),
Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo),
Expand Down Expand Up @@ -124,6 +126,9 @@ impl Value {
let value = (value as u128).into();
ExpressionKind::Literal(Literal::Integer(value, negative))
}
Value::U1(value) => {
ExpressionKind::Literal(Literal::Integer((value as u128).into(), false))
}
Value::U8(value) => {
ExpressionKind::Literal(Literal::Integer((value as u128).into(), false))
}
Expand Down Expand Up @@ -249,6 +254,9 @@ impl Value {
let value = (value as u128).into();
HirExpression::Literal(HirLiteral::Integer(value, negative))
}
Value::U1(value) => {
HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false))
}
Value::U8(value) => {
HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false))
}
Expand Down Expand Up @@ -385,6 +393,7 @@ impl Display for Value {
Value::I16(value) => write!(f, "{value}"),
Value::I32(value) => write!(f, "{value}"),
Value::I64(value) => write!(f, "{value}"),
Value::U1(value) => write!(f, "{value}"),
Value::U8(value) => write!(f, "{value}"),
Value::U16(value) => write!(f, "{value}"),
Value::U32(value) => write!(f, "{value}"),
Expand Down
18 changes: 16 additions & 2 deletions noir_stdlib/src/compat.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
global BN254_MODULUS_BE_BYTES: [u8] = &[
comptime global BN254_MODULUS_BE_BYTES: [u8] = &[
48, 100, 78, 114, 225, 49, 160, 41, 184, 80, 69, 182, 129, 129, 88, 93, 40, 51, 232, 72, 121, 185, 112, 145, 67, 225, 245, 147, 240, 0, 0, 1
];

pub fn is_bn254() -> bool {
crate::field::modulus_be_bytes() == BN254_MODULUS_BE_BYTES
comptime
{
// We can't use the `Eq` trait here due to limitations on calling non-comptime functions
// defined within the same crate.
let mut eq = true;

let modulus_be_bytes = crate::field::modulus_be_bytes();
// We can't do `BN254_MODULUS_BE_BYTES.len()` due to limitations on calling non-comptime functions.
assert_eq(crate::field::modulus_num_bits(), 254);
for i in 0..32 {
eq &= modulus_be_bytes[i] == BN254_MODULUS_BE_BYTES[i];
}

eq
}
}
11 changes: 6 additions & 5 deletions noir_stdlib/src/field/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,20 @@ impl Field {
}

#[builtin(modulus_num_bits)]
pub fn modulus_num_bits() -> u64 {}
pub comptime fn modulus_num_bits() -> u64 {}

#[builtin(modulus_be_bits)]
pub fn modulus_be_bits() -> [u1] {}
pub comptime fn modulus_be_bits() -> [u1] {}

#[builtin(modulus_le_bits)]
pub fn modulus_le_bits() -> [u1] {}
pub comptime fn modulus_le_bits() -> [u1] {}

#[builtin(modulus_be_bytes)]
pub fn modulus_be_bytes() -> [u8] {}
pub comptime fn modulus_be_bytes() -> [u8] {}

#[builtin(modulus_le_bytes)]
pub fn modulus_le_bytes() -> [u8] {}
pub comptime fn modulus_le_bytes() -> [u8] {}

// Convert a 32 byte array to a field element by modding
pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field {
// Convert it to a field element
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "comptime_slice_equality"
type = "bin"
authors = [""]
compiler_version = ">=0.31.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
comptime
{
assert_eq(&[1], &[1]);
}
}

0 comments on commit 5bbce79

Please sign in to comment.