From b40cf95b362c4471e1ad200a0ad8bf0fca854757 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 14 Mar 2024 12:15:49 +0000 Subject: [PATCH] feat(avm): brillig CONST of size > u128 --- .../contracts/avm_test_contract/src/main.nr | 25 ++++----- .../noirc_evaluator/src/brillig/brillig_ir.rs | 52 +++++++++++++++++-- .../simulator/src/avm/avm_simulator.test.ts | 5 +- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr index 675b8c40867..2f2c45609df 100644 --- a/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -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; @@ -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 @@ -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)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 80b13c8f6ba..357f71f20ec 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -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::Field(BinaryFieldOp::Mul), + ); + self.binary_instruction( + high_register, + intermediate_register, + high_register, + BrilligBinaryOp::Field(BinaryFieldOp::Mul), + ); + // Now we can add. + self.binary_instruction( + high_register, + low_register, + intermediate_register, + BrilligBinaryOp::Field(BinaryFieldOp::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) { diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index b8e4708d2aa..1561c064428 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -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();