diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index ee61a9d13d3..1183f415f79 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -9,7 +9,10 @@ mod variable_liveness; use acvm::FieldElement; use self::{brillig_block::BrilligBlock, brillig_fn::FunctionContext}; -use super::brillig_ir::{artifact::BrilligArtifact, BrilligContext}; +use super::brillig_ir::{ + artifact::{BrilligArtifact, Label}, + BrilligContext, +}; use crate::ssa::ir::function::Function; /// Converting an SSA function into Brillig bytecode. @@ -21,7 +24,7 @@ pub(crate) fn convert_ssa_function( let mut function_context = FunctionContext::new(func); - brillig_context.enter_context(FunctionContext::function_id_to_function_label(func.id())); + brillig_context.enter_context(Label::function(func.id())); for block in function_context.blocks.clone() { BrilligBlock::compile(&mut function_context, &mut brillig_context, block, &func.dfg); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 8e2b2fb7a29..802d442885f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -6,14 +6,15 @@ use acvm::{ use crate::brillig::brillig_ir::{ brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, + registers::RegisterAllocator, BrilligContext, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions /// Extracting arguments and results from the SSA function call /// And making any necessary type conversions to adapt noir's blackbox calls to brillig's -pub(crate) fn convert_black_box_call( - brillig_context: &mut BrilligContext, +pub(crate) fn convert_black_box_call( + brillig_context: &mut BrilligContext, bb_func: &BlackBoxFunc, function_arguments: &[BrilligVariable], function_results: &[BrilligVariable], @@ -397,8 +398,8 @@ pub(crate) fn convert_black_box_call( } } -fn convert_array_or_vector( - brillig_context: &mut BrilligContext, +fn convert_array_or_vector( + brillig_context: &mut BrilligContext, array_or_vector: &BrilligVariable, bb_func: &BlackBoxFunc, ) -> BrilligVector { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 76ab613ed1f..74fdbfcdce6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,8 +1,10 @@ +use crate::brillig::brillig_ir::artifact::Label; use crate::brillig::brillig_ir::brillig_variable::{ type_to_heap_value_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }; +use crate::brillig::brillig_ir::registers::Stack; use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::instruction::ConstrainError; @@ -34,7 +36,7 @@ pub(crate) struct BrilligBlock<'block> { /// The basic block that is being converted pub(crate) block_id: BasicBlockId, /// Context for creating brillig opcodes - pub(crate) brillig_context: &'block mut BrilligContext, + pub(crate) brillig_context: &'block mut BrilligContext, /// Tracks the available variable during the codegen of the block pub(crate) variables: BlockVariables, /// For each instruction, the set of values that are not used anymore after it. @@ -45,7 +47,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA Basic block into a sequence of Brillig opcodes pub(crate) fn compile( function_context: &'block mut FunctionContext, - brillig_context: &'block mut BrilligContext, + brillig_context: &'block mut BrilligContext, block_id: BasicBlockId, dfg: &DataFlowGraph, ) { @@ -93,7 +95,7 @@ impl<'block> BrilligBlock<'block> { /// This uses the current functions's function ID and the block ID /// Making the assumption that the block ID passed in belongs to this /// function. - fn create_block_label_for_current_function(&self, block_id: BasicBlockId) -> String { + fn create_block_label_for_current_function(&self, block_id: BasicBlockId) -> Label { Self::create_block_label(self.function_context.function_id, block_id) } /// Creates a unique label for a block using the function Id and the block ID. @@ -102,8 +104,8 @@ impl<'block> BrilligBlock<'block> { /// for us to create a unique label across functions and blocks. /// /// This is so that during linking there are no duplicates or labels being overwritten. - fn create_block_label(function_id: FunctionId, block_id: BasicBlockId) -> String { - format!("{function_id}-{block_id}") + fn create_block_label(function_id: FunctionId, block_id: BasicBlockId) -> Label { + Label::block(function_id, block_id) } /// Converts an SSA terminator instruction into the necessary opcodes. @@ -468,13 +470,21 @@ impl<'block> BrilligBlock<'block> { result_ids[1], dfg, ); - let source_size_as_register = - self.convert_ssa_array_set(source_variable, destination_variable, None); - // we need to explicitly set the destination_len_variable self.brillig_context - .mov_instruction(destination_len_variable.address, source_size_as_register); - self.brillig_context.deallocate_register(source_size_as_register); + .call_array_copy_procedure(source_variable, destination_variable); + + let BrilligVariable::BrilligArray(BrilligArray { size: source_size, .. }) = + source_variable + else { + unreachable!("ICE: AsSlice on non-array") + }; + + // we need to explicitly set the destination_len_variable + self.brillig_context.usize_const_instruction( + destination_len_variable.address, + source_size.into(), + ); } Value::Intrinsic( Intrinsic::SlicePushBack @@ -660,12 +670,12 @@ impl<'block> BrilligBlock<'block> { dfg, ); self.validate_array_index(source_variable, index_register); - let source_size_as_register = self.convert_ssa_array_set( + self.convert_ssa_array_set( source_variable, destination_variable, - Some((index_register.address, value_variable)), + index_register, + value_variable, ); - self.brillig_context.deallocate_register(source_size_as_register); } Instruction::RangeCheck { value, max_bit_size, assert_message } => { let value = self.convert_ssa_single_addr_value(*value, dfg); @@ -761,9 +771,6 @@ impl<'block> BrilligBlock<'block> { .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); - // Create label for the function that will be called - let label_of_function_to_call = FunctionContext::function_id_to_function_label(func_id); - let variables_to_save = self.variables.get_available_variables(self.function_context); let saved_registers = self @@ -774,7 +781,7 @@ impl<'block> BrilligBlock<'block> { self.variables.dump_constants(); // Call instruction, which will interpret above registers 0..num args - self.brillig_context.add_external_call_instruction(label_of_function_to_call); + self.brillig_context.add_external_call_instruction(func_id); // Important: resolve after pre_call_save_registers_prep_args // This ensures we don't save the results to registers unnecessarily. @@ -870,88 +877,23 @@ impl<'block> BrilligBlock<'block> { &mut self, source_variable: BrilligVariable, destination_variable: BrilligVariable, - opt_index_and_value: Option<(MemoryAddress, BrilligVariable)>, - ) -> MemoryAddress { + index_register: SingleAddrVariable, + value_variable: BrilligVariable, + ) { + assert!(index_register.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); let destination_pointer = match destination_variable { BrilligVariable::BrilligArray(BrilligArray { pointer, .. }) => pointer, BrilligVariable::BrilligVector(BrilligVector { pointer, .. }) => pointer, _ => unreachable!("ICE: array_set SSA returns non-array"), }; - let reference_count = match source_variable { - BrilligVariable::BrilligArray(BrilligArray { rc, .. }) - | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, - _ => unreachable!("ICE: array_set SSA on non-array"), - }; - - let (source_pointer, source_size_as_register) = match source_variable { - BrilligVariable::BrilligArray(BrilligArray { size, pointer, rc: _ }) => { - let source_size_register = self.brillig_context.allocate_register(); - self.brillig_context.usize_const_instruction(source_size_register, size.into()); - (pointer, source_size_register) - } - BrilligVariable::BrilligVector(BrilligVector { size, pointer, rc: _ }) => { - let source_size_register = self.brillig_context.allocate_register(); - self.brillig_context.mov_instruction(source_size_register, size); - (pointer, source_size_register) - } - _ => unreachable!("ICE: array_set SSA on non-array"), - }; - - // Here we want to compare the reference count against 1. - let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); - let condition = self.brillig_context.allocate_register(); - self.brillig_context.memory_op_instruction( - reference_count, - one.address, - condition, - BrilligBinaryOp::Equals, + self.brillig_context.call_array_copy_procedure(source_variable, destination_variable); + // Then set the value in the newly created array + self.brillig_context.codegen_store_variable_in_array( + destination_pointer, + index_register, + value_variable, ); - self.brillig_context.codegen_branch(condition, |ctx, cond| { - if cond { - // Reference count is 1, we can mutate the array directly - ctx.mov_instruction(destination_pointer, source_pointer); - } else { - // First issue a array copy to the destination - ctx.codegen_allocate_array(destination_pointer, source_size_as_register); - - ctx.codegen_copy_array( - source_pointer, - destination_pointer, - SingleAddrVariable::new( - source_size_as_register, - BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - ), - ); - } - }); - - match destination_variable { - BrilligVariable::BrilligArray(BrilligArray { rc: target_rc, .. }) => { - self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); - } - BrilligVariable::BrilligVector(BrilligVector { - size: target_size, - rc: target_rc, - .. - }) => { - self.brillig_context.mov_instruction(target_size, source_size_as_register); - self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); - } - _ => unreachable!("ICE: array_set SSA on non-array"), - } - - if let Some((index_register, value_variable)) = opt_index_and_value { - // Then set the value in the newly created array - self.brillig_context.codegen_store_variable_in_array( - destination_pointer, - SingleAddrVariable::new_usize(index_register), - value_variable, - ); - } - - self.brillig_context.deallocate_register(condition); - source_size_as_register } /// Convert the SSA slice operations to brillig slice operations @@ -1733,11 +1675,9 @@ impl<'block> BrilligBlock<'block> { let subitem_pointer = SingleAddrVariable::new_usize(self.brillig_context.allocate_register()); - let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); - // Initializes a single subitem let initializer_fn = - |ctx: &mut BrilligContext<_>, subitem_start_pointer: SingleAddrVariable| { + |ctx: &mut BrilligContext<_, _>, subitem_start_pointer: SingleAddrVariable| { ctx.mov_instruction(subitem_pointer.address, subitem_start_pointer.address); for (subitem_index, subitem) in subitem_to_repeat_variables.into_iter().enumerate() @@ -1746,7 +1686,7 @@ impl<'block> BrilligBlock<'block> { if subitem_index != item_types.len() - 1 { ctx.memory_op_instruction( subitem_pointer.address, - one.address, + ReservedRegisters::usize_one(), subitem_pointer.address, BrilligBinaryOp::Add, ); @@ -1764,13 +1704,13 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_single_addr(step_variable); self.brillig_context.deallocate_single_addr(subitem_pointer); - self.brillig_context.deallocate_single_addr(one); } else { let subitem = subitem_to_repeat_variables.into_iter().next().unwrap(); - let initializer_fn = |ctx: &mut BrilligContext<_>, item_pointer: SingleAddrVariable| { - ctx.codegen_store_variable_in_pointer(item_pointer.address, subitem); - }; + let initializer_fn = + |ctx: &mut BrilligContext<_, _>, item_pointer: SingleAddrVariable| { + ctx.codegen_store_variable_in_pointer(item_pointer.address, subitem); + }; // for (let item_pointer = pointer; item_pointer < pointer + data_length; item_pointer += 1) { initializer_fn(iterator) } self.brillig_context.codegen_for_loop( @@ -1791,7 +1731,6 @@ impl<'block> BrilligBlock<'block> { ) { // Allocate a register for the iterator let write_pointer_register = self.brillig_context.allocate_register(); - let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); self.brillig_context.mov_instruction(write_pointer_register, pointer); @@ -1804,7 +1743,7 @@ impl<'block> BrilligBlock<'block> { // Increment the write_pointer_register self.brillig_context.memory_op_instruction( write_pointer_register, - one.address, + ReservedRegisters::usize_one(), write_pointer_register, BrilligBinaryOp::Add, ); @@ -1812,7 +1751,6 @@ impl<'block> BrilligBlock<'block> { } self.brillig_context.deallocate_register(write_pointer_register); - self.brillig_context.deallocate_single_addr(one); } /// Converts an SSA `ValueId` into a `MemoryAddress`. Initializes if necessary. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index ffbca256d28..63b2073c654 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -7,6 +7,7 @@ use crate::{ get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }, + registers::{RegisterAllocator, Stack}, BrilligContext, }, ssa::ir::{ @@ -51,7 +52,7 @@ impl BlockVariables { pub(crate) fn define_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { @@ -71,7 +72,7 @@ impl BlockVariables { pub(crate) fn define_single_addr_variable( &mut self, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, ) -> SingleAddrVariable { @@ -84,7 +85,7 @@ impl BlockVariables { &mut self, value_id: &ValueId, function_context: &mut FunctionContext, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, ) { assert!(self.available_variables.remove(value_id), "ICE: Variable is not available"); let variable = function_context @@ -123,7 +124,7 @@ impl BlockVariables { /// We keep constants block-local. pub(crate) fn allocate_constant( &mut self, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, value_id: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { @@ -155,9 +156,9 @@ pub(crate) fn compute_array_length(item_typ: &CompositeType, elem_count: usize) } /// For a given value_id, allocates the necessary registers to hold it. -pub(crate) fn allocate_value( +pub(crate) fn allocate_value( value_id: ValueId, - brillig_context: &mut BrilligContext, + brillig_context: &mut BrilligContext, dfg: &DataFlowGraph, ) -> BrilligVariable { let typ = dfg.type_of_value(value_id); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index f0752c80c46..c1abad17a8f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -2,7 +2,7 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ - artifact::{BrilligParameter, Label}, + artifact::BrilligParameter, brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, }, ssa::ir::{ @@ -44,11 +44,6 @@ impl FunctionContext { } } - /// Creates a function label from a given SSA function id. - pub(crate) fn function_id_to_function_label(function_id: FunctionId) -> Label { - function_id.to_string() - } - pub(crate) fn ssa_type_to_parameter(typ: &Type) -> BrilligParameter { match typ { Type::Numeric(_) | Type::Reference(_) => { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 55679432b1f..9dbf0ee9bb6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -24,7 +24,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy the source vector into the target vector - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(source_vector.size), @@ -74,7 +74,7 @@ impl<'block> BrilligBlock<'block> { ); // Now we copy the source vector into the target vector starting at index variables_to_insert.len() - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_vector.pointer, destination_copy_pointer, SingleAddrVariable::new_usize(source_vector.size), @@ -121,7 +121,7 @@ impl<'block> BrilligBlock<'block> { ); // Now we copy the source vector starting at index removed_items.len() into the target vector - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_copy_pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), @@ -154,7 +154,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy all elements except the last items into the target vector - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), @@ -192,11 +192,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.codegen_copy_array( - source_vector.pointer, - target_vector.pointer, - index, - ); + self.brillig_context.codegen_mem_copy(source_vector.pointer, target_vector.pointer, index); // Compute the source pointer just at the index let source_pointer_at_index = self.brillig_context.allocate_register(); @@ -231,7 +227,7 @@ impl<'block> BrilligBlock<'block> { ); // Copy the elements to the right of the index - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_pointer_at_index, target_pointer_after_index, SingleAddrVariable::new_usize(item_count), @@ -279,11 +275,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.codegen_copy_array( - source_vector.pointer, - target_vector.pointer, - index, - ); + self.brillig_context.codegen_mem_copy(source_vector.pointer, target_vector.pointer, index); // Compute the source pointer after the removed items let source_pointer_after_index = self.brillig_context.allocate_register(); @@ -323,7 +315,7 @@ impl<'block> BrilligBlock<'block> { ); // Copy the elements to the right of the index - self.brillig_context.codegen_copy_array( + self.brillig_context.codegen_mem_copy( source_pointer_after_index, target_pointer_at_index, SingleAddrVariable::new_usize(item_count), @@ -371,10 +363,11 @@ mod tests { use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; use crate::brillig::brillig_gen::brillig_fn::FunctionContext; - use crate::brillig::brillig_ir::artifact::BrilligParameter; + use crate::brillig::brillig_ir::artifact::{BrilligParameter, Label}; use crate::brillig::brillig_ir::brillig_variable::{ BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }; + use crate::brillig::brillig_ir::registers::Stack; use crate::brillig::brillig_ir::tests::{ create_and_run_vm, create_context, create_entry_point_bytecode, }; @@ -384,12 +377,14 @@ mod tests { use crate::ssa::ir::map::Id; use crate::ssa::ssa_gen::Ssa; - fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { + fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { let mut builder = FunctionBuilder::new("main".to_string(), Id::test_new(0)); builder.set_runtime(RuntimeType::Brillig); let ssa = builder.finish(); - let brillig_context = create_context(); + let mut brillig_context = create_context(ssa.main_id); + brillig_context.enter_context(Label::block(ssa.main_id, Id::test_new(0))); + brillig_context.disable_procedures(); let function_context = FunctionContext::new(ssa.main()); (ssa, function_context, brillig_context) @@ -397,7 +392,7 @@ mod tests { fn create_brillig_block<'a>( function_context: &'a mut FunctionContext, - brillig_context: &'a mut BrilligContext, + brillig_context: &'a mut BrilligContext, ) -> BrilligBlock<'a> { let variables = BlockVariables::default(); BrilligBlock { 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 21f8722c116..05117b96c5d 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 @@ -12,6 +12,7 @@ pub(crate) mod artifact; pub(crate) mod brillig_variable; pub(crate) mod debug_show; +pub(crate) mod procedures; pub(crate) mod registers; mod codegen_binary; @@ -23,11 +24,11 @@ mod codegen_stack; mod entry_point; mod instructions; +use artifact::Label; pub(crate) use instructions::BrilligBinaryOp; +use registers::{RegisterAllocator, ScratchSpace}; -use self::{ - artifact::BrilligArtifact, debug_show::DebugToString, registers::BrilligRegistersContext, -}; +use self::{artifact::BrilligArtifact, debug_show::DebugToString, registers::Stack}; use crate::ssa::ir::dfg::CallStack; use acvm::{ acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, @@ -46,6 +47,8 @@ pub(crate) enum ReservedRegisters { FreeMemoryPointer = 0, /// This register stores the previous stack pointer. The registers of the caller are stored here. PreviousStackPointer = 1, + /// This register stores a 1_usize constant. + UsizeOne = 2, } impl ReservedRegisters { @@ -53,7 +56,7 @@ impl ReservedRegisters { /// /// This is used to offset the general registers /// which should not overwrite the special register - const NUM_RESERVED_REGISTERS: usize = 2; + const NUM_RESERVED_REGISTERS: usize = 3; /// Returns the length of the reserved registers pub(crate) fn len() -> usize { @@ -70,42 +73,67 @@ impl ReservedRegisters { MemoryAddress::from(ReservedRegisters::PreviousStackPointer as usize) } - /// Returns a user defined (non-reserved) register index. - fn user_register_index(index: usize) -> MemoryAddress { - MemoryAddress::from(index + ReservedRegisters::len()) + /// Returns the usize one register. This will be used to perform arithmetic operations. + pub(crate) fn usize_one() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::UsizeOne as usize) } } /// Brillig context object that is used while constructing the /// Brillig bytecode. -pub(crate) struct BrilligContext { +pub(crate) struct BrilligContext { obj: BrilligArtifact, /// Tracks register allocations - registers: BrilligRegistersContext, + registers: Registers, /// Context label, must be unique with respect to the function /// being linked. - context_label: String, + context_label: Label, /// Section label, used to separate sections of code - section_label: usize, + current_section: usize, /// Stores the next available section next_section: usize, /// IR printer debug_show: DebugShow, + /// Whether this context can call procedures or not. + /// This is used to prevent a procedure from calling another procedure. + can_call_procedures: bool, } -impl BrilligContext { - /// Initial context state - pub(crate) fn new(enable_debug_trace: bool) -> BrilligContext { +/// Regular brillig context to codegen user defined functions +impl BrilligContext { + pub(crate) fn new(enable_debug_trace: bool) -> BrilligContext { BrilligContext { obj: BrilligArtifact::default(), - registers: BrilligRegistersContext::new(), - context_label: String::default(), - section_label: 0, + registers: Stack::new(), + context_label: Label::entrypoint(), + current_section: 0, next_section: 1, debug_show: DebugShow::new(enable_debug_trace), + can_call_procedures: true, } } + /// Allows disabling procedures so tests don't need a linking pass + pub(crate) fn disable_procedures(&mut self) { + self.can_call_procedures = false; + } +} + +/// Special brillig context to codegen compiler intrinsic shared procedures +impl BrilligContext { + pub(crate) fn new_for_procedure(enable_debug_trace: bool) -> BrilligContext { + BrilligContext { + obj: BrilligArtifact::default(), + registers: ScratchSpace::new(), + context_label: Label::entrypoint(), + current_section: 0, + next_section: 1, + debug_show: DebugShow::new(enable_debug_trace), + can_call_procedures: false, + } + } +} +impl BrilligContext { /// Adds a brillig instruction to the brillig byte code fn push_opcode(&mut self, opcode: BrilligOpcode) { self.obj.push_opcode(opcode); @@ -135,8 +163,10 @@ pub(crate) mod tests { use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; + use crate::ssa::ir::function::FunctionId; - use super::artifact::{BrilligParameter, GeneratedBrillig}; + use super::artifact::{BrilligParameter, GeneratedBrillig, Label}; + use super::registers::Stack; use super::{BrilligOpcode, ReservedRegisters}; pub(crate) struct DummyBlackBoxSolver; @@ -195,20 +225,20 @@ pub(crate) mod tests { } } - pub(crate) fn create_context() -> BrilligContext { + pub(crate) fn create_context(id: FunctionId) -> BrilligContext { let mut context = BrilligContext::new(true); - context.enter_context("test"); + context.enter_context(Label::function(id)); context } pub(crate) fn create_entry_point_bytecode( - context: BrilligContext, + context: BrilligContext, arguments: Vec, returns: Vec, ) -> GeneratedBrillig { let artifact = context.artifact(); let mut entry_point_artifact = - BrilligContext::new_entry_point_artifact(arguments, returns, "test".to_string()); + BrilligContext::new_entry_point_artifact(arguments, returns, FunctionId::test_new(0)); entry_point_artifact.link_with(&artifact); entry_point_artifact.finish() } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 2d0bdb5955c..ca37ea225a4 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -1,7 +1,8 @@ use acvm::acir::brillig::Opcode as BrilligOpcode; use std::collections::{BTreeMap, HashMap}; -use crate::ssa::ir::dfg::CallStack; +use crate::brillig::brillig_ir::procedures::ProcedureId; +use crate::ssa::ir::{basic_block::BasicBlockId, dfg::CallStack, function::FunctionId}; /// Represents a parameter or a return value of an entry point function. #[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] @@ -44,7 +45,7 @@ pub(crate) struct BrilligArtifact { /// which are defined in other bytecode, that this bytecode has called. /// TODO: perhaps we should combine this with the `unresolved_jumps` field /// TODO: and have an enum which indicates whether the jump is internal or external - unresolved_external_call_labels: Vec<(JumpInstructionPosition, UnresolvedJumpLocation)>, + unresolved_external_call_labels: Vec<(JumpInstructionPosition, Label)>, /// Maps the opcodes that are associated with a callstack to it. locations: BTreeMap, /// The current call stack. All opcodes that are pushed will be associated with this call stack. @@ -53,11 +54,74 @@ pub(crate) struct BrilligArtifact { /// A pointer to a location in the opcode. pub(crate) type OpcodeLocation = usize; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub(crate) enum LabelType { + /// Labels for the entry point bytecode + Entrypoint, + /// Labels for user defined functions + Function(FunctionId, Option), + /// Labels for intrinsic procedures + Procedure(ProcedureId), +} + +impl std::fmt::Display for LabelType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + LabelType::Function(function_id, block_id) => { + if let Some(block_id) = block_id { + write!(f, "Function({:?}, {:?})", function_id, block_id) + } else { + write!(f, "Function({:?})", function_id) + } + } + LabelType::Entrypoint => write!(f, "Entrypoint"), + LabelType::Procedure(procedure_id) => write!(f, "Procedure({:?})", procedure_id), + } + } +} + /// An identifier for a location in the code. /// /// It is assumed that an entity will keep a map /// of labels to Opcode locations. -pub(crate) type Label = String; +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub(crate) struct Label { + pub(crate) label_type: LabelType, + pub(crate) section: Option, +} + +impl Label { + pub(crate) fn add_section(&self, section: usize) -> Self { + Label { label_type: self.label_type, section: Some(section) } + } + + pub(crate) fn function(func_id: FunctionId) -> Self { + Label { label_type: LabelType::Function(func_id, None), section: None } + } + + pub(crate) fn block(func_id: FunctionId, block_id: BasicBlockId) -> Self { + Label { label_type: LabelType::Function(func_id, Some(block_id)), section: None } + } + + pub(crate) fn entrypoint() -> Self { + Label { label_type: LabelType::Entrypoint, section: None } + } + + pub(crate) fn procedure(procedure_id: ProcedureId) -> Self { + Label { label_type: LabelType::Procedure(procedure_id), section: None } + } +} + +impl std::fmt::Display for Label { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(section) = self.section { + write!(f, "{:?} - {}", self.label_type, section) + } else { + write!(f, "{:?}", self.label_type) + } + } +} /// Pointer to a unresolved Jump instruction in /// the bytecode. pub(crate) type JumpInstructionPosition = OpcodeLocation; @@ -86,7 +150,7 @@ impl BrilligArtifact { /// Gets the first unresolved function call of this artifact. pub(crate) fn first_unresolved_function_call(&self) -> Option