diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs index 0503ba78685..f7e15ed9919 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen.rs @@ -1,11 +1,10 @@ //! The code generation logic for converting [crate::ssa] objects into their respective [Brillig] artifacts. -pub(crate) mod brillig_black_box; pub(crate) mod brillig_block; pub(crate) mod brillig_block_variables; +mod brillig_call; pub(crate) mod brillig_fn; pub(crate) mod brillig_globals; mod brillig_instructions; -pub(crate) mod brillig_slice_ops; pub(crate) mod constant_allocation; mod variable_liveness; diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/brillig_black_box.rs similarity index 100% rename from compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs rename to compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/brillig_black_box.rs diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/brillig_slice_ops.rs similarity index 99% rename from compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs rename to compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/brillig_slice_ops.rs index a8747812504..e01784d4fb0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/brillig_slice_ops.rs @@ -7,7 +7,7 @@ use crate::brillig::brillig_ir::{ registers::RegisterAllocator, }; -use super::brillig_block::BrilligBlock; +use super::super::brillig_block::BrilligBlock; impl BrilligBlock<'_, Registers> { fn write_variables(&mut self, write_pointer: MemoryAddress, variables: &[BrilligVariable]) { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/code_gen_call.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/code_gen_call.rs new file mode 100644 index 00000000000..81180400b03 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/code_gen_call.rs @@ -0,0 +1,421 @@ +use acvm::acir::BlackBoxFunc; +use acvm::acir::brillig::ValueOrArray; +use acvm::{AcirField, FieldElement}; +use iter_extended::vecmap; + +use crate::brillig::BrilligBlock; +use crate::brillig::brillig_ir::{BrilligBinaryOp, registers::RegisterAllocator}; +use crate::ssa::ir::instruction::{Endian, Hint, InstructionId, Intrinsic}; +use crate::ssa::ir::{ + dfg::DataFlowGraph, + types::{NumericType, Type}, + value::{Value, ValueId}, +}; + +use super::brillig_black_box::convert_black_box_call; +use crate::brillig::brillig_ir::brillig_variable::type_to_heap_value_type; + +impl BrilligBlock<'_, Registers> { + /// Converts a foreign function call into Brillig bytecode. + /// + /// Foreign functions are external host functions that interact with the Brillig VM. + /// This method handles the conversion of inputs/outputs and manages memory allocation. + fn convert_ssa_foreign_call( + &mut self, + func_name: &str, + arguments: &[ValueId], + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { + let result_ids = dfg.instruction_results(instruction_id); + + let input_values = vecmap(arguments, |value_id| { + let variable = self.convert_ssa_value(*value_id, dfg); + self.brillig_context.variable_to_value_or_array(variable) + }); + let input_value_types = vecmap(arguments, |value_id| { + let value_type = dfg.type_of_value(*value_id); + type_to_heap_value_type(&value_type) + }); + let output_variables = + vecmap(result_ids, |value_id| self.allocate_external_call_result(*value_id, dfg)); + let output_values = vecmap(&output_variables, |variable| { + self.brillig_context.variable_to_value_or_array(*variable) + }); + let output_value_types = vecmap(result_ids, |value_id| { + let value_type = dfg.type_of_value(*value_id); + type_to_heap_value_type(&value_type) + }); + self.brillig_context.foreign_call_instruction( + func_name.to_owned(), + &input_values, + &input_value_types, + &output_values, + &output_value_types, + ); + + // Deallocate the temporary heap arrays and vectors of the inputs + for input_value in input_values { + match input_value { + ValueOrArray::HeapArray(array) => { + self.brillig_context.deallocate_heap_array(array); + } + ValueOrArray::HeapVector(vector) => { + self.brillig_context.deallocate_heap_vector(vector); + } + _ => {} + } + } + + // Deallocate the temporary heap arrays and vectors of the outputs + for (i, (output_register, output_variable)) in + output_values.iter().zip(output_variables).enumerate() + { + match output_register { + // Returned vectors need to emit some bytecode to format the result as a BrilligVector + ValueOrArray::HeapVector(heap_vector) => { + self.brillig_context.initialize_externally_returned_vector( + output_variable.extract_vector(), + *heap_vector, + ); + // Update the dynamic slice length maintained in SSA + if let ValueOrArray::MemoryAddress(len_index) = output_values[i - 1] { + let element_size = dfg[result_ids[i]].get_type().element_size(); + self.brillig_context.mov_instruction(len_index, heap_vector.size); + self.brillig_context.codegen_usize_op_in_place( + len_index, + BrilligBinaryOp::UnsignedDiv, + element_size, + ); + } else { + unreachable!( + "ICE: a vector must be preceded by a register containing its length" + ); + } + self.brillig_context.deallocate_heap_vector(*heap_vector); + } + ValueOrArray::HeapArray(array) => { + self.brillig_context.deallocate_heap_array(*array); + } + ValueOrArray::MemoryAddress(_) => {} + } + } + } + + /// Converts the ArrayLen intrinsic to Brillig bytecode. + /// + /// For slices (represented as tuples of (length, contents)), this directly moves + /// the length field. For arrays, it calculates the length based on the array size. + fn convert_ssa_array_len_intrinsic( + &mut self, + arguments: &[ValueId], + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { + let [result_value] = dfg.instruction_result(instruction_id); + let result_variable = self.variables.define_single_addr_variable( + self.function_context, + self.brillig_context, + result_value, + dfg, + ); + let param_id = arguments[0]; + // Slices are represented as a tuple in the form: (length, slice contents). + // Thus, we can expect the first argument to a field in the case of a slice + // or an array in the case of an array. + if let Type::Numeric(_) = dfg.type_of_value(param_id) { + let len_variable = self.convert_ssa_value(arguments[0], dfg); + let length = len_variable.extract_single_addr(); + self.brillig_context.mov_instruction(result_variable.address, length.address); + } else { + self.convert_ssa_array_len(arguments[0], result_variable.address, dfg); + } + } + + /// Converts a field less than comparison intrinsic to Brillig bytecode. + /// + /// Compares two field elements and returns a boolean result. + fn convert_ssa_field_less_than( + &mut self, + arguments: &[ValueId], + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { + let lhs = self.convert_ssa_single_addr_value(arguments[0], dfg); + assert!(lhs.bit_size == FieldElement::max_num_bits()); + let rhs = self.convert_ssa_single_addr_value(arguments[1], dfg); + assert!(rhs.bit_size == FieldElement::max_num_bits()); + + let [result] = dfg.instruction_result(instruction_id); + let destination = self + .variables + .define_variable(self.function_context, self.brillig_context, result, dfg) + .extract_single_addr(); + assert!(destination.bit_size == 1); + + self.brillig_context.binary_instruction(lhs, rhs, destination, BrilligBinaryOp::LessThan); + } + + /// Converts a black box function call into Brillig bytecode. + /// + /// Black box functions are native cryptographic operations or other optimized primitives. + /// Some black box functions (ECDSA, MultiScalarMul, EmbeddedCurveAdd) have a predicate + /// argument in SSA that is removed for Brillig as CFG flattening is not needed. + fn convert_ssa_black_box_call( + &mut self, + bb_func: &BlackBoxFunc, + arguments: &[ValueId], + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { + assert!( + !arguments.iter().any(|arg| dfg.type_of_value(*arg).contains_slice_element()), + "Blackbox functions should not be called with arguments of slice type" + ); + + let mut arguments = arguments.to_vec(); + if matches!( + bb_func, + BlackBoxFunc::EcdsaSecp256k1 + | BlackBoxFunc::EcdsaSecp256r1 + | BlackBoxFunc::MultiScalarMul + | BlackBoxFunc::EmbeddedCurveAdd + ) { + // Some black box functions have a predicate argument in SSA which we don't want to + // use in the brillig VM. This is as we do not need to flatten the CFG in brillig + // so we expect the predicate to always be true. + let predicate = arguments + .pop() + .expect("ICE: ECDSA black box function must have a predicate argument"); + assert_eq!( + dfg.get_numeric_constant_with_type(predicate), + Some((FieldElement::one(), NumericType::bool())), + "ICE: ECDSA black box function must have a predicate argument with value 1" + ); + } + + let function_arguments = vecmap(arguments, |arg| self.convert_ssa_value(arg, dfg)); + let function_results = dfg.instruction_results(instruction_id); + let function_results = + vecmap(function_results, |result| self.allocate_external_call_result(*result, dfg)); + convert_black_box_call( + self.brillig_context, + bb_func, + &function_arguments, + &function_results, + ); + } + + /// Converts an array to a slice by copying the array contents into a vector. + /// + /// This intrinsic converts a fixed-size array into a dynamically-sized slice (vector). + fn convert_ssa_as_slice( + &mut self, + arguments: &[ValueId], + instruction_id: InstructionId, + dfg: &DataFlowGraph, + ) { + let source_variable = self.convert_ssa_value(arguments[0], dfg); + let result_ids = dfg.instruction_results(instruction_id); + let destination_len_variable = self.variables.define_single_addr_variable( + self.function_context, + self.brillig_context, + result_ids[0], + dfg, + ); + let destination_variable = self.variables.define_variable( + self.function_context, + self.brillig_context, + result_ids[1], + dfg, + ); + let destination_vector = destination_variable.extract_vector(); + let source_array = source_variable.extract_array(); + let element_size = dfg.type_of_value(arguments[0]).element_size(); + + let source_size_register = + self.brillig_context.make_usize_constant_instruction(source_array.size.into()); + + // we need to explicitly set the destination_len_variable + self.brillig_context.codegen_usize_op( + source_size_register.address, + destination_len_variable.address, + BrilligBinaryOp::UnsignedDiv, + element_size, + ); + + self.brillig_context.codegen_initialize_vector( + destination_vector, + source_size_register, + None, + ); + + // Items + let vector_items_pointer = + self.brillig_context.codegen_make_vector_items_pointer(destination_vector); + let array_items_pointer = + self.brillig_context.codegen_make_array_items_pointer(source_array); + + self.brillig_context.codegen_mem_copy( + array_items_pointer, + vector_items_pointer, + source_size_register, + ); + + self.brillig_context.deallocate_single_addr(source_size_register); + self.brillig_context.deallocate_register(vector_items_pointer); + self.brillig_context.deallocate_register(array_items_pointer); + } + + pub(crate) fn call_gen( + &mut self, + instruction_id: InstructionId, + func: ValueId, + arguments: &[ValueId], + dfg: &DataFlowGraph, + ) { + match &dfg[func] { + Value::ForeignFunction(func_name) => { + self.convert_ssa_foreign_call(func_name, arguments, instruction_id, dfg); + } + Value::Function(func_id) => { + let result_ids = dfg.instruction_results(instruction_id); + self.convert_ssa_function_call(*func_id, arguments, dfg, result_ids); + } + Value::Intrinsic(intrinsic) => { + // This match could be combined with the above but without it rust analyzer + // can't automatically insert any missing cases + match intrinsic { + Intrinsic::ArrayLen => { + self.convert_ssa_array_len_intrinsic(arguments, instruction_id, dfg); + } + Intrinsic::AsSlice => { + self.convert_ssa_as_slice(arguments, instruction_id, dfg); + } + Intrinsic::SlicePushBack + | Intrinsic::SlicePopBack + | Intrinsic::SlicePushFront + | Intrinsic::SlicePopFront + | Intrinsic::SliceInsert + | Intrinsic::SliceRemove => { + self.convert_ssa_slice_intrinsic_call( + dfg, + &dfg[func], + instruction_id, + arguments, + ); + } + Intrinsic::ToBits(endianness) => { + let [result] = dfg.instruction_result(instruction_id); + + let source = self.convert_ssa_single_addr_value(arguments[0], dfg); + + let target_array = self + .variables + .define_variable( + self.function_context, + self.brillig_context, + result, + dfg, + ) + .extract_array(); + + let two = + self.brillig_context.make_usize_constant_instruction(2_usize.into()); + + self.brillig_context.codegen_to_radix( + source, + target_array, + two, + matches!(endianness, Endian::Little), + true, + ); + + self.brillig_context.deallocate_single_addr(two); + } + + Intrinsic::ToRadix(endianness) => { + let [result] = dfg.instruction_result(instruction_id); + + let source = self.convert_ssa_single_addr_value(arguments[0], dfg); + let radix = self.convert_ssa_single_addr_value(arguments[1], dfg); + + let target_array = self + .variables + .define_variable( + self.function_context, + self.brillig_context, + result, + dfg, + ) + .extract_array(); + + self.brillig_context.codegen_to_radix( + source, + target_array, + radix, + matches!(endianness, Endian::Little), + false, + ); + } + Intrinsic::Hint(Hint::BlackBox) => { + let result_ids = dfg.instruction_results(instruction_id); + self.convert_ssa_identity_call(arguments, dfg, result_ids); + } + Intrinsic::BlackBox(bb_func) => { + self.convert_ssa_black_box_call(bb_func, arguments, instruction_id, dfg); + } + // `Intrinsic::AsWitness` is used to provide hints to acir-gen on optimal expression splitting. + // It is then useless in the brillig runtime and so we can ignore it + Intrinsic::AsWitness => (), + Intrinsic::FieldLessThan => { + self.convert_ssa_field_less_than(arguments, instruction_id, dfg); + } + Intrinsic::ArrayRefCount => { + let array = self.convert_ssa_value(arguments[0], dfg); + let [result] = dfg.instruction_result(instruction_id); + + let destination = self.variables.define_variable( + self.function_context, + self.brillig_context, + result, + dfg, + ); + let destination = destination.extract_register(); + let array = array.extract_register(); + self.brillig_context.load_instruction(destination, array); + } + Intrinsic::SliceRefCount => { + let array = self.convert_ssa_value(arguments[1], dfg); + let [result] = dfg.instruction_result(instruction_id); + + let destination = self.variables.define_variable( + self.function_context, + self.brillig_context, + result, + dfg, + ); + let destination = destination.extract_register(); + let array = array.extract_register(); + self.brillig_context.load_instruction(destination, array); + } + Intrinsic::IsUnconstrained + | Intrinsic::DerivePedersenGenerators + | Intrinsic::ApplyRangeConstraint + | Intrinsic::StrAsBytes + | Intrinsic::AssertConstant + | Intrinsic::StaticAssert + | Intrinsic::ArrayAsStrUnchecked => { + unreachable!("unsupported function call type {:?}", dfg[func]) + } + } + } + Value::Instruction { .. } + | Value::Param { .. } + | Value::NumericConstant { .. } + | Value::Global(_) => { + unreachable!("unsupported function call type {:?}", dfg[func]) + } + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/brillig_calls.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/mod.rs similarity index 52% rename from compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/brillig_calls.rs rename to compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/mod.rs index 7afdfbcc686..aa475c71c49 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/brillig_calls.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_call/mod.rs @@ -1,21 +1,22 @@ -use acvm::acir::BlackBoxFunc; -use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; -use acvm::{AcirField, FieldElement}; +pub(super) mod brillig_black_box; +pub(super) mod brillig_slice_ops; +pub(super) mod code_gen_call; + +use acvm::acir::brillig::MemoryAddress; use iter_extended::vecmap; use crate::brillig::BrilligBlock; use crate::brillig::brillig_ir::{BrilligBinaryOp, registers::RegisterAllocator}; use crate::ssa::ir::function::FunctionId; -use crate::ssa::ir::instruction::{Endian, Hint, InstructionId, Intrinsic}; +use crate::ssa::ir::instruction::{InstructionId, Intrinsic}; use crate::ssa::ir::{ dfg::DataFlowGraph, - types::{NumericType, Type}, + types::Type, value::{Value, ValueId}, }; -use super::super::brillig_black_box::convert_black_box_call; use crate::brillig::brillig_ir::brillig_variable::{ - BrilligArray, BrilligVariable, SingleAddrVariable, type_to_heap_value_type, + BrilligArray, BrilligVariable, SingleAddrVariable, }; impl BrilligBlock<'_, Registers> { @@ -491,359 +492,4 @@ impl BrilligBlock<'_, Registers> { _ => unreachable!("ICE: Slice operation not supported"), } } - - pub(crate) fn call_gen( - &mut self, - instruction_id: InstructionId, - func: ValueId, - arguments: &Vec, - dfg: &DataFlowGraph, - ) { - match &dfg[func] { - Value::ForeignFunction(func_name) => { - let result_ids = dfg.instruction_results(instruction_id); - - let input_values = vecmap(arguments, |value_id| { - let variable = self.convert_ssa_value(*value_id, dfg); - self.brillig_context.variable_to_value_or_array(variable) - }); - let input_value_types = vecmap(arguments, |value_id| { - let value_type = dfg.type_of_value(*value_id); - type_to_heap_value_type(&value_type) - }); - let output_variables = vecmap(result_ids, |value_id| { - self.allocate_external_call_result(*value_id, dfg) - }); - let output_values = vecmap(&output_variables, |variable| { - self.brillig_context.variable_to_value_or_array(*variable) - }); - let output_value_types = vecmap(result_ids, |value_id| { - let value_type = dfg.type_of_value(*value_id); - type_to_heap_value_type(&value_type) - }); - self.brillig_context.foreign_call_instruction( - func_name.to_owned(), - &input_values, - &input_value_types, - &output_values, - &output_value_types, - ); - - // Deallocate the temporary heap arrays and vectors of the inputs - for input_value in input_values { - match input_value { - ValueOrArray::HeapArray(array) => { - self.brillig_context.deallocate_heap_array(array); - } - ValueOrArray::HeapVector(vector) => { - self.brillig_context.deallocate_heap_vector(vector); - } - _ => {} - } - } - - // Deallocate the temporary heap arrays and vectors of the outputs - for (i, (output_register, output_variable)) in - output_values.iter().zip(output_variables).enumerate() - { - match output_register { - // Returned vectors need to emit some bytecode to format the result as a BrilligVector - ValueOrArray::HeapVector(heap_vector) => { - self.brillig_context.initialize_externally_returned_vector( - output_variable.extract_vector(), - *heap_vector, - ); - // Update the dynamic slice length maintained in SSA - if let ValueOrArray::MemoryAddress(len_index) = output_values[i - 1] { - let element_size = dfg[result_ids[i]].get_type().element_size(); - self.brillig_context.mov_instruction(len_index, heap_vector.size); - self.brillig_context.codegen_usize_op_in_place( - len_index, - BrilligBinaryOp::UnsignedDiv, - element_size, - ); - } else { - unreachable!( - "ICE: a vector must be preceded by a register containing its length" - ); - } - self.brillig_context.deallocate_heap_vector(*heap_vector); - } - ValueOrArray::HeapArray(array) => { - self.brillig_context.deallocate_heap_array(*array); - } - ValueOrArray::MemoryAddress(_) => {} - } - } - } - Value::Function(func_id) => { - let result_ids = dfg.instruction_results(instruction_id); - self.convert_ssa_function_call(*func_id, arguments, dfg, result_ids); - } - Value::Intrinsic(intrinsic) => { - // This match could be combined with the above but without it rust analyzer - // can't automatically insert any missing cases - match intrinsic { - Intrinsic::ArrayLen => { - let [result_value] = dfg.instruction_result(instruction_id); - let result_variable = self.variables.define_single_addr_variable( - self.function_context, - self.brillig_context, - result_value, - dfg, - ); - let param_id = arguments[0]; - // Slices are represented as a tuple in the form: (length, slice contents). - // Thus, we can expect the first argument to a field in the case of a slice - // or an array in the case of an array. - if let Type::Numeric(_) = dfg.type_of_value(param_id) { - let len_variable = self.convert_ssa_value(arguments[0], dfg); - let length = len_variable.extract_single_addr(); - self.brillig_context - .mov_instruction(result_variable.address, length.address); - } else { - self.convert_ssa_array_len(arguments[0], result_variable.address, dfg); - } - } - Intrinsic::AsSlice => { - let source_variable = self.convert_ssa_value(arguments[0], dfg); - let result_ids = dfg.instruction_results(instruction_id); - let destination_len_variable = self.variables.define_single_addr_variable( - self.function_context, - self.brillig_context, - result_ids[0], - dfg, - ); - let destination_variable = self.variables.define_variable( - self.function_context, - self.brillig_context, - result_ids[1], - dfg, - ); - let destination_vector = destination_variable.extract_vector(); - let source_array = source_variable.extract_array(); - let element_size = dfg.type_of_value(arguments[0]).element_size(); - - let source_size_register = self - .brillig_context - .make_usize_constant_instruction(source_array.size.into()); - - // we need to explicitly set the destination_len_variable - self.brillig_context.codegen_usize_op( - source_size_register.address, - destination_len_variable.address, - BrilligBinaryOp::UnsignedDiv, - element_size, - ); - - self.brillig_context.codegen_initialize_vector( - destination_vector, - source_size_register, - None, - ); - - // Items - let vector_items_pointer = self - .brillig_context - .codegen_make_vector_items_pointer(destination_vector); - let array_items_pointer = - self.brillig_context.codegen_make_array_items_pointer(source_array); - - self.brillig_context.codegen_mem_copy( - array_items_pointer, - vector_items_pointer, - source_size_register, - ); - - self.brillig_context.deallocate_single_addr(source_size_register); - self.brillig_context.deallocate_register(vector_items_pointer); - self.brillig_context.deallocate_register(array_items_pointer); - } - Intrinsic::SlicePushBack - | Intrinsic::SlicePopBack - | Intrinsic::SlicePushFront - | Intrinsic::SlicePopFront - | Intrinsic::SliceInsert - | Intrinsic::SliceRemove => { - self.convert_ssa_slice_intrinsic_call( - dfg, - &dfg[func], - instruction_id, - arguments, - ); - } - Intrinsic::ToBits(endianness) => { - let [result] = dfg.instruction_result(instruction_id); - - let source = self.convert_ssa_single_addr_value(arguments[0], dfg); - - let target_array = self - .variables - .define_variable( - self.function_context, - self.brillig_context, - result, - dfg, - ) - .extract_array(); - - let two = - self.brillig_context.make_usize_constant_instruction(2_usize.into()); - - self.brillig_context.codegen_to_radix( - source, - target_array, - two, - matches!(endianness, Endian::Little), - true, - ); - - self.brillig_context.deallocate_single_addr(two); - } - - Intrinsic::ToRadix(endianness) => { - let [result] = dfg.instruction_result(instruction_id); - - let source = self.convert_ssa_single_addr_value(arguments[0], dfg); - let radix = self.convert_ssa_single_addr_value(arguments[1], dfg); - - let target_array = self - .variables - .define_variable( - self.function_context, - self.brillig_context, - result, - dfg, - ) - .extract_array(); - - self.brillig_context.codegen_to_radix( - source, - target_array, - radix, - matches!(endianness, Endian::Little), - false, - ); - } - Intrinsic::Hint(Hint::BlackBox) => { - let result_ids = dfg.instruction_results(instruction_id); - self.convert_ssa_identity_call(arguments, dfg, result_ids); - } - Intrinsic::BlackBox(bb_func) => { - assert!( - !arguments - .iter() - .any(|arg| dfg.type_of_value(*arg).contains_slice_element()), - "Blackbox functions should not be called with arguments of slice type" - ); - - let mut arguments = arguments.to_vec(); - if matches!( - bb_func, - BlackBoxFunc::EcdsaSecp256k1 - | BlackBoxFunc::EcdsaSecp256r1 - | BlackBoxFunc::MultiScalarMul - | BlackBoxFunc::EmbeddedCurveAdd - ) { - // Some black box functions have a predicate argument in SSA which we don't want to - // use in the brillig VM. This is as we do not need to flatten the CFG in brillig - // so we expect the predicate to always be true. - let predicate = arguments.pop().expect( - "ICE: ECDSA black box function must have a predicate argument", - ); - assert_eq!( - dfg.get_numeric_constant_with_type(predicate), - Some((FieldElement::one(), NumericType::bool())), - "ICE: ECDSA black box function must have a predicate argument with value 1" - ); - } - - let function_arguments = - vecmap(arguments, |arg| self.convert_ssa_value(arg, dfg)); - let function_results = dfg.instruction_results(instruction_id); - let function_results = vecmap(function_results, |result| { - self.allocate_external_call_result(*result, dfg) - }); - convert_black_box_call( - self.brillig_context, - bb_func, - &function_arguments, - &function_results, - ); - } - // `Intrinsic::AsWitness` is used to provide hints to acir-gen on optimal expression splitting. - // It is then useless in the brillig runtime and so we can ignore it - Intrinsic::AsWitness => (), - Intrinsic::FieldLessThan => { - let lhs = self.convert_ssa_single_addr_value(arguments[0], dfg); - assert!(lhs.bit_size == FieldElement::max_num_bits()); - let rhs = self.convert_ssa_single_addr_value(arguments[1], dfg); - assert!(rhs.bit_size == FieldElement::max_num_bits()); - - let [result] = dfg.instruction_result(instruction_id); - let destination = self - .variables - .define_variable( - self.function_context, - self.brillig_context, - result, - dfg, - ) - .extract_single_addr(); - assert!(destination.bit_size == 1); - - self.brillig_context.binary_instruction( - lhs, - rhs, - destination, - BrilligBinaryOp::LessThan, - ); - } - Intrinsic::ArrayRefCount => { - let array = self.convert_ssa_value(arguments[0], dfg); - let [result] = dfg.instruction_result(instruction_id); - - let destination = self.variables.define_variable( - self.function_context, - self.brillig_context, - result, - dfg, - ); - let destination = destination.extract_register(); - let array = array.extract_register(); - self.brillig_context.load_instruction(destination, array); - } - Intrinsic::SliceRefCount => { - let array = self.convert_ssa_value(arguments[1], dfg); - let [result] = dfg.instruction_result(instruction_id); - - let destination = self.variables.define_variable( - self.function_context, - self.brillig_context, - result, - dfg, - ); - let destination = destination.extract_register(); - let array = array.extract_register(); - self.brillig_context.load_instruction(destination, array); - } - Intrinsic::IsUnconstrained - | Intrinsic::DerivePedersenGenerators - | Intrinsic::ApplyRangeConstraint - | Intrinsic::StrAsBytes - | Intrinsic::AssertConstant - | Intrinsic::StaticAssert - | Intrinsic::ArrayAsStrUnchecked => { - unreachable!("unsupported function call type {:?}", dfg[func]) - } - } - } - Value::Instruction { .. } - | Value::Param { .. } - | Value::NumericConstant { .. } - | Value::Global(_) => { - unreachable!("unsupported function call type {:?}", dfg[func]) - } - } - } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/mod.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/mod.rs index 40a07f48f6d..85538270470 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/mod.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_instructions/mod.rs @@ -1,3 +1,2 @@ pub(crate) mod brillig_binary; -pub(crate) mod brillig_calls; pub(crate) mod brillig_memory;