From 5cef325c11ce04a5da796fdaa854a90eb9fdcd4f Mon Sep 17 00:00:00 2001 From: sirasistant Date: Tue, 1 Apr 2025 09:47:50 +0000 Subject: [PATCH] fix: Transpile cmov --- avm-transpiler/src/transpile.rs | 42 +++++++++++++++++++ .../contracts/avm_test_contract/src/main.nr | 9 ++++ .../src/public/avm/avm_simulator.test.ts | 17 ++++++++ 3 files changed, 68 insertions(+) diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs index 085b48227951..e1da3bbdef29 100644 --- a/avm-transpiler/src/transpile.rs +++ b/avm-transpiler/src/transpile.rs @@ -322,6 +322,48 @@ pub fn brillig_to_avm(brillig_bytecode: &[BrilligOpcode]) -> (Vec< destination.to_usize() as u32, )); } + BrilligOpcode::ConditionalMov { destination, source_a, source_b, condition } => { + // Move source_a to destination, if condition is true jump to the next brillig opcode, else move source_b to destination + avm_instrs.push(generate_mov_instruction( + Some( + AddressingModeBuilder::default() + .direct_operand(source_a) + .direct_operand(destination) + .build(), + ), + source_a.to_usize() as u32, + destination.to_usize() as u32, + )); + + unresolved_jumps.insert( + UnresolvedPCLocation { + instruction_index: avm_instrs.len(), + immediate_index: 0, + }, + Label::BrilligPC { pc: brillig_pcs_to_avm_pcs.len() as u32 }, // We want to jump to the next brillig opcode + ); + + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::JUMPI_32, + indirect: Some( + AddressingModeBuilder::default().direct_operand(condition).build(), + ), + operands: vec![make_operand(16, &condition.to_usize())], + immediates: vec![make_unresolved_pc()], + ..Default::default() + }); + + avm_instrs.push(generate_mov_instruction( + Some( + AddressingModeBuilder::default() + .direct_operand(source_b) + .direct_operand(destination) + .build(), + ), + source_b.to_usize() as u32, + destination.to_usize() as u32, + )); + } BrilligOpcode::Load { destination, source_pointer } => { avm_instrs.push(generate_mov_instruction( Some( 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 768ad9df33e0..e71ec598826c 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 @@ -191,6 +191,15 @@ pub contract AvmTest { commitment } + #[public] + fn conditional_move(x: [Field; 1], y: [Field; 1], b: bool) -> [Field; 1] { + if b { + x + } else { + y + } + } + /************************************************************************ * Misc ************************************************************************/ diff --git a/yarn-project/simulator/src/public/avm/avm_simulator.test.ts b/yarn-project/simulator/src/public/avm/avm_simulator.test.ts index 4465582819b2..a50aa55729ab 100644 --- a/yarn-project/simulator/src/public/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/public/avm/avm_simulator.test.ts @@ -352,6 +352,23 @@ describe('AVM simulator: transpiled Noir contracts', () => { expect(results.output).toEqual(expectedResult); }); + it('conditional move operations', async () => { + const calldata: Fr[] = [new Fr(27), new Fr(28), new Fr(1)]; + + const bytecode = getAvmTestContractBytecode('conditional_move'); + + let context = initContext({ env: initExecutionEnvironment({ calldata }) }); + let results = await new AvmSimulator(context).executeBytecode(bytecode); + expect(results.reverted).toBe(false); + expect(results.output).toEqual([new Fr(27)]); + + calldata[2] = new Fr(0); + context = initContext({ env: initExecutionEnvironment({ calldata }) }); + results = await new AvmSimulator(context).executeBytecode(bytecode); + expect(results.reverted).toBe(false); + expect(results.output).toEqual([new Fr(28)]); + }); + describe('U128 addition and overflows', () => { it('U128 addition', async () => { const calldata: Fr[] = [