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: replace to_radix directive with brillig #6386

Closed
wants to merge 8 commits into from
Closed
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
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.

1 change: 0 additions & 1 deletion acvm-repo/acvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ repository.workspace = true
workspace = true

[dependencies]
num-bigint.workspace = true
thiserror.workspace = true
tracing.workspace = true
serde.workspace = true
Expand Down
4 changes: 2 additions & 2 deletions acvm-repo/acvm/src/compiler/optimizers/merge_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
use acir::{
circuit::{
brillig::{BrilligInputs, BrilligOutputs},
directives::Directive,
opcodes::BlockId,
Circuit, Opcode,
},
Expand Down Expand Up @@ -157,7 +156,6 @@ impl MergeExpressionsOptimizer {
match opcode {
Opcode::AssertZero(expr) => CircuitSimulator::expr_wit(expr),
Opcode::BlackBoxFuncCall(bb_func) => bb_func.get_input_witnesses(),
Opcode::Directive(Directive::ToLeRadix { a, .. }) => CircuitSimulator::expr_wit(a),
Opcode::MemoryOp { block_id: _, op, predicate } => {
//index et value, et predicate
let mut witnesses = BTreeSet::new();
Expand Down Expand Up @@ -193,6 +191,8 @@ impl MergeExpressionsOptimizer {
}
witnesses
}
// Directive opcode is to be removed
Opcode::Directive(_) => unreachable!(),
}
}

Expand Down
15 changes: 3 additions & 12 deletions acvm-repo/acvm/src/compiler/transformers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use acir::{
circuit::{brillig::BrilligOutputs, directives::Directive, Circuit, ExpressionWidth, Opcode},
circuit::{brillig::BrilligOutputs, Circuit, ExpressionWidth, Opcode},
native_types::{Expression, Witness},
AcirField,
};
Expand Down Expand Up @@ -104,17 +104,6 @@ pub(super) fn transform_internal<F: AcirField>(
new_acir_opcode_positions.push(acir_opcode_positions[index]);
transformed_opcodes.push(opcode);
}
Opcode::Directive(ref directive) => {
match directive {
Directive::ToLeRadix { b, .. } => {
for witness in b {
transformer.mark_solvable(*witness);
}
}
}
new_acir_opcode_positions.push(acir_opcode_positions[index]);
transformed_opcodes.push(opcode);
}
Opcode::MemoryInit { .. } => {
// `MemoryInit` does not write values to the `WitnessMap`
new_acir_opcode_positions.push(acir_opcode_positions[index]);
Expand Down Expand Up @@ -156,6 +145,8 @@ pub(super) fn transform_internal<F: AcirField>(
new_acir_opcode_positions.push(acir_opcode_positions[index]);
transformed_opcodes.push(opcode);
}
// Directive opcode is to be removed
Opcode::Directive(_) => unreachable!(),
}
}

Expand Down
49 changes: 0 additions & 49 deletions acvm-repo/acvm/src/pwg/directives/mod.rs

This file was deleted.

8 changes: 3 additions & 5 deletions acvm-repo/acvm/src/pwg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
use acvm_blackbox_solver::BlackBoxResolutionError;

use self::{
arithmetic::ExpressionSolver, blackbox::bigint::AcvmBigIntSolver, directives::solve_directives,
memory_op::MemoryOpSolver,
arithmetic::ExpressionSolver, blackbox::bigint::AcvmBigIntSolver, memory_op::MemoryOpSolver,
};
use crate::BlackBoxFunctionSolver;

Expand All @@ -29,8 +28,6 @@
pub(crate) mod arithmetic;
// Brillig bytecode
pub(crate) mod brillig;
// Directives
pub(crate) mod directives;
// black box functions
pub(crate) mod blackbox;
mod memory_op;
Expand Down Expand Up @@ -58,7 +55,7 @@
RequiresForeignCall(ForeignCallWaitInfo<F>),

/// The ACVM has encountered a request for an ACIR [call][acir::circuit::Opcode]
/// to execute a separate ACVM instance. The result of the ACIR call must be passd back to the ACVM.

Check warning on line 58 in acvm-repo/acvm/src/pwg/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (passd)
///
/// Once this is done, the ACVM can be restarted to solve the remaining opcodes.
RequiresAcirCall(AcirCallWaitInfo<F>),
Expand Down Expand Up @@ -371,7 +368,6 @@
bb_func,
&mut self.bigint_solver,
),
Opcode::Directive(directive) => solve_directives(&mut self.witness_map, directive),
Opcode::MemoryInit { block_id, init, .. } => {
let solver = self.block_solvers.entry(*block_id).or_default();
solver.init(init, &self.witness_map)
Expand All @@ -388,6 +384,8 @@
Ok(Some(input_values)) => return self.wait_for_acir_call(input_values),
res => res.map(|_| ()),
},
// Directive opcode is to be removed
Opcode::Directive(_) => unreachable!(),
};
self.handle_opcode_resolution(resolution)
}
Expand Down
127 changes: 126 additions & 1 deletion compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use acvm::acir::{
brillig::{BinaryFieldOp, BitSize, IntegerBitSize, MemoryAddress, Opcode as BrilligOpcode},
brillig::{
BinaryFieldOp, BinaryIntOp, BitSize, HeapVector, IntegerBitSize, MemoryAddress,
Opcode as BrilligOpcode,
},
AcirField,
};

Expand Down Expand Up @@ -135,3 +138,125 @@ pub(crate) fn directive_quotient<F: AcirField>() -> GeneratedBrillig<F> {
..Default::default()
}
}

/// Generates brillig bytecode which performs a radix-base decomposition of `a`
/// The brillig inputs are 'a', the numbers of limbs and the radix
pub(crate) fn directive_to_radix<F: AcirField>() -> GeneratedBrillig<F> {
let memory_adr_int_size = IntegerBitSize::U32;
let memory_adr_size = BitSize::Integer(memory_adr_int_size);

// (0) is the input field `a` to decompose
// (1) contains the number of limbs (second input)
let limbs_nb = MemoryAddress::direct(1);
// (2) contains the radix (third input)
let radix = MemoryAddress::direct(2);
// (3) and (4) are intermediate registers
// (5,6,7) are constants: 0,1,3
let zero = MemoryAddress::direct(5);
let one = MemoryAddress::direct(6);
let three = MemoryAddress::direct(7);
// (7) is the iteration bound, it is the same register as three because the latter is only used at the start
let bound = MemoryAddress::direct(7);
// (8) is the register for storing the loop condition
let cond = MemoryAddress::direct(8);
// (9) is the pointer to the result array
let result_pointer = MemoryAddress::direct(9);
// address of the result array
let result_base_adr = 10_usize;

let result_vector =
HeapVector { pointer: MemoryAddress::direct(result_base_adr), size: limbs_nb };

let byte_code = vec![
// Initialize registers
// Constants
// Zero
BrilligOpcode::Const { destination: zero, bit_size: memory_adr_size, value: F::zero() },
// One
BrilligOpcode::Const {
destination: one,
bit_size: memory_adr_size,
value: F::from(1_usize),
},
// Three
BrilligOpcode::Const {
destination: three,
bit_size: memory_adr_size,
value: F::from(3_usize),
},
// Brillig Inputs
BrilligOpcode::CalldataCopy {
destination_address: MemoryAddress::direct(0),
size_address: three,
offset_address: zero,
},
// The number of limbs needs to be an integer
BrilligOpcode::Cast { destination: limbs_nb, source: limbs_nb, bit_size: memory_adr_size },
// Result_pointer starts at the base address
BrilligOpcode::Const {
destination: result_pointer,
bit_size: memory_adr_size,
value: F::from(result_base_adr),
},
// Loop bound
BrilligOpcode::BinaryIntOp {
destination: bound,
op: BinaryIntOp::Add,
bit_size: memory_adr_int_size,
lhs: result_pointer,
rhs: limbs_nb,
},
// loop label: (3) = a / radix (integer division)
BrilligOpcode::BinaryFieldOp {
op: BinaryFieldOp::IntegerDiv,
lhs: MemoryAddress::direct(0),
rhs: radix,
destination: MemoryAddress::direct(3),
},
//(4) = (3)*256
BrilligOpcode::BinaryFieldOp {
op: BinaryFieldOp::Mul,
lhs: MemoryAddress::direct(3),
rhs: radix,
destination: MemoryAddress::direct(4),
},
//(4) = a-(3)*256 (remainder)
BrilligOpcode::BinaryFieldOp {
op: BinaryFieldOp::Sub,
lhs: MemoryAddress::direct(0),
rhs: MemoryAddress::direct(4),
destination: MemoryAddress::direct(4),
},
// Store the remainder in the result array
BrilligOpcode::Store {
destination_pointer: result_pointer,
source: MemoryAddress::direct(4),
},
// Increment the result pointer
BrilligOpcode::BinaryIntOp {
op: BinaryIntOp::Add,
lhs: result_pointer,
rhs: one,
destination: result_pointer,
bit_size: memory_adr_int_size,
},
//a := quotient
BrilligOpcode::Mov {
destination: MemoryAddress::direct(0),
source: MemoryAddress::direct(3),
},
// loop condition
BrilligOpcode::BinaryIntOp {
op: BinaryIntOp::LessThan,
lhs: result_pointer,
rhs: bound,
destination: cond,
bit_size: memory_adr_int_size,
},
// loop back
BrilligOpcode::JumpIf { condition: cond, location: 7 },
BrilligOpcode::Stop { return_data: result_vector },
];

GeneratedBrillig { byte_code, name: "directive_to_radix".to_string(), ..Default::default() }
}
64 changes: 52 additions & 12 deletions compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ use acvm::acir::{
native_types::Witness,
BlackBoxFunc,
};
use acvm::{
acir::AcirField,
acir::{circuit::directives::Directive, native_types::Expression},
};

use acvm::{acir::native_types::Expression, acir::AcirField};
use iter_extended::vecmap;
use noirc_errors::debug_info::ProcedureDebugId;
use num_bigint::BigUint;
Expand Down Expand Up @@ -92,13 +88,15 @@ pub(crate) type BrilligProcedureRangeMap = BTreeMap<ProcedureDebugId, (usize, us
pub(crate) enum BrilligStdlibFunc {
Inverse,
Quotient,
ToLeBytes,
}

impl BrilligStdlibFunc {
pub(crate) fn get_generated_brillig<F: AcirField>(&self) -> GeneratedBrillig<F> {
match self {
BrilligStdlibFunc::Inverse => brillig_directive::directive_invert(),
BrilligStdlibFunc::Quotient => brillig_directive::directive_quotient(),
BrilligStdlibFunc::ToLeBytes => brillig_directive::directive_to_radix(),
}
}
}
Expand Down Expand Up @@ -377,13 +375,7 @@ impl<F: AcirField> GeneratedAcir<F> {
"ICE: Radix must be a power of 2"
);

let limb_witnesses = vecmap(0..limb_count, |_| self.next_witness_index());
self.push_opcode(AcirOpcode::Directive(Directive::ToLeRadix {
a: input_expr.clone(),
b: limb_witnesses.clone(),
radix,
}));

let limb_witnesses = self.brillig_to_radix(input_expr, radix, limb_count);
let mut composed_limbs = Expression::default();

let mut radix_pow = BigUint::from(1u128);
Expand All @@ -403,6 +395,54 @@ impl<F: AcirField> GeneratedAcir<F> {
Ok(limb_witnesses)
}

/// Adds brillig opcode for to_radix
///
/// This code will decompose `expr` in a radix-base
/// and return `Witnesses` which may (or not, because it does not apply constraints)
/// be limbs resulting from the decomposition.
///
/// Safety: It is the callers responsibility to ensure that the
/// resulting `Witnesses` are properly constrained.
pub(crate) fn brillig_to_radix(
&mut self,
expr: &Expression<F>,
radix: u32,
limb_count: u32,
) -> Vec<Witness> {
// Create the witness for the result
let limb_witnesses = vecmap(0..limb_count, |_| self.next_witness_index());

// Get the decomposition brillig code
let le_bytes_code = brillig_directive::directive_to_radix();
// Prepare the inputs/outputs
let limbs_nb = Expression {
mul_terms: Vec::new(),
linear_combinations: Vec::new(),
q_c: F::from(limb_count as u128),
};
let radix_expr = Expression {
mul_terms: Vec::new(),
linear_combinations: Vec::new(),
q_c: F::from(radix as u128),
};
let inputs = vec![
BrilligInputs::Single(expr.clone()),
BrilligInputs::Single(limbs_nb),
BrilligInputs::Single(radix_expr),
];
let outputs = vec![BrilligOutputs::Array(limb_witnesses.clone())];

self.brillig_call(
None,
&le_bytes_code,
inputs,
outputs,
PLACEHOLDER_BRILLIG_INDEX,
Some(BrilligStdlibFunc::ToLeBytes),
);
limb_witnesses
}

/// Adds an inversion brillig opcode.
///
/// This code will invert `expr` without applying constraints
Expand Down
Loading
Loading