Skip to content

Commit

Permalink
feat(avm): brillig CONST of size > u128 (#5217)
Browse files Browse the repository at this point in the history
The AVM cannot support setting constants of field size (because the whole instruction has to fit in a field). To align Brillig with AVM bytecode, I'm changing bytecode emission for the case where the constant is > 128 bits.

The current change splits the field into two 128bit limbs, and then multiplies and adds.
  • Loading branch information
fcarreiro authored Mar 15, 2024
1 parent 258ff4a commit 2e63479
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ impl Deserialize<2> for Note {

contract AvmTest {
use crate::Note;

global big_field_128_bits: Field = 0x001234567890abcdef1234567890abcdef;
global big_field_136_bits: Field = 0x991234567890abcdef1234567890abcdef;

// Libs
use dep::aztec::prelude::Map;
Expand Down Expand Up @@ -94,12 +97,6 @@ contract AvmTest {
8 as u8
}

// Bit size 16 in Noir is deprecated.
// #[aztec(public-vm)]
// fn setOpcodeUint16() -> pub u16 {
// 60000 as u16
// }

#[aztec(public-vm)]
fn setOpcodeUint32() -> pub u32 {
1 << 30 as u32
Expand All @@ -110,18 +107,14 @@ contract AvmTest {
1 << 60 as u64
}

// Can't return this since it doesn't fit in a Noir field.
// #[aztec(public-vm)]
// fn setOpcodeUint128() -> pub u128 {
// 1 << 120 as u128
// }

// Field should fit in 128 bits
// ACIR only supports fields of up to 126 bits!
// Same with internal fields for unconstrained functions, apprently.
#[aztec(public-vm)]
fn setOpcodeSmallField() -> pub Field {
200 as Field
big_field_128_bits
}

#[aztec(public-vm)]
fn setOpcodeBigField() -> pub Field {
big_field_136_bits
}

#[aztec(public-vm)]
Expand Down
52 changes: 47 additions & 5 deletions noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,11 +593,53 @@ impl BrilligContext {
pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) {
self.debug_show.const_instruction(result.address, constant);

self.push_opcode(BrilligOpcode::Const {
destination: result.address,
value: constant,
bit_size: result.bit_size,
});
if result.bit_size > 128 && !constant.to_field().fits_in_u128() {
let high = Value::from(FieldElement::from_be_bytes_reduce(
constant
.to_field()
.to_be_bytes()
.get(0..16)
.expect("FieldElement::to_be_bytes() too short!"),
));
let low = Value::from(constant.to_u128());
let high_register = SingleAddrVariable::new(self.allocate_register(), 254);
let low_register = SingleAddrVariable::new(self.allocate_register(), 254);
let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254);
self.const_instruction(high_register, high);
self.const_instruction(low_register, low);
// I want to multiply high by 2^128, but I can't get that big constant in.
// So I'll multiply by 2^64 twice.
self.const_instruction(intermediate_register, Value::from(1_u128 << 64));
self.binary_instruction(
high_register,
intermediate_register,
high_register,
BrilligBinaryOp::Mul,
);
self.binary_instruction(
high_register,
intermediate_register,
high_register,
BrilligBinaryOp::Mul,
);
// Now we can add.
self.binary_instruction(
high_register,
low_register,
intermediate_register,
BrilligBinaryOp::Add,
);
self.cast_instruction(result, intermediate_register);
self.deallocate_single_addr(high_register);
self.deallocate_single_addr(low_register);
self.deallocate_single_addr(intermediate_register);
} else {
self.push_opcode(BrilligOpcode::Const {
destination: result.address,
value: constant,
bit_size: result.bit_size,
});
}
}

pub(crate) fn usize_const(&mut self, result: MemoryAddress, constant: Value) {
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ describe('AVM simulator', () => {

describe.each([
['avm_setOpcodeUint8', 8n],
// ['avm_setOpcodeUint16', 60000n],
['avm_setOpcodeUint32', 1n << 30n],
['avm_setOpcodeUint64', 1n << 60n],
// ['avm_setOpcodeUint128', 1n << 120n],
['avm_setOpcodeSmallField', 200n],
['avm_setOpcodeSmallField', 0x001234567890abcdef1234567890abcdefn],
['avm_setOpcodeBigField', 0x991234567890abcdef1234567890abcdefn],
])('Should execute contract SET functions', (name: string, res: bigint) => {
it(`Should execute contract function '${name}'`, async () => {
const context = initContext();
Expand Down

0 comments on commit 2e63479

Please sign in to comment.