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

feat: Implement endianness specified versions of to_bytes to_radix and to_bits #914

Merged
merged 11 commits into from
Mar 1, 2023
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
5 changes: 5 additions & 0 deletions crates/nargo/tests/test_data/to_be_bytes/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
authors = [""]
compiler_version = "0.1"

[dependencies]
1 change: 1 addition & 0 deletions crates/nargo/tests/test_data/to_be_bytes/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x = "2040124"
14 changes: 14 additions & 0 deletions crates/nargo/tests/test_data/to_be_bytes/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use dep::std;

fn main(x : Field) -> pub [u8; 31] {
// The result of this byte array will be big-endian
let byte_array = x.to_be_bytes(31);
let mut bytes = [0; 31];
for i in 0..31 {
bytes[i] = byte_array[i];
guipublic marked this conversation as resolved.
Show resolved Hide resolved
}
constrain bytes[30] == 60;
constrain bytes[29] == 33;
constrain bytes[28] == 31;
bytes
}
18 changes: 13 additions & 5 deletions crates/noirc_evaluator/src/ssa/acir_gen/constraints.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
errors::RuntimeErrorKind,
ssa::acir_gen::{const_from_expression, expression_from_witness, expression_to_witness},
ssa::{
acir_gen::{const_from_expression, expression_from_witness, expression_to_witness},
builtin::Endian,
},
Evaluator,
};
use acvm::{
Expand Down Expand Up @@ -395,21 +398,27 @@ pub(crate) fn to_radix_base(
lhs: &Expression,
radix: u32,
limb_size: u32,
endianness: Endian,
evaluator: &mut Evaluator,
) -> Vec<Witness> {
// ensure there is no overflow
let mut max = BigUint::from(radix);
max = max.pow(limb_size) - BigUint::one();
assert!(max < FieldElement::modulus());

let (result, bytes) = to_radix(radix, limb_size, evaluator);
let (mut result, bytes) = to_radix_little(radix, limb_size, evaluator);

evaluator.opcodes.push(AcirOpcode::Directive(Directive::ToRadix {
a: lhs.clone(),
b: result.clone(),
radix,
is_little_endian: true,
}));

if endianness == Endian::Big {
result.reverse();
}

evaluator.opcodes.push(AcirOpcode::Arithmetic(subtract(lhs, FieldElement::one(), &bytes)));

result
Expand All @@ -419,7 +428,7 @@ pub(crate) fn to_radix_base(
// radix: the base, (it is a constant, not a witness)
// num_limbs: the number of elements in the decomposition
// output: (the elements of the decomposition as witness, the sum expression)
pub(crate) fn to_radix(
pub(crate) fn to_radix_little(
radix: u32,
num_limbs: u32,
evaluator: &mut Evaluator,
Expand Down Expand Up @@ -448,7 +457,6 @@ pub(crate) fn to_radix(
evaluator,
);
}

(result, digits)
}

Expand All @@ -465,7 +473,7 @@ pub(crate) fn evaluate_cmp(
let mut sub_expr = subtract(lhs, FieldElement::one(), rhs);
let two_pow = BigUint::one() << (bit_size + 1);
sub_expr.q_c += FieldElement::from_be_bytes_reduce(&two_pow.to_bytes_be());
let bits = to_radix_base(&sub_expr, 2, bit_size + 2, evaluator);
let bits = to_radix_base(&sub_expr, 2, bit_size + 2, Endian::Little, evaluator);
expression_from_witness(bits[(bit_size - 1) as usize])
} else {
let is_greater = expression_from_witness(bound_check(lhs, rhs, bit_size, evaluator));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ pub(crate) fn evaluate(

let outputs;
match opcode {
Opcode::ToBits => {
Opcode::ToBits(endianess) => {
// TODO: document where `0` and `1` are coming from, for args[0], args[1]
let bit_size = ctx.get_as_constant(args[1]).unwrap().to_u128() as u32;
let l_c = var_cache.get_or_compute_internal_var_unwrap(args[0], evaluator, ctx);
outputs = to_radix_base(l_c.expression(), 2, bit_size, evaluator);
outputs = to_radix_base(l_c.expression(), 2, bit_size, endianess, evaluator);
if let ObjectType::Pointer(a) = res_type {
memory_map.map_array(a, &outputs, ctx);
}
}
Opcode::ToRadix => {
Opcode::ToRadix(endianess) => {
// TODO: document where `0`, `1` and `2` are coming from, for args[0],args[1], args[2]
let radix = ctx.get_as_constant(args[1]).unwrap().to_u128() as u32;
let limb_size = ctx.get_as_constant(args[2]).unwrap().to_u128() as u32;
let l_c = var_cache.get_or_compute_internal_var_unwrap(args[0], evaluator, ctx);
outputs = to_radix_base(l_c.expression(), radix, limb_size, evaluator);
outputs = to_radix_base(l_c.expression(), radix, limb_size, endianess, evaluator);
if let ObjectType::Pointer(a) = res_type {
memory_map.map_array(a, &outputs, ctx);
}
Expand Down
40 changes: 31 additions & 9 deletions crates/noirc_evaluator/src/ssa/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use num_traits::{One, Zero};
#[derive(Clone, Debug, Hash, Copy, PartialEq, Eq)]
pub enum Opcode {
LowLevel(BlackBoxFunc),
ToBits,
ToRadix,
ToBits(Endian),
ToRadix(Endian),
Println(PrintlnInfo),
Sort,
}
Expand All @@ -33,8 +33,10 @@ impl Opcode {
/// corresponds to any of the opcodes.
pub fn lookup(op_name: &str) -> Option<Opcode> {
match op_name {
"to_le_bits" => Some(Opcode::ToBits),
"to_radix" => Some(Opcode::ToRadix),
"to_le_bits" => Some(Opcode::ToBits(Endian::Little)),
"to_be_bits" => Some(Opcode::ToBits(Endian::Big)),
"to_le_radix" => Some(Opcode::ToRadix(Endian::Little)),
"to_be_radix" => Some(Opcode::ToRadix(Endian::Big)),
"println" => {
Some(Opcode::Println(PrintlnInfo { is_string_output: false, show_output: true }))
}
Expand All @@ -46,8 +48,20 @@ impl Opcode {
fn name(&self) -> &str {
match self {
Opcode::LowLevel(op) => op.name(),
Opcode::ToBits => "to_le_bits",
Opcode::ToRadix => "to_radix",
Opcode::ToBits(endianness) => {
if *endianness == Endian::Little {
"to_le_bits"
} else {
"to_be_bits"
}
}
Opcode::ToRadix(endianness) => {
if *endianness == Endian::Little {
"to_le_radix"
} else {
"to_be_radix"
}
}
Opcode::Println(_) => "println",
Opcode::Sort => "arraysort",
}
Expand Down Expand Up @@ -78,7 +92,9 @@ impl Opcode {
}
}
}
Opcode::ToBits | Opcode::ToRadix | Opcode::Println(_) | Opcode::Sort => BigUint::zero(), //pointers do not overflow
Opcode::ToBits(_) | Opcode::ToRadix(_) | Opcode::Println(_) | Opcode::Sort => {
BigUint::zero()
} //pointers do not overflow
}
}

Expand All @@ -105,8 +121,8 @@ impl Opcode {
}
}
}
Opcode::ToBits => (FieldElement::max_num_bits(), ObjectType::Boolean),
Opcode::ToRadix => (FieldElement::max_num_bits(), ObjectType::NativeField),
Opcode::ToBits(_) => (FieldElement::max_num_bits(), ObjectType::Boolean),
Opcode::ToRadix(_) => (FieldElement::max_num_bits(), ObjectType::NativeField),
Opcode::Println(_) => (0, ObjectType::NotAnObject),
Opcode::Sort => {
let a = super::mem::Memory::deref(ctx, args[0]).unwrap();
Expand All @@ -124,3 +140,9 @@ pub struct PrintlnInfo {
// This is a flag used during `nargo test` to determine whether to display println output.
pub show_output: bool,
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Endian {
Big,
Little,
}
2 changes: 1 addition & 1 deletion crates/noirc_evaluator/src/ssa/optimizations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn evaluate_intrinsic(
block_id: BlockId,
) -> Vec<NodeId> {
match op {
builtin::Opcode::ToBits => {
builtin::Opcode::ToBits(_) => {
let bit_count = args[1] as u32;
let mut result = Vec::new();

Expand Down
13 changes: 10 additions & 3 deletions noir_stdlib/src/field.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@
impl Field {
#[builtin(to_le_bits)]
fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] {}
#[builtin(to_be_bits)]
fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] {}

fn to_le_bytes(x : Field, byte_size: u32) -> [u8] {
x.to_radix(256, byte_size)
x.to_le_radix(256, byte_size)
}
fn to_be_bytes(x : Field, byte_size: u32) -> [u8] {
x.to_be_radix(256, byte_size)
}

#[builtin(to_radix)]
#[builtin(to_le_radix)]
//decompose _x into a _result_len vector over the _radix basis
//_radix must be less than 256
fn to_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {}
fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {}
#[builtin(to_be_radix)]
fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] {}

// Returns self to the power of the given exponent value.
// Caution: we assume the exponent fits into 32 bits
Expand Down