From 0b3f138556203451a15e99e84c5836b752da4b1a Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Tue, 10 Dec 2024 15:04:50 -0600 Subject: [PATCH 1/5] Remove Type fields from values --- compiler/noirc_evaluator/src/acir/mod.rs | 9 +- .../src/brillig/brillig_gen/brillig_block.rs | 26 +++--- .../check_for_underconstrained_values.rs | 2 +- .../src/ssa/function_builder/data_bus.rs | 5 +- .../src/ssa/function_builder/mod.rs | 45 +++++----- .../noirc_evaluator/src/ssa/ir/basic_block.rs | 22 ++++- compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 72 ++++++++------- .../noirc_evaluator/src/ssa/ir/instruction.rs | 87 ++++++++++--------- .../src/ssa/ir/instruction/call.rs | 48 ++++------ compiler/noirc_evaluator/src/ssa/ir/value.rs | 22 +---- .../src/ssa/opt/remove_enable_side_effects.rs | 2 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 4 +- .../src/ssa/ssa_gen/context.rs | 2 +- 13 files changed, 163 insertions(+), 183 deletions(-) diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index cfba6ccf3a6..819a83dae78 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -757,7 +757,7 @@ impl<'a> Context<'a> { Instruction::ArrayGet { .. } | Instruction::ArraySet { .. } => { self.handle_array_operation(instruction_id, dfg)?; } - Instruction::Allocate => { + Instruction::Allocate { .. } => { return Err(RuntimeError::UnknownReference { call_stack: self.acir_context.get_call_stack().clone(), }); @@ -805,7 +805,7 @@ impl<'a> Context<'a> { let mut warnings = Vec::new(); match instruction { - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, result_types: _ } => { let function_value = &dfg[*func]; match function_value { Value::Function(id) => { @@ -1009,7 +1009,7 @@ impl<'a> Context<'a> { // Pass the instruction between array methods rather than the internal fields themselves let (array, index, store_value) = match dfg[instruction] { - Instruction::ArrayGet { array, index } => (array, index, None), + Instruction::ArrayGet { array, index, result_type: _ } => (array, index, None), Instruction::ArraySet { array, index, value, mutable } => { mutable_array_set = mutable; (array, index, Some(value)) @@ -1872,7 +1872,8 @@ impl<'a> Context<'a> { let acir_value = match value { Value::NumericConstant { constant, typ } => { - AcirValue::Var(self.acir_context.add_constant(*constant), typ.into()) + let typ = AcirType::from(Type::Numeric(*typ)); + AcirValue::Var(self.acir_context.add_constant(*constant), typ) } Value::Intrinsic(..) => todo!(), Value::Function(function_id) => { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 7b4cdb2b8ce..65263f2d7c4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -175,12 +175,7 @@ impl<'block> BrilligBlock<'block> { // the block parameters need to be defined/allocated before the given block. Variable liveness provides when the block parameters are defined. // For the entry block, the defined block params will be the params of the function + any extra params of blocks it's the immediate dominator of. for param_id in self.function_context.liveness.defined_block_params(&self.block_id) { - let value = &dfg[param_id]; - let param_type = match value { - Value::Param { typ, .. } => typ, - _ => unreachable!("ICE: Only Param type values should appear in block parameters"), - }; - match param_type { + match dfg.type_of_value(param_id) { // Simple parameters and arrays are passed as already filled registers // In the case of arrays, the values should already be in memory and the register should // Be a valid pointer to the array. @@ -281,7 +276,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_single_addr(condition); } } - Instruction::Allocate => { + Instruction::Allocate { .. } => { let result_value = dfg.instruction_results(instruction_id)[0]; let pointer = self.variables.define_single_addr_variable( self.function_context, @@ -298,7 +293,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context .store_instruction(address_var.address, source_variable.extract_register()); } - Instruction::Load { address } => { + Instruction::Load { address, result_type: _ } => { let target_variable = self.variables.define_variable( self.function_context, self.brillig_context, @@ -321,7 +316,7 @@ impl<'block> BrilligBlock<'block> { ); self.brillig_context.not_instruction(condition_register, result_register); } - Instruction::Call { func, arguments } => match &dfg[*func] { + Instruction::Call { func, arguments, result_types: _ } => match &dfg[*func] { Value::ForeignFunction(func_name) => { let result_ids = dfg.instruction_results(instruction_id); @@ -378,7 +373,8 @@ impl<'block> BrilligBlock<'block> { // 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(); + let element_size = + dfg.type_of_value(result_ids[i]).element_size(); self.brillig_context .mov_instruction(len_index, heap_vector.size); self.brillig_context.codegen_usize_op_in_place( @@ -680,7 +676,7 @@ impl<'block> BrilligBlock<'block> { let source_variable = self.convert_ssa_single_addr_value(*value, dfg); self.convert_cast(destination_variable, source_variable); } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { let result_ids = dfg.instruction_results(instruction_id); let destination_variable = self.variables.define_variable( self.function_context, @@ -1285,8 +1281,8 @@ impl<'block> BrilligBlock<'block> { result_variable: SingleAddrVariable, ) { let binary_type = type_of_binary_operation( - dfg[binary.lhs].get_type(), - dfg[binary.rhs].get_type(), + &dfg.type_of_value(binary.lhs), + &dfg.type_of_value(binary.rhs), binary.operator, ); @@ -1794,7 +1790,7 @@ impl<'block> BrilligBlock<'block> { result: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { - let typ = dfg[result].get_type(); + let typ = dfg.type_of_value(result); match typ { Type::Numeric(_) => self.variables.define_variable( self.function_context, @@ -1811,7 +1807,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.allocate_foreign_call_result_array(typ, array); + self.allocate_foreign_call_result_array(&typ, array); variable } diff --git a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index a3421fce8e6..fafd689b90f 100644 --- a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -196,7 +196,7 @@ impl Context { self.value_sets.push(instruction_arguments_and_results); } - Instruction::Call { func: func_id, arguments: argument_ids } => { + Instruction::Call { func: func_id, arguments: argument_ids, result_types: _ } => { match &function.dfg[*func_id] { Value::Intrinsic(intrinsic) => match intrinsic { Intrinsic::ApplyRangeConstraint diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index bd2585a3bfa..055d48244a4 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -115,8 +115,7 @@ impl FunctionBuilder { /// Insert a value into a data bus builder fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) { assert!(databus.databus.is_none(), "initializing finalized call data"); - let typ = self.current_function.dfg[value].get_type().clone(); - match typ { + match self.current_function.dfg.type_of_value(value) { Type::Numeric(_) => { databus.values.push_back(value); databus.index += 1; @@ -226,7 +225,7 @@ impl FunctionBuilder { let ssa_param_sizes: Vec = ssa_params .iter() .map(|ssa_param| { - self.current_function.dfg[*ssa_param].get_type().flattened_size() as usize + self.current_function.dfg.type_of_value(*ssa_param).flattened_size() as usize }) .collect(); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 0ae61404442..cdcc50a9457 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -163,13 +163,11 @@ impl FunctionBuilder { pub(crate) fn insert_instruction( &mut self, instruction: Instruction, - ctrl_typevars: Option>, ) -> InsertInstructionResult { let block = self.current_block(); self.current_function.dfg.insert_instruction_and_results( instruction, block, - ctrl_typevars, self.call_stack.clone(), ) } @@ -191,7 +189,7 @@ impl FunctionBuilder { /// which is always a Reference to the allocated data. pub(crate) fn insert_allocate(&mut self, element_type: Type) -> ValueId { let reference_type = Type::Reference(Arc::new(element_type)); - self.insert_instruction(Instruction::Allocate, Some(vec![reference_type])).first() + self.insert_instruction(Instruction::Allocate { element_type }).first() } pub(crate) fn set_location(&mut self, location: Location) -> &mut FunctionBuilder { @@ -212,15 +210,15 @@ impl FunctionBuilder { /// which should point to a previous Allocate instruction. Note that this is limited to loading /// a single value. Loading multiple values (such as a tuple) will require multiple loads. /// Returns the element that was loaded. - pub(crate) fn insert_load(&mut self, address: ValueId, type_to_load: Type) -> ValueId { - self.insert_instruction(Instruction::Load { address }, Some(vec![type_to_load])).first() + pub(crate) fn insert_load(&mut self, address: ValueId, result_type: Type) -> ValueId { + self.insert_instruction(Instruction::Load { address, result_type }).first() } /// Insert a Store instruction at the end of the current block, storing the given element /// at the given address. Expects that the address points somewhere /// within a previous Allocate instruction. pub(crate) fn insert_store(&mut self, address: ValueId, value: ValueId) { - self.insert_instruction(Instruction::Store { address, value }, None); + self.insert_instruction(Instruction::Store { address, value }); } /// Insert a binary instruction at the end of the current block. @@ -240,19 +238,19 @@ impl FunctionBuilder { ); } let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); - self.insert_instruction(instruction, None).first() + self.insert_instruction(instruction).first() } /// Insert a not instruction at the end of the current block. /// Returns the result of the instruction. pub(crate) fn insert_not(&mut self, rhs: ValueId) -> ValueId { - self.insert_instruction(Instruction::Not(rhs), None).first() + self.insert_instruction(Instruction::Not(rhs)).first() } /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. pub(crate) fn insert_cast(&mut self, value: ValueId, typ: Type) -> ValueId { - self.insert_instruction(Instruction::Cast(value, typ), None).first() + self.insert_instruction(Instruction::Cast(value, typ)).first() } /// Insert a truncate instruction at the end of the current block. @@ -263,8 +261,7 @@ impl FunctionBuilder { bit_size: u32, max_bit_size: u32, ) -> ValueId { - self.insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }, None) - .first() + self.insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }).first() } /// Insert a constrain instruction at the end of the current block. @@ -274,7 +271,7 @@ impl FunctionBuilder { rhs: ValueId, assert_message: Option, ) { - self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message), None); + self.insert_instruction(Instruction::Constrain(lhs, rhs, assert_message)); } /// Insert a [`Instruction::RangeCheck`] instruction at the end of the current block. @@ -284,10 +281,7 @@ impl FunctionBuilder { max_bit_size: u32, assert_message: Option, ) { - self.insert_instruction( - Instruction::RangeCheck { value, max_bit_size, assert_message }, - None, - ); + self.insert_instruction(Instruction::RangeCheck { value, max_bit_size, assert_message }); } /// Insert a call instruction at the end of the current block and return @@ -298,7 +292,8 @@ impl FunctionBuilder { arguments: Vec, result_types: Vec, ) -> Cow<[ValueId]> { - self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results() + let call = Instruction::Call { func, arguments, result_types: Arc::new(result_types) }; + self.insert_instruction(call).results() } /// Insert an instruction to extract an element from an array @@ -308,8 +303,8 @@ impl FunctionBuilder { index: ValueId, element_type: Type, ) -> ValueId { - let element_type = Some(vec![element_type]); - self.insert_instruction(Instruction::ArrayGet { array, index }, element_type).first() + let get = Instruction::ArrayGet { array, index, result_type: element_type }; + self.insert_instruction(get).first() } /// Insert an instruction to create a new array with the given index replaced with a new value @@ -319,7 +314,7 @@ impl FunctionBuilder { index: ValueId, value: ValueId, ) -> ValueId { - self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: false }, None) + self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: false }) .first() } @@ -329,26 +324,26 @@ impl FunctionBuilder { index: ValueId, value: ValueId, ) -> ValueId { - self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: true }, None) + self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: true }) .first() } /// Insert an instruction to increment an array's reference count. This only has an effect /// in unconstrained code where arrays are reference counted and copy on write. pub(crate) fn insert_inc_rc(&mut self, value: ValueId) { - self.insert_instruction(Instruction::IncrementRc { value }, None); + self.insert_instruction(Instruction::IncrementRc { value }); } /// Insert an instruction to decrement an array's reference count. This only has an effect /// in unconstrained code where arrays are reference counted and copy on write. pub(crate) fn insert_dec_rc(&mut self, value: ValueId) { - self.insert_instruction(Instruction::DecrementRc { value }, None); + self.insert_instruction(Instruction::DecrementRc { value }); } /// Insert an enable_side_effects_if instruction. These are normally only automatically /// inserted during the flattening pass when branching is removed. pub(crate) fn insert_enable_side_effects_if(&mut self, condition: ValueId) { - self.insert_instruction(Instruction::EnableSideEffectsIf { condition }, None); + self.insert_instruction(Instruction::EnableSideEffectsIf { condition }); } /// Insert a `make_array` instruction to create a new array or slice. @@ -359,7 +354,7 @@ impl FunctionBuilder { typ: Type, ) -> ValueId { assert!(matches!(typ, Type::Array(..) | Type::Slice(_))); - self.insert_instruction(Instruction::MakeArray { elements, typ }, None).first() + self.insert_instruction(Instruction::MakeArray { elements, typ }).first() } /// Terminates the current block with the given terminator instruction diff --git a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs index a7c637dedd0..bf20828d2ce 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs @@ -2,6 +2,7 @@ use super::{ dfg::CallStack, instruction::{InstructionId, TerminatorInstruction}, map::Id, + types::Type, value::ValueId, }; use serde::{Deserialize, Serialize}; @@ -14,9 +15,12 @@ use serde::{Deserialize, Serialize}; /// block, then all instructions are executed. ie single-entry single-exit. #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub(crate) struct BasicBlock { - /// Parameters to the basic block. + /// Each parameter of this block parameters: Vec, + /// Types of each parameter to this block + parameter_types: Vec, + /// Instructions in the basic block. instructions: Vec, @@ -33,8 +37,8 @@ pub(crate) type BasicBlockId = Id; impl BasicBlock { /// Create a new BasicBlock with the given parameters. /// Parameters can also be added later via BasicBlock::add_parameter - pub(crate) fn new() -> Self { - Self { parameters: Vec::new(), instructions: Vec::new(), terminator: None } + pub(crate) fn new(parameter_types: Vec) -> Self { + Self { parameter_types, parameters: Vec::new(), instructions: Vec::new(), terminator: None } } /// Returns the parameters of this block @@ -42,6 +46,11 @@ impl BasicBlock { &self.parameters } + /// Retrieve the type of the given parameter + pub(crate) fn type_of_parameter(&self, parameter_index: usize) -> &Type { + &self.parameter_types[parameter_index] + } + /// Removes all the parameters of this block pub(crate) fn take_parameters(&mut self) -> Vec { std::mem::take(&mut self.parameters) @@ -50,8 +59,9 @@ impl BasicBlock { /// Adds a parameter to this BasicBlock. /// Expects that the ValueId given should refer to a Value::Param /// instance with its position equal to self.parameters.len(). - pub(crate) fn add_parameter(&mut self, parameter: ValueId) { + pub(crate) fn add_parameter(&mut self, parameter: ValueId, typ: Type) { self.parameters.push(parameter); + self.parameter_types.push(typ); } /// Replace this block's current parameters with that of the given Vec. @@ -153,4 +163,8 @@ impl BasicBlock { None => vec![].into_iter(), } } + + pub(crate) fn parameter_types(&self) -> &[Type] { + &self.parameter_types + } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 827944e22d1..1b3f459bee3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -103,8 +103,8 @@ impl DataFlowGraph { /// Creates a new basic block with no parameters. /// After being created, the block is unreachable in the current function /// until another block is made to jump to it. - pub(crate) fn make_block(&mut self) -> BasicBlockId { - self.blocks.insert(BasicBlock::new()) + pub(crate) fn make_block(&mut self, parameter_types: Vec) -> BasicBlockId { + self.blocks.insert(BasicBlock::new(parameter_types)) } /// Create a new block with the same parameter count and parameter @@ -115,12 +115,11 @@ impl DataFlowGraph { &mut self, block: BasicBlockId, ) -> BasicBlockId { - let new_block = self.make_block(); let parameters = self.blocks[block].parameters(); + let new_block = self.make_block(self.blocks[block].parameter_types().to_vec()); - let parameters = vecmap(parameters.iter().enumerate(), |(position, param)| { - let typ = self.values[*param].get_type().clone(); - self.values.insert(Value::Param { block: new_block, position, typ }) + let parameters = vecmap(parameters.iter().enumerate(), |(position, _)| { + self.values.insert(Value::Param { block: new_block, position }) }); self.blocks[new_block].set_parameters(parameters); @@ -154,13 +153,9 @@ impl DataFlowGraph { /// Populates the instruction's results with the given ctrl_typevars if the instruction /// is a Load, Call, or Intrinsic. Otherwise the instruction's results will be known /// by the instruction itself and None can safely be passed for this parameter. - pub(crate) fn make_instruction( - &mut self, - instruction_data: Instruction, - ctrl_typevars: Option>, - ) -> InstructionId { + pub(crate) fn make_instruction(&mut self, instruction_data: Instruction) -> InstructionId { let id = self.instructions.insert(instruction_data); - self.make_instruction_results(id, ctrl_typevars); + self.make_instruction_results(id); id } @@ -169,10 +164,9 @@ impl DataFlowGraph { &mut self, instruction: Instruction, block: BasicBlockId, - ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { - match instruction.simplify(self, block, ctrl_typevars.clone(), &call_stack) { + match instruction.simplify(self, block, &call_stack) { SimplifyResult::SimplifiedTo(simplification) => { InsertInstructionResult::SimplifiedTo(simplification) } @@ -198,7 +192,7 @@ impl DataFlowGraph { let mut last_id = None; for instruction in instructions { - let id = self.make_instruction(instruction, ctrl_typevars.clone()); + let id = self.make_instruction(instruction); self.blocks[block].insert_instruction(id); self.locations.insert(id, call_stack.clone()); last_id = Some(id); @@ -233,11 +227,12 @@ impl DataFlowGraph { pub(crate) fn set_type_of_value(&mut self, value_id: ValueId, target_type: Type) { let value = &mut self.values[value_id]; match value { - Value::Instruction { typ, .. } - | Value::Param { typ, .. } - | Value::NumericConstant { typ, .. } => { + Value::Instruction { typ, .. } | Value::NumericConstant { typ, .. } => { *typ = target_type; } + Value::Param { block, position } => { + // todo + } _ => { unreachable!("ICE: Cannot set type of {:?}", value); } @@ -302,15 +297,11 @@ impl DataFlowGraph { /// make_instruction automatically. /// /// Returns the results of the instruction - pub(crate) fn make_instruction_results( - &mut self, - instruction_id: InstructionId, - ctrl_typevars: Option>, - ) { - let result_types = self.instruction_result_types(instruction_id, ctrl_typevars); - let results = vecmap(result_types.into_iter().enumerate(), |(position, typ)| { + pub(crate) fn make_instruction_results(&mut self, instruction_id: InstructionId) { + let result_types = self.instruction_result_types(instruction_id); + let results = vecmap(result_types.into_iter().enumerate(), |(position, _typ)| { let instruction = instruction_id; - self.values.insert(Value::Instruction { typ, position, instruction }) + self.values.insert(Value::Instruction { position, instruction }) }); self.results.insert(instruction_id, results); @@ -324,25 +315,32 @@ impl DataFlowGraph { /// the type of an instruction that does not require them. Compared to passing an empty Vec, /// Option has the benefit of panicking if it is accidentally used for a Call instruction, /// rather than silently returning the empty Vec and continuing. - fn instruction_result_types( - &self, - instruction_id: InstructionId, - ctrl_typevars: Option>, - ) -> Vec { + fn instruction_result_types(&self, instruction_id: InstructionId) -> Vec { let instruction = &self.instructions[instruction_id]; match instruction.result_type() { InstructionResultType::Known(typ) => vec![typ], InstructionResultType::Operand(value) => vec![self.type_of_value(value)], InstructionResultType::None => vec![], - InstructionResultType::Unknown => { - ctrl_typevars.expect("Control typevars required but not given") - } } } /// Returns the type of a given value pub(crate) fn type_of_value(&self, value: ValueId) -> Type { - self.values[value].get_type().clone() + match &self.values[value] { + Value::Instruction { instruction, position } => { + match self[*instruction].result_type() { + // How expensive is this recursive call? Maybe we should store types + InstructionResultType::Operand(value) => self.type_of_value(value), + InstructionResultType::Known(typ) => typ, + InstructionResultType::None => unreachable!("Instruction has no results"), + } + } + Value::Param { block, position } => self[*block].type_of_parameter(*position), + Value::NumericConstant { typ, .. } => Type::Numeric(*typ), + Value::Function(_) => Type::Function, + Value::Intrinsic(_) => Type::Function, + Value::ForeignFunction(_) => Type::Function, + } } /// Returns the maximum possible number of bits that `value` can potentially be. @@ -409,8 +407,8 @@ impl DataFlowGraph { pub(crate) fn add_block_parameter(&mut self, block_id: BasicBlockId, typ: Type) -> ValueId { let block = &mut self.blocks[block_id]; let position = block.parameters().len(); - let parameter = self.values.insert(Value::Param { block: block_id, position, typ }); - block.add_parameter(parameter); + let parameter = self.values.insert(Value::Param { block: block_id, position }); + block.add_parameter(parameter, typ); parameter } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index ba212fdad96..827b9be89df 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,6 +1,9 @@ use noirc_errors::Location; use serde::{Deserialize, Serialize}; -use std::hash::{Hash, Hasher}; +use std::{ + hash::{Hash, Hasher}, + sync::Arc, +}; use acvm::{ acir::AcirField, @@ -268,15 +271,15 @@ pub(crate) enum Instruction { RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, /// Performs a function call with a list of its arguments. - Call { func: ValueId, arguments: Vec }, + Call { func: ValueId, arguments: Vec, result_types: Arc> }, /// Allocates a region of memory. Note that this is not concerned with /// the type of memory, the type of element is determined when loading this memory. /// This is used for representing mutable variables and references. - Allocate, + Allocate { element_type: Type }, /// Loads a value from memory. - Load { address: ValueId }, + Load { address: ValueId, result_type: Type }, /// Writes a value to memory. Store { address: ValueId, value: ValueId }, @@ -301,7 +304,7 @@ pub(crate) enum Instruction { EnableSideEffectsIf { condition: ValueId }, /// Retrieve a value from an array at the given index - ArrayGet { array: ValueId, index: ValueId }, + ArrayGet { array: ValueId, index: ValueId, result_type: Type }, /// Creates a new array with the new value at the given index. All other elements are identical /// to those in the given array. This will not modify the original array unless `mutable` is @@ -352,12 +355,22 @@ impl Instruction { } /// Returns the type that this instruction will return. - pub(crate) fn result_type(&self) -> InstructionResultType { + pub(crate) fn result_type(&self, position: usize) -> InstructionResultType { match self { Instruction::Binary(binary) => binary.result_type(), - Instruction::Cast(_, typ) | Instruction::MakeArray { typ, .. } => { + Instruction::Cast(_, typ) + | Instruction::MakeArray { typ, .. } + | Instruction::Load { result_type: typ, .. } + | Instruction::ArrayGet { result_type: typ, .. } => { InstructionResultType::Known(typ.clone()) } + Instruction::Call { result_types, .. } => { + InstructionResultType::Known(result_types[position].clone()) + } + Instruction::Allocate { element_type } => { + let typ = Type::Reference(Arc::new(element_type.clone())); + InstructionResultType::Known(typ) + } Instruction::Not(value) | Instruction::Truncate { value, .. } | Instruction::ArraySet { array: value, .. } @@ -370,19 +383,9 @@ impl Instruction { | Instruction::DecrementRc { .. } | Instruction::RangeCheck { .. } | Instruction::EnableSideEffectsIf { .. } => InstructionResultType::None, - Instruction::Allocate { .. } - | Instruction::Load { .. } - | Instruction::ArrayGet { .. } - | Instruction::Call { .. } => InstructionResultType::Unknown, } } - /// True if this instruction requires specifying the control type variables when - /// inserting this instruction into a DataFlowGraph. - pub(crate) fn requires_ctrl_typevars(&self) -> bool { - matches!(self.result_type(), InstructionResultType::Unknown) - } - /// Indicates if the instruction has a side effect, ie. it can fail, or it interacts with memory. /// /// This is similar to `can_be_deduplicated`, but it doesn't depend on whether the caller takes @@ -393,7 +396,7 @@ impl Instruction { match self { // These either have side-effects or interact with memory EnableSideEffectsIf { .. } - | Allocate + | Allocate { .. } | Load { .. } | Store { .. } | IncrementRc { .. } @@ -453,7 +456,7 @@ impl Instruction { match self { // These either have side-effects or interact with memory EnableSideEffectsIf { .. } - | Allocate + | Allocate { .. } | Load { .. } | Store { .. } | IncrementRc { .. } @@ -508,7 +511,7 @@ impl Instruction { Cast(_, _) | Not(_) | Truncate { .. } - | Allocate + | Allocate { .. } | Load { .. } | ArrayGet { .. } | IfElse { .. } @@ -563,7 +566,7 @@ impl Instruction { true } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { // `ArrayGet`s which read from "known good" indices from an array should not need a predicate. !dfg.is_safe_index(*index, *array) } @@ -583,7 +586,7 @@ impl Instruction { | Instruction::Truncate { .. } | Instruction::Constrain(_, _, _) | Instruction::RangeCheck { .. } - | Instruction::Allocate + | Instruction::Allocate { .. } | Instruction::Load { .. } | Instruction::Store { .. } | Instruction::IfElse { .. } @@ -626,21 +629,28 @@ impl Instruction { }); Instruction::Constrain(lhs, rhs, assert_message) } - Instruction::Call { func, arguments } => Instruction::Call { + Instruction::Call { func, arguments, result_types } => Instruction::Call { func: f(*func), arguments: vecmap(arguments.iter().copied(), f), + result_types: result_types.clone(), }, - Instruction::Allocate => Instruction::Allocate, - Instruction::Load { address } => Instruction::Load { address: f(*address) }, + Instruction::Allocate { element_type } => { + Instruction::Allocate { element_type: element_type.clone() } + } + Instruction::Load { address, result_type } => { + Instruction::Load { address: f(*address), result_type: result_type.clone() } + } Instruction::Store { address, value } => { Instruction::Store { address: f(*address), value: f(*value) } } Instruction::EnableSideEffectsIf { condition } => { Instruction::EnableSideEffectsIf { condition: f(*condition) } } - Instruction::ArrayGet { array, index } => { - Instruction::ArrayGet { array: f(*array), index: f(*index) } - } + Instruction::ArrayGet { array, index, result_type } => Instruction::ArrayGet { + array: f(*array), + index: f(*index), + result_type: result_type.clone(), + }, Instruction::ArraySet { array, index, value, mutable } => Instruction::ArraySet { array: f(*array), index: f(*index), @@ -678,7 +688,7 @@ impl Instruction { f(binary.lhs); f(binary.rhs); } - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, result_types: _ } => { f(*func); for argument in arguments { f(*argument); @@ -687,7 +697,7 @@ impl Instruction { Instruction::Cast(value, _) | Instruction::Not(value) | Instruction::Truncate { value, .. } - | Instruction::Load { address: value } => { + | Instruction::Load { address: value, result_type: _ } => { f(*value); } Instruction::Constrain(lhs, rhs, assert_error) => { @@ -705,7 +715,7 @@ impl Instruction { f(*value); } Instruction::Allocate { .. } => (), - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { f(*array); f(*index); } @@ -745,7 +755,6 @@ impl Instruction { &self, dfg: &mut DataFlowGraph, block: BasicBlockId, - ctrl_typevars: Option>, call_stack: &CallStack, ) -> SimplifyResult { use SimplifyResult::*; @@ -781,7 +790,7 @@ impl Instruction { SimplifiedToInstructionMultiple(constraints) } } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { if let Some(index) = dfg.get_numeric_constant(*index) { try_optimize_array_get_from_previous_set(dfg, *array, index) } else { @@ -802,7 +811,6 @@ impl Instruction { let new_array = dfg.insert_instruction_and_results( instruction, block, - Option::None, call_stack.clone(), ); return SimplifiedTo(new_array.first()); @@ -862,8 +870,8 @@ impl Instruction { None } } - Instruction::Call { func, arguments } => { - simplify_call(*func, arguments, dfg, block, ctrl_typevars, call_stack) + Instruction::Call { func, arguments, result_types: _ } => { + simplify_call(*func, arguments, dfg, block, call_stack) } Instruction::EnableSideEffectsIf { condition } => { if let Some(last) = dfg[block].instructions().last().copied() { @@ -1021,7 +1029,7 @@ fn try_optimize_array_set_from_previous_get( ) -> SimplifyResult { let array_from_get = match &dfg[target_value] { Value::Instruction { instruction, .. } => match &dfg[*instruction] { - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { if *array == array_id && *index == target_index { // If array and index match from the value, we can immediately simplify return SimplifyResult::SimplifiedTo(array_id); @@ -1122,9 +1130,8 @@ pub(crate) enum InstructionResultType { /// The result type of this instruction is known to be this type - independent of its operands. Known(Type), - /// The result type of this function is unknown and separate from its operand types. - /// This occurs for function calls and load operations. - Unknown, + /// Function calls are a special case, they may return multiple values + Multiple(Arc>), /// This instruction does not return any results. None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 40dd0bca41a..7aa1df37159 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -35,9 +35,9 @@ mod blackbox; pub(super) fn simplify_call( func: ValueId, arguments: &[ValueId], + return_types: &[Type], dfg: &mut DataFlowGraph, block: BasicBlockId, - ctrl_typevars: Option>, call_stack: &CallStack, ) -> SimplifyResult { let intrinsic = match &dfg[func] { @@ -45,7 +45,7 @@ pub(super) fn simplify_call( _ => return SimplifyResult::None, }; - let return_type = ctrl_typevars.and_then(|return_types| return_types.first().cloned()); + let return_type = return_types.get(0); let constant_args: Option> = arguments.iter().map(|value_id| dfg.get_numeric_constant(*value_id)).collect(); @@ -346,14 +346,8 @@ pub(super) fn simplify_call( bit_size: target_type.bit_size(), max_bit_size: incoming_type.bit_size(), }; - let truncated_value = dfg - .insert_instruction_and_results( - truncate, - block, - Some(vec![incoming_type]), - call_stack.clone(), - ) - .first(); + let truncated_value = + dfg.insert_instruction_and_results(truncate, block, call_stack.clone()).first(); let instruction = Instruction::Cast(truncated_value, target_type); SimplifyResult::SimplifiedToInstruction(instruction) @@ -410,7 +404,7 @@ fn update_slice_length( let one = dfg.make_constant(FieldElement::one(), Type::length_type()); let instruction = Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }); let call_stack = dfg.get_value_call_stack(slice_len); - dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + dfg.insert_instruction_and_results(instruction, block, call_stack).first() } fn simplify_slice_push_back( @@ -426,16 +420,11 @@ fn simplify_slice_push_back( let len_equals_capacity_instr = Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Eq, rhs: capacity }); let len_equals_capacity = dfg - .insert_instruction_and_results(len_equals_capacity_instr, block, None, call_stack.clone()) + .insert_instruction_and_results(len_equals_capacity_instr, block, call_stack.clone()) .first(); let len_not_equals_capacity_instr = Instruction::Not(len_equals_capacity); let len_not_equals_capacity = dfg - .insert_instruction_and_results( - len_not_equals_capacity_instr, - block, - None, - call_stack.clone(), - ) + .insert_instruction_and_results(len_not_equals_capacity_instr, block, call_stack.clone()) .first(); let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Add, block); @@ -455,7 +444,7 @@ fn simplify_slice_push_back( }; let set_last_slice_value = dfg - .insert_instruction_and_results(set_last_slice_value_instr, block, None, call_stack.clone()) + .insert_instruction_and_results(set_last_slice_value_instr, block, call_stack.clone()) .first(); let mut slice_sizes = HashMap::default(); @@ -490,29 +479,24 @@ fn simplify_slice_pop_back( } }; - let element_count = element_type.element_size(); + let element_count = element_types.len(); let mut results = VecDeque::with_capacity(element_count + 1); let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); let element_size = dfg.make_constant((element_count as u128).into(), Type::length_type()); let flattened_len_instr = Instruction::binary(BinaryOp::Mul, arguments[0], element_size); - let mut flattened_len = dfg - .insert_instruction_and_results(flattened_len_instr, block, None, call_stack.clone()) - .first(); + let mut flattened_len = + dfg.insert_instruction_and_results(flattened_len_instr, block, call_stack.clone()).first(); flattened_len = update_slice_length(flattened_len, dfg, BinaryOp::Sub, block); // We must pop multiple elements in the case of a slice of tuples - for _ in 0..element_count { + for result_type in element_types { let get_last_elem_instr = - Instruction::ArrayGet { array: arguments[1], index: flattened_len }; + Instruction::ArrayGet { array: arguments[1], index: flattened_len, result_type }; + let get_last_elem = dfg - .insert_instruction_and_results( - get_last_elem_instr, - block, - Some(element_types.to_vec()), - call_stack.clone(), - ) + .insert_instruction_and_results(get_last_elem_instr, block, call_stack.clone()) .first(); results.push_front(get_last_elem); @@ -649,7 +633,7 @@ fn make_array( ) -> ValueId { let instruction = Instruction::MakeArray { elements, typ }; let call_stack = call_stack.clone(); - dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() + dfg.insert_instruction_and_results(instruction, block, call_stack).first() } fn make_constant_slice( diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index ef494200308..1d000e6172b 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -7,7 +7,7 @@ use super::{ function::FunctionId, instruction::{InstructionId, Intrinsic}, map::Id, - types::Type, + types::{NumericType, Type}, }; pub(crate) type ValueId = Id; @@ -25,16 +25,16 @@ pub(crate) enum Value { /// Example, if you add two numbers together, then the resulting /// value would have position `0`, the typ would be the type /// of the operands, and the instruction would map to an add instruction. - Instruction { instruction: InstructionId, position: usize, typ: Type }, + Instruction { instruction: InstructionId, position: usize }, /// This Value originates from a block parameter. Since function parameters /// are also represented as block parameters, this includes function parameters as well. /// /// position -- the index of this Value in the block parameters list - Param { block: BasicBlockId, position: usize, typ: Type }, + Param { block: BasicBlockId, position: usize }, /// This Value originates from a numeric constant - NumericConstant { constant: FieldElement, typ: Type }, + NumericConstant { constant: FieldElement, typ: NumericType }, /// This Value refers to a function in the IR. /// Functions always have the type Type::Function. @@ -52,17 +52,3 @@ pub(crate) enum Value { /// other than generating different backend operations and being only accessible through Brillig. ForeignFunction(String), } - -impl Value { - /// Retrieves the type of this Value - pub(crate) fn get_type(&self) -> &Type { - match self { - Value::Instruction { typ, .. } => typ, - Value::Param { typ, .. } => typ, - Value::NumericConstant { typ, .. } => typ, - Value::Function { .. } => &Type::Function, - Value::Intrinsic { .. } => &Type::Function, - Value::ForeignFunction { .. } => &Type::Function, - } - } -} diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index ce5534ecc7a..235d8968257 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -151,7 +151,7 @@ impl Context { EnableSideEffectsIf { .. } | ArrayGet { .. } | ArraySet { .. } - | Allocate + | Allocate { .. } | Store { .. } | Load { .. } => true, diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 142447c83a5..4119494b287 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -455,7 +455,7 @@ impl Loop { // We insert into a fresh block first and move instructions into the unroll_into block later // only once we verify the jmpif instruction has a constant condition. If it does not, we can // just discard this fresh block and leave the loop unmodified. - let fresh_block = function.dfg.make_block(); + let fresh_block = function.dfg.make_block(Vec::new()); let mut context = LoopIteration::new(function, self, fresh_block, self.header); let source_block = &context.dfg()[context.source_block]; @@ -586,7 +586,7 @@ impl Loop { for block in &self.blocks { for instruction in function.dfg[*block].instructions() { match &function.dfg[*instruction] { - Instruction::Load { address } if refs.contains(address) => { + Instruction::Load { address, .. } if refs.contains(address) => { loads += 1; } Instruction::Store { address, .. } if refs.contains(address) => { diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 116e0de4ecd..ee56959b1a4 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -491,7 +491,7 @@ impl<'a> FunctionContext<'a> { same_sign, Some(message.into()), ); - self.builder.set_location(location).insert_instruction(overflow_check, None); + self.builder.set_location(location).insert_instruction(overflow_check); } BinaryOpKind::Multiply => { // Overflow check for the multiplication: From 755573d2c8f307f1dc14c9c802a41449fd5b8605 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 11 Dec 2024 08:03:52 -0600 Subject: [PATCH 2/5] More changes --- .../src/brillig/brillig_gen/brillig_block.rs | 4 +- .../src/ssa/function_builder/data_bus.rs | 6 +- .../src/ssa/function_builder/mod.rs | 8 +- .../noirc_evaluator/src/ssa/ir/basic_block.rs | 12 ++- compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 86 ++++++++++++------- .../src/ssa/ir/function_inserter.rs | 5 -- .../noirc_evaluator/src/ssa/ir/instruction.rs | 8 +- .../src/ssa/ir/instruction/binary.rs | 29 +++---- compiler/noirc_evaluator/src/ssa/ir/types.rs | 24 +++++- compiler/noirc_evaluator/src/ssa/ir/value.rs | 2 +- .../src/ssa/opt/remove_if_else.rs | 7 +- 11 files changed, 116 insertions(+), 75 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 65263f2d7c4..439ede24ead 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -222,14 +222,14 @@ impl<'block> BrilligBlock<'block> { ) { // If the constraint is of the form `x == u1 1` then we can simply constrain `x` directly ( - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), + Some((constant, NumericType::Unsigned { bit_size: 1 })), None, ) if constant == FieldElement::one() => { (self.convert_ssa_single_addr_value(*rhs, dfg), false) } ( None, - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), + Some((constant, NumericType::Unsigned { bit_size: 1 })), ) if constant == FieldElement::one() => { (self.convert_ssa_single_addr_value(*lhs, dfg), false) } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index 055d48244a4..b342ee720bd 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, sync::Arc}; -use crate::ssa::ir::{function::RuntimeType, types::Type, value::ValueId}; +use crate::ssa::ir::{function::RuntimeType, types::{Type, NumericType}, value::ValueId}; use acvm::FieldElement; use fxhash::FxHashMap as HashMap; use noirc_frontend::ast; @@ -127,10 +127,12 @@ impl FunctionBuilder { for _i in 0..len { for subitem_typ in typ.iter() { // load each element of the array, and add it to the databus + let length_type = NumericType::length_type(); + let index_field = FieldElement::from(index as i128); let index_var = self .current_function .dfg - .make_constant(FieldElement::from(index as i128), Type::length_type()); + .make_constant(index_field, length_type); let element = self.insert_array_get(value, index_var, subitem_typ.clone()); index += match subitem_typ { Type::Array(_, _) | Type::Slice(_) => subitem_typ.element_size(), diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index cdcc50a9457..c624c299356 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -20,7 +20,7 @@ use super::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, - instruction::{ConstrainError, InstructionId, Intrinsic}, + instruction::{ConstrainError, InstructionId, Intrinsic}, types::NumericType, }, ssa_gen::Ssa, }; @@ -122,19 +122,19 @@ impl FunctionBuilder { pub(crate) fn numeric_constant( &mut self, value: impl Into, - typ: Type, + typ: NumericType, ) -> ValueId { self.current_function.dfg.make_constant(value.into(), typ) } /// Insert a numeric constant into the current function of type Field pub(crate) fn field_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::field()) + self.numeric_constant(value.into(), NumericType::NativeField) } /// Insert a numeric constant into the current function of type Type::length_type() pub(crate) fn length_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::length_type()) + self.numeric_constant(value.into(), NumericType::length_type()) } /// Returns the type of the given value. diff --git a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs index bf20828d2ce..e9b2911a297 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs @@ -35,10 +35,10 @@ pub(crate) struct BasicBlock { pub(crate) type BasicBlockId = Id; impl BasicBlock { - /// Create a new BasicBlock with the given parameters. - /// Parameters can also be added later via BasicBlock::add_parameter - pub(crate) fn new(parameter_types: Vec) -> Self { - Self { parameter_types, parameters: Vec::new(), instructions: Vec::new(), terminator: None } + /// Create a new BasicBlock with no parameters. + /// Parameters can be added later via BasicBlock::add_parameter + pub(crate) fn new() -> Self { + Self { parameter_types: Vec::new(), parameters: Vec::new(), instructions: Vec::new(), terminator: None } } /// Returns the parameters of this block @@ -167,4 +167,8 @@ impl BasicBlock { pub(crate) fn parameter_types(&self) -> &[Type] { &self.parameter_types } + + pub(crate) fn parameter_types_mut(&mut self) -> &mut Vec { + &mut self.parameter_types + } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 1b3f459bee3..64ca057ddc3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -9,7 +9,7 @@ use super::{ Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction, }, map::DenseMap, - types::Type, + types::{Type, NumericType}, value::{Value, ValueId}, }; @@ -50,7 +50,7 @@ pub(crate) struct DataFlowGraph { /// Each constant is unique, attempting to insert the same constant /// twice will return the same ValueId. #[serde(skip)] - constants: HashMap<(FieldElement, Type), ValueId>, + constants: HashMap<(FieldElement, NumericType), ValueId>, /// Contains each function that has been imported into the current function. /// A unique `ValueId` for each function's [`Value::Function`] is stored so any given FunctionId @@ -103,8 +103,8 @@ impl DataFlowGraph { /// Creates a new basic block with no parameters. /// After being created, the block is unreachable in the current function /// until another block is made to jump to it. - pub(crate) fn make_block(&mut self, parameter_types: Vec) -> BasicBlockId { - self.blocks.insert(BasicBlock::new(parameter_types)) + pub(crate) fn make_block(&mut self) -> BasicBlockId { + self.blocks.insert(BasicBlock::new()) } /// Create a new block with the same parameter count and parameter @@ -116,13 +116,15 @@ impl DataFlowGraph { block: BasicBlockId, ) -> BasicBlockId { let parameters = self.blocks[block].parameters(); - let new_block = self.make_block(self.blocks[block].parameter_types().to_vec()); + let new_block = self.make_block(); let parameters = vecmap(parameters.iter().enumerate(), |(position, _)| { self.values.insert(Value::Param { block: new_block, position }) }); - self.blocks[new_block].set_parameters(parameters); + let new_block_value = &mut self.blocks[new_block]; + new_block_value.set_parameters(parameters); + new_block_value.parameter_types_mut().extend_from_slice(self.blocks[block].parameter_types()); new_block } @@ -225,17 +227,39 @@ impl DataFlowGraph { /// Set the type of value_id to the target_type. pub(crate) fn set_type_of_value(&mut self, value_id: ValueId, target_type: Type) { - let value = &mut self.values[value_id]; - match value { - Value::Instruction { typ, .. } | Value::NumericConstant { typ, .. } => { - *typ = target_type; + match &self.values[value_id] { + Value::Instruction { instruction, position } => { + let position = *position; + match &mut self[*instruction] { + Instruction::Call { result_types, .. } => { + result_types[position] = target_type; + }, + Instruction::Load { result_type, .. } + | Instruction::Cast(_, result_type) + | Instruction::ArrayGet { result_type, .. } => { + *result_type = target_type; + }, + + instruction @ (Instruction::Binary(_) + | Instruction::Not(_) + | Instruction::Truncate { .. } + | Instruction::Allocate { .. } + | Instruction::Store { .. } + | Instruction::ArraySet { .. } + | Instruction::IfElse { .. } + | Instruction::MakeArray { .. }) => panic!("Can't set the type of {instruction:?}"), + + Instruction::EnableSideEffectsIf { .. } + | Instruction::Constrain(..) + | Instruction::RangeCheck { .. } + | Instruction::IncrementRc { .. } + | Instruction::DecrementRc { .. } => unreachable!("These instructions have no results"), + } } Value::Param { block, position } => { - // todo - } - _ => { - unreachable!("ICE: Cannot set type of {:?}", value); + self[*block].parameter_types_mut()[*position] = target_type; } + value => unreachable!("ICE: Cannot set type of {:?}", value), } } @@ -252,11 +276,11 @@ impl DataFlowGraph { /// Creates a new constant value, or returns the Id to an existing one if /// one already exists. - pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: Type) -> ValueId { - if let Some(id) = self.constants.get(&(constant, typ.clone())) { + pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: NumericType) -> ValueId { + if let Some(id) = self.constants.get(&(constant, typ)) { return *id; } - let id = self.values.insert(Value::NumericConstant { constant, typ: typ.clone() }); + let id = self.values.insert(Value::NumericConstant { constant, typ }); self.constants.insert((constant, typ), id); id } @@ -298,8 +322,8 @@ impl DataFlowGraph { /// /// Returns the results of the instruction pub(crate) fn make_instruction_results(&mut self, instruction_id: InstructionId) { - let result_types = self.instruction_result_types(instruction_id); - let results = vecmap(result_types.into_iter().enumerate(), |(position, _typ)| { + let result_count = self.instruction_result_count(instruction_id); + let results = vecmap(0 .. result_count, |position| { let instruction = instruction_id; self.values.insert(Value::Instruction { position, instruction }) }); @@ -315,12 +339,13 @@ impl DataFlowGraph { /// the type of an instruction that does not require them. Compared to passing an empty Vec, /// Option has the benefit of panicking if it is accidentally used for a Call instruction, /// rather than silently returning the empty Vec and continuing. - fn instruction_result_types(&self, instruction_id: InstructionId) -> Vec { + fn instruction_result_count(&self, instruction_id: InstructionId) -> usize { let instruction = &self.instructions[instruction_id]; match instruction.result_type() { - InstructionResultType::Known(typ) => vec![typ], - InstructionResultType::Operand(value) => vec![self.type_of_value(value)], - InstructionResultType::None => vec![], + InstructionResultType::Known(_) => 1, + InstructionResultType::Operand(_) => 1, + InstructionResultType::None => 0, + InstructionResultType::Multiple(types) => types.len() } } @@ -333,9 +358,10 @@ impl DataFlowGraph { InstructionResultType::Operand(value) => self.type_of_value(value), InstructionResultType::Known(typ) => typ, InstructionResultType::None => unreachable!("Instruction has no results"), + InstructionResultType::Multiple(types) => types[*position].clone() } } - Value::Param { block, position } => self[*block].type_of_parameter(*position), + Value::Param { block, position } => self[*block].type_of_parameter(*position).clone(), Value::NumericConstant { typ, .. } => Type::Numeric(*typ), Value::Function(_) => Type::Function, Value::Intrinsic(_) => Type::Function, @@ -365,7 +391,7 @@ impl DataFlowGraph { /// True if the type of this value is Type::Reference. /// Using this method over type_of_value avoids cloning the value's type. pub(crate) fn value_is_reference(&self, value: ValueId) -> bool { - matches!(self.values[value].get_type(), Type::Reference(_)) + matches!(self.type_of_value(value), Type::Reference(_)) } /// Replaces an instruction result with a fresh id. @@ -374,7 +400,6 @@ impl DataFlowGraph { instruction_id: InstructionId, prev_value_id: ValueId, ) -> ValueId { - let typ = self.type_of_value(prev_value_id); let results = self.results.get_mut(&instruction_id).unwrap(); let res_position = results .iter() @@ -382,7 +407,6 @@ impl DataFlowGraph { .expect("Result id not found while replacing"); let value_id = self.values.insert(Value::Instruction { - typ, position: res_position, instruction: instruction_id, }); @@ -423,9 +447,9 @@ impl DataFlowGraph { pub(crate) fn get_numeric_constant_with_type( &self, value: ValueId, - ) -> Option<(FieldElement, Type)> { + ) -> Option<(FieldElement, NumericType)> { match &self.values[self.resolve(value)] { - Value::NumericConstant { constant, typ } => Some((*constant, typ.clone())), + Value::NumericConstant { constant, typ } => Some((*constant, *typ)), _ => None, } } @@ -632,8 +656,8 @@ mod tests { #[test] fn make_instruction() { let mut dfg = DataFlowGraph::default(); - let ins = Instruction::Allocate; - let ins_id = dfg.make_instruction(ins, Some(vec![Type::field()])); + let ins = Instruction::Allocate { element_type: Type::field() }; + let ins_id = dfg.make_instruction(ins); let results = dfg.instruction_results(ins_id); assert_eq!(results.len(), 1); diff --git a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index 6ebd2aa1105..f8c9df5f7a1 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -120,10 +120,6 @@ impl<'f> FunctionInserter<'f> { let results = self.function.dfg.instruction_results(id); let results = vecmap(results, |id| self.function.dfg.resolve(*id)); - let ctrl_typevars = instruction - .requires_ctrl_typevars() - .then(|| vecmap(&results, |result| self.function.dfg.type_of_value(*result))); - // Large arrays can lead to OOM panics if duplicated from being unrolled in loops. // To prevent this, try to reuse the same ID for identical arrays instead of inserting // another MakeArray instruction. Note that this assumes the function inserter is inserting @@ -151,7 +147,6 @@ impl<'f> FunctionInserter<'f> { let new_results = self.function.dfg.insert_instruction_and_results( instruction, block, - ctrl_typevars, call_stack, ); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 827b9be89df..03f02f16ba7 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -355,7 +355,7 @@ impl Instruction { } /// Returns the type that this instruction will return. - pub(crate) fn result_type(&self, position: usize) -> InstructionResultType { + pub(crate) fn result_type(&self) -> InstructionResultType { match self { Instruction::Binary(binary) => binary.result_type(), Instruction::Cast(_, typ) @@ -365,7 +365,7 @@ impl Instruction { InstructionResultType::Known(typ.clone()) } Instruction::Call { result_types, .. } => { - InstructionResultType::Known(result_types[position].clone()) + InstructionResultType::Multiple(result_types.clone()) } Instruction::Allocate { element_type } => { let typ = Type::Reference(Arc::new(element_type.clone())); @@ -870,8 +870,8 @@ impl Instruction { None } } - Instruction::Call { func, arguments, result_types: _ } => { - simplify_call(*func, arguments, dfg, block, call_stack) + Instruction::Call { func, arguments, result_types } => { + simplify_call(*func, arguments, result_types, dfg, block, call_stack) } Instruction::EnableSideEffectsIf { condition } => { if let Some(last) = dfg[block].instructions().last().copied() { diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 487370488b9..f1a37afa8be 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -88,12 +88,12 @@ impl Binary { pub(super) fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { let lhs = dfg.get_numeric_constant(self.lhs); let rhs = dfg.get_numeric_constant(self.rhs); - let operand_type = dfg.type_of_value(self.lhs); + let operand_type = dfg.type_of_value(self.lhs).unwrap_numeric(); if let (Some(lhs), Some(rhs)) = (lhs, rhs) { return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { Some((result, result_type)) => { - let value = dfg.make_constant(result, result_type); + let value = dfg.make_constant(result, result_type.unwrap_numeric()); SimplifyResult::SimplifiedTo(value) } None => SimplifyResult::None, @@ -168,11 +168,11 @@ impl Binary { } BinaryOp::Eq => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let one = dfg.make_constant(FieldElement::one(), Type::bool()); + let one = dfg.make_constant(FieldElement::one(), NumericType::bool()); return SimplifyResult::SimplifiedTo(one); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { return SimplifyResult::SimplifiedTo(self.rhs); @@ -191,13 +191,13 @@ impl Binary { } BinaryOp::Lt => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } if operand_type.is_unsigned() { if rhs_is_zero { // Unsigned values cannot be less than zero. - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), operand_type); return SimplifyResult::SimplifiedTo(zero); } else if rhs_is_one { let zero = dfg.make_constant(FieldElement::zero(), operand_type); @@ -217,7 +217,7 @@ impl Binary { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { return SimplifyResult::SimplifiedTo(self.lhs); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Boolean AND is equivalent to multiplication, which is a cheaper operation. let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); return SimplifyResult::SimplifiedToInstruction(instruction); @@ -294,10 +294,10 @@ fn eval_constant_binary_op( lhs: FieldElement, rhs: FieldElement, operator: BinaryOp, - mut operand_type: Type, -) -> Option<(FieldElement, Type)> { - let value = match &operand_type { - Type::Numeric(NumericType::NativeField) => { + mut operand_type: NumericType, +) -> Option<(FieldElement, NumericType)> { + let value = match operand_type { + NumericType::NativeField => { // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, // and the operation should be handled by ACIR generation. @@ -306,7 +306,7 @@ fn eval_constant_binary_op( } operator.get_field_function()?(lhs, rhs) } - Type::Numeric(NumericType::Unsigned { bit_size }) => { + NumericType::Unsigned { bit_size } => { let function = operator.get_u128_function(); let lhs = truncate(lhs.try_into_u128()?, *bit_size); @@ -327,7 +327,7 @@ fn eval_constant_binary_op( } result.into() } - Type::Numeric(NumericType::Signed { bit_size }) => { + NumericType::Signed { bit_size } => { let function = operator.get_i128_function(); let lhs = try_convert_field_element_to_signed_integer(lhs, *bit_size)?; @@ -349,11 +349,10 @@ fn eval_constant_binary_op( } convert_signed_integer_to_field_element(result, *bit_size) } - _ => return None, }; if matches!(operator, BinaryOp::Eq | BinaryOp::Lt) { - operand_type = Type::bool(); + operand_type = NumericType::bool(); } Some((value, operand_type)) diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 4e4f7e8aa62..32652c9239a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -30,6 +30,20 @@ impl NumericType { } } + pub(crate) fn length_type() -> NumericType { + NumericType::Unsigned { bit_size: SSA_WORD_SIZE } + } + + /// Creates the u1 type + pub(crate) fn bool() -> NumericType { + NumericType::Unsigned { bit_size: 1 } + } + + /// Creates the char type, represented as u8. + pub(crate) fn char() -> NumericType { + NumericType::Unsigned { bit_size: 8 } + } + /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. @@ -122,7 +136,15 @@ impl Type { /// Creates the type of an array's length. pub(crate) fn length_type() -> Type { - Type::unsigned(SSA_WORD_SIZE) + Type::Numeric(NumericType::length_type()) + } + + /// Unwrap the inner NumericType or panic + pub(crate) fn unwrap_numeric(&self) -> NumericType { + match self { + Type::Numeric(numeric) => *numeric, + other => panic!("Expected NumericType, found {other:?}"), + } } /// Returns the bit size of the provided numeric type. diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index 1d000e6172b..8cb654cc9dd 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -7,7 +7,7 @@ use super::{ function::FunctionId, instruction::{InstructionId, Intrinsic}, map::Id, - types::{NumericType, Type}, + types::NumericType, }; pub(crate) type ValueId = Id; diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index f93f63c1fbb..36d586a56cb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -96,16 +96,11 @@ impl Context { let _typ = function.dfg.type_of_value(value); let results = function.dfg.instruction_results(instruction); let result = results[0]; - // let result = match typ { - // Type::Array(..) => results[0], - // Type::Slice(..) => results[1], - // other => unreachable!("IfElse instructions should only have arrays or slices at this point. Found {other:?}"), - // }; function.dfg.set_value_from_id(result, value); self.array_set_conditionals.insert(result, current_conditional); } - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, result_types: _ } => { if let Value::Intrinsic(intrinsic) = function.dfg[*func] { let results = function.dfg.instruction_results(instruction); From 3af04a41b185965f03bd3d9563ea8a614219b037 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 11 Dec 2024 09:06:31 -0600 Subject: [PATCH 3/5] Get non-tests compiling --- compiler/noirc_evaluator/src/acir/mod.rs | 3 +- .../src/brillig/brillig_gen/brillig_block.rs | 22 ++-- .../src/ssa/function_builder/data_bus.rs | 16 ++- .../src/ssa/function_builder/mod.rs | 9 +- compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 27 ++--- .../noirc_evaluator/src/ssa/ir/instruction.rs | 11 +- .../src/ssa/ir/instruction/binary.rs | 41 ++++--- .../src/ssa/ir/instruction/call.rs | 69 +++++------- .../src/ssa/ir/instruction/call/blackbox.rs | 31 +++--- .../src/ssa/ir/instruction/cast.rs | 45 ++++---- .../src/ssa/ir/instruction/constrain.rs | 11 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 37 +++++++ compiler/noirc_evaluator/src/ssa/ir/value.rs | 19 ++-- .../src/ssa/opt/as_slice_length.rs | 4 +- .../src/ssa/opt/constant_folding.rs | 6 +- .../src/ssa/opt/defunctionalize.rs | 11 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 25 ++--- .../src/ssa/opt/flatten_cfg.rs | 33 ++---- .../src/ssa/opt/flatten_cfg/value_merger.rs | 35 +++--- .../src/ssa/opt/remove_bit_shifts.rs | 38 +++---- .../src/ssa/opt/remove_enable_side_effects.rs | 5 +- .../src/ssa/opt/remove_if_else.rs | 4 +- .../src/ssa/opt/resolve_is_unconstrained.rs | 10 +- .../src/ssa/ssa_gen/context.rs | 104 +++++++++--------- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 17 +-- 25 files changed, 320 insertions(+), 313 deletions(-) diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index cfba6ccf3a6..b496bc6a735 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -1872,7 +1872,8 @@ impl<'a> Context<'a> { let acir_value = match value { Value::NumericConstant { constant, typ } => { - AcirValue::Var(self.acir_context.add_constant(*constant), typ.into()) + let typ = AcirType::from(Type::Numeric(*typ)); + AcirValue::Var(self.acir_context.add_constant(*constant), typ) } Value::Intrinsic(..) => todo!(), Value::Function(function_id) => { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 7b4cdb2b8ce..56511127da8 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -226,16 +226,14 @@ impl<'block> BrilligBlock<'block> { dfg.get_numeric_constant_with_type(*rhs), ) { // If the constraint is of the form `x == u1 1` then we can simply constrain `x` directly - ( - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), - None, - ) if constant == FieldElement::one() => { + (Some((constant, NumericType::Unsigned { bit_size: 1 })), None) + if constant == FieldElement::one() => + { (self.convert_ssa_single_addr_value(*rhs, dfg), false) } - ( - None, - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), - ) if constant == FieldElement::one() => { + (None, Some((constant, NumericType::Unsigned { bit_size: 1 }))) + if constant == FieldElement::one() => + { (self.convert_ssa_single_addr_value(*lhs, dfg), false) } @@ -1285,8 +1283,8 @@ impl<'block> BrilligBlock<'block> { result_variable: SingleAddrVariable, ) { let binary_type = type_of_binary_operation( - dfg[binary.lhs].get_type(), - dfg[binary.rhs].get_type(), + dfg[binary.lhs].get_type().as_ref(), + dfg[binary.rhs].get_type().as_ref(), binary.operator, ); @@ -1795,7 +1793,7 @@ impl<'block> BrilligBlock<'block> { dfg: &DataFlowGraph, ) -> BrilligVariable { let typ = dfg[result].get_type(); - match typ { + match typ.as_ref() { Type::Numeric(_) => self.variables.define_variable( self.function_context, self.brillig_context, @@ -1811,7 +1809,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.allocate_foreign_call_result_array(typ, array); + self.allocate_foreign_call_result_array(typ.as_ref(), array); variable } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index bd2585a3bfa..1d18683ee9e 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -1,6 +1,10 @@ use std::{collections::BTreeMap, sync::Arc}; -use crate::ssa::ir::{function::RuntimeType, types::Type, value::ValueId}; +use crate::ssa::ir::{ + function::RuntimeType, + types::{NumericType, Type}, + value::ValueId, +}; use acvm::FieldElement; use fxhash::FxHashMap as HashMap; use noirc_frontend::ast; @@ -115,7 +119,7 @@ impl FunctionBuilder { /// Insert a value into a data bus builder fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) { assert!(databus.databus.is_none(), "initializing finalized call data"); - let typ = self.current_function.dfg[value].get_type().clone(); + let typ = self.current_function.dfg[value].get_type().into_owned(); match typ { Type::Numeric(_) => { databus.values.push_back(value); @@ -128,10 +132,10 @@ impl FunctionBuilder { for _i in 0..len { for subitem_typ in typ.iter() { // load each element of the array, and add it to the databus - let index_var = self - .current_function - .dfg - .make_constant(FieldElement::from(index as i128), Type::length_type()); + let length_type = NumericType::length_type(); + let index_var = FieldElement::from(index as i128); + let index_var = + self.current_function.dfg.make_constant(index_var, length_type); let element = self.insert_array_get(value, index_var, subitem_typ.clone()); index += match subitem_typ { Type::Array(_, _) | Type::Slice(_) => subitem_typ.element_size(), diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 0ae61404442..7dc9671381e 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -21,6 +21,7 @@ use super::{ dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, instruction::{ConstrainError, InstructionId, Intrinsic}, + types::NumericType, }, ssa_gen::Ssa, }; @@ -122,19 +123,19 @@ impl FunctionBuilder { pub(crate) fn numeric_constant( &mut self, value: impl Into, - typ: Type, + typ: NumericType, ) -> ValueId { self.current_function.dfg.make_constant(value.into(), typ) } /// Insert a numeric constant into the current function of type Field pub(crate) fn field_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::field()) + self.numeric_constant(value.into(), NumericType::NativeField) } /// Insert a numeric constant into the current function of type Type::length_type() pub(crate) fn length_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::length_type()) + self.numeric_constant(value.into(), NumericType::length_type()) } /// Returns the type of the given value. @@ -251,7 +252,7 @@ impl FunctionBuilder { /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. - pub(crate) fn insert_cast(&mut self, value: ValueId, typ: Type) -> ValueId { + pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { self.insert_instruction(Instruction::Cast(value, typ), None).first() } diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 827944e22d1..94074eb3854 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -9,7 +9,7 @@ use super::{ Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction, }, map::DenseMap, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }; @@ -50,7 +50,7 @@ pub(crate) struct DataFlowGraph { /// Each constant is unique, attempting to insert the same constant /// twice will return the same ValueId. #[serde(skip)] - constants: HashMap<(FieldElement, Type), ValueId>, + constants: HashMap<(FieldElement, NumericType), ValueId>, /// Contains each function that has been imported into the current function. /// A unique `ValueId` for each function's [`Value::Function`] is stored so any given FunctionId @@ -119,7 +119,7 @@ impl DataFlowGraph { let parameters = self.blocks[block].parameters(); let parameters = vecmap(parameters.iter().enumerate(), |(position, param)| { - let typ = self.values[*param].get_type().clone(); + let typ = self.values[*param].get_type().into_owned(); self.values.insert(Value::Param { block: new_block, position, typ }) }); @@ -233,11 +233,12 @@ impl DataFlowGraph { pub(crate) fn set_type_of_value(&mut self, value_id: ValueId, target_type: Type) { let value = &mut self.values[value_id]; match value { - Value::Instruction { typ, .. } - | Value::Param { typ, .. } - | Value::NumericConstant { typ, .. } => { + Value::Instruction { typ, .. } | Value::Param { typ, .. } => { *typ = target_type; } + Value::NumericConstant { typ, .. } => { + *typ = target_type.unwrap_numeric(); + } _ => { unreachable!("ICE: Cannot set type of {:?}", value); } @@ -257,11 +258,11 @@ impl DataFlowGraph { /// Creates a new constant value, or returns the Id to an existing one if /// one already exists. - pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: Type) -> ValueId { - if let Some(id) = self.constants.get(&(constant, typ.clone())) { + pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: NumericType) -> ValueId { + if let Some(id) = self.constants.get(&(constant, typ)) { return *id; } - let id = self.values.insert(Value::NumericConstant { constant, typ: typ.clone() }); + let id = self.values.insert(Value::NumericConstant { constant, typ }); self.constants.insert((constant, typ), id); id } @@ -342,7 +343,7 @@ impl DataFlowGraph { /// Returns the type of a given value pub(crate) fn type_of_value(&self, value: ValueId) -> Type { - self.values[value].get_type().clone() + self.values[value].get_type().into_owned() } /// Returns the maximum possible number of bits that `value` can potentially be. @@ -367,7 +368,7 @@ impl DataFlowGraph { /// True if the type of this value is Type::Reference. /// Using this method over type_of_value avoids cloning the value's type. pub(crate) fn value_is_reference(&self, value: ValueId) -> bool { - matches!(self.values[value].get_type(), Type::Reference(_)) + matches!(self.values[value].get_type().as_ref(), Type::Reference(_)) } /// Replaces an instruction result with a fresh id. @@ -425,9 +426,9 @@ impl DataFlowGraph { pub(crate) fn get_numeric_constant_with_type( &self, value: ValueId, - ) -> Option<(FieldElement, Type)> { + ) -> Option<(FieldElement, NumericType)> { match &self.values[self.resolve(value)] { - Value::NumericConstant { constant, typ } => Some((*constant, typ.clone())), + Value::NumericConstant { constant, typ } => Some((*constant, *typ)), _ => None, } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index ba212fdad96..3bde8786dda 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -252,8 +252,8 @@ pub(crate) enum Instruction { /// Binary Operations like +, -, *, /, ==, != Binary(Binary), - /// Converts `Value` into Typ - Cast(ValueId, Type), + /// Converts `Value` into the given NumericType + Cast(ValueId, NumericType), /// Computes a bit wise not Not(ValueId), @@ -355,9 +355,8 @@ impl Instruction { pub(crate) fn result_type(&self) -> InstructionResultType { match self { Instruction::Binary(binary) => binary.result_type(), - Instruction::Cast(_, typ) | Instruction::MakeArray { typ, .. } => { - InstructionResultType::Known(typ.clone()) - } + Instruction::Cast(_, typ) => InstructionResultType::Known(Type::Numeric(*typ)), + Instruction::MakeArray { typ, .. } => InstructionResultType::Known(typ.clone()), Instruction::Not(value) | Instruction::Truncate { value, .. } | Instruction::ArraySet { array: value, .. } @@ -751,7 +750,7 @@ impl Instruction { use SimplifyResult::*; match self { Instruction::Binary(binary) => binary.simplify(dfg), - Instruction::Cast(value, typ) => simplify_cast(*value, typ, dfg), + Instruction::Cast(value, typ) => simplify_cast(*value, *typ, dfg), Instruction::Not(value) => { match &dfg[dfg.resolve(*value)] { // Limit optimizing ! on constants to only booleans. If we tried it on fields, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 487370488b9..0f52168a30d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -88,7 +88,7 @@ impl Binary { pub(super) fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { let lhs = dfg.get_numeric_constant(self.lhs); let rhs = dfg.get_numeric_constant(self.rhs); - let operand_type = dfg.type_of_value(self.lhs); + let operand_type = dfg.type_of_value(self.lhs).unwrap_numeric(); if let (Some(lhs), Some(rhs)) = (lhs, rhs) { return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { @@ -168,11 +168,11 @@ impl Binary { } BinaryOp::Eq => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let one = dfg.make_constant(FieldElement::one(), Type::bool()); + let one = dfg.make_constant(FieldElement::one(), NumericType::bool()); return SimplifyResult::SimplifiedTo(one); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { return SimplifyResult::SimplifiedTo(self.rhs); @@ -191,13 +191,13 @@ impl Binary { } BinaryOp::Lt => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } if operand_type.is_unsigned() { if rhs_is_zero { // Unsigned values cannot be less than zero. - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } else if rhs_is_one { let zero = dfg.make_constant(FieldElement::zero(), operand_type); @@ -217,7 +217,7 @@ impl Binary { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { return SimplifyResult::SimplifiedTo(self.lhs); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Boolean AND is equivalent to multiplication, which is a cheaper operation. let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); return SimplifyResult::SimplifiedToInstruction(instruction); @@ -294,10 +294,10 @@ fn eval_constant_binary_op( lhs: FieldElement, rhs: FieldElement, operator: BinaryOp, - mut operand_type: Type, -) -> Option<(FieldElement, Type)> { - let value = match &operand_type { - Type::Numeric(NumericType::NativeField) => { + mut operand_type: NumericType, +) -> Option<(FieldElement, NumericType)> { + let value = match operand_type { + NumericType::NativeField => { // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, // and the operation should be handled by ACIR generation. @@ -306,11 +306,11 @@ fn eval_constant_binary_op( } operator.get_field_function()?(lhs, rhs) } - Type::Numeric(NumericType::Unsigned { bit_size }) => { + NumericType::Unsigned { bit_size } => { let function = operator.get_u128_function(); - let lhs = truncate(lhs.try_into_u128()?, *bit_size); - let rhs = truncate(rhs.try_into_u128()?, *bit_size); + let lhs = truncate(lhs.try_into_u128()?, bit_size); + let rhs = truncate(rhs.try_into_u128()?, bit_size); // The divisor is being truncated into the type of the operand, which can potentially // lead to the rhs being zero. @@ -322,16 +322,16 @@ fn eval_constant_binary_op( } let result = function(lhs, rhs)?; // Check for overflow - if result >= 1 << *bit_size { + if result >= 1 << bit_size { return None; } result.into() } - Type::Numeric(NumericType::Signed { bit_size }) => { + NumericType::Signed { bit_size } => { let function = operator.get_i128_function(); - let lhs = try_convert_field_element_to_signed_integer(lhs, *bit_size)?; - let rhs = try_convert_field_element_to_signed_integer(rhs, *bit_size)?; + let lhs = try_convert_field_element_to_signed_integer(lhs, bit_size)?; + let rhs = try_convert_field_element_to_signed_integer(rhs, bit_size)?; // The divisor is being truncated into the type of the operand, which can potentially // lead to the rhs being zero. // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. @@ -343,17 +343,16 @@ fn eval_constant_binary_op( let result = function(lhs, rhs)?; // Check for overflow - let two_pow_bit_size_minus_one = 1i128 << (*bit_size - 1); + let two_pow_bit_size_minus_one = 1i128 << (bit_size - 1); if result >= two_pow_bit_size_minus_one || result < -two_pow_bit_size_minus_one { return None; } - convert_signed_integer_to_field_element(result, *bit_size) + convert_signed_integer_to_field_element(result, bit_size) } - _ => return None, }; if matches!(operator, BinaryOp::Eq | BinaryOp::Lt) { - operand_type = Type::bool(); + operand_type = NumericType::bool(); } Some((value, operand_type)) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 40dd0bca41a..1daa1af7907 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -15,7 +15,7 @@ use crate::ssa::{ dfg::{CallStack, DataFlowGraph}, instruction::Intrinsic, map::Id, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, opt::flatten_cfg::value_merger::ValueMerger, @@ -61,7 +61,13 @@ pub(super) fn simplify_call( unreachable!("ICE: Intrinsic::ToRadix return type must be array") }; constant_to_radix(endian, field, 2, limb_count, |values| { - make_constant_array(dfg, values.into_iter(), Type::bool(), block, call_stack) + make_constant_array( + dfg, + values.into_iter(), + NumericType::bool(), + block, + call_stack, + ) }) } else { SimplifyResult::None @@ -81,7 +87,7 @@ pub(super) fn simplify_call( make_constant_array( dfg, values.into_iter(), - Type::unsigned(8), + NumericType::Unsigned { bit_size: 8 }, block, call_stack, ) @@ -93,7 +99,7 @@ pub(super) fn simplify_call( Intrinsic::ArrayLen => { if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); - SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::length_type())) + SimplifyResult::SimplifiedTo(dfg.make_constant(length, NumericType::length_type())) } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { SimplifyResult::SimplifiedTo(arguments[0]) } else { @@ -116,7 +122,7 @@ pub(super) fn simplify_call( ); let slice_length_value = array.len() / elements_size; let slice_length = - dfg.make_constant(slice_length_value.into(), Type::length_type()); + dfg.make_constant(slice_length_value.into(), NumericType::length_type()); let new_slice = make_array(dfg, array, Type::Slice(inner_element_types), block, call_stack); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) @@ -331,10 +337,7 @@ pub(super) fn simplify_call( simplify_black_box_func(bb_func, arguments, dfg, block, call_stack) } Intrinsic::AsField => { - let instruction = Instruction::Cast( - arguments[0], - Type::Numeric(crate::ssa::ir::types::NumericType::NativeField), - ); + let instruction = Instruction::Cast(arguments[0], NumericType::NativeField); SimplifyResult::SimplifiedToInstruction(instruction) } Intrinsic::FromField => { @@ -355,7 +358,7 @@ pub(super) fn simplify_call( ) .first(); - let instruction = Instruction::Cast(truncated_value, target_type); + let instruction = Instruction::Cast(truncated_value, target_type.unwrap_numeric()); SimplifyResult::SimplifiedToInstruction(instruction) } Intrinsic::AsWitness => SimplifyResult::None, @@ -371,7 +374,7 @@ pub(super) fn simplify_call( if let Some(constants) = constant_args { let lhs = constants[0]; let rhs = constants[1]; - let result = dfg.make_constant((lhs < rhs).into(), Type::bool()); + let result = dfg.make_constant((lhs < rhs).into(), NumericType::bool()); SimplifyResult::SimplifiedTo(result) } else { SimplifyResult::None @@ -407,7 +410,7 @@ fn update_slice_length( operator: BinaryOp, block: BasicBlockId, ) -> ValueId { - let one = dfg.make_constant(FieldElement::one(), Type::length_type()); + let one = dfg.make_constant(FieldElement::one(), NumericType::length_type()); let instruction = Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }); let call_stack = dfg.get_value_call_stack(slice_len); dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() @@ -422,7 +425,7 @@ fn simplify_slice_push_back( call_stack: CallStack, ) -> SimplifyResult { // The capacity must be an integer so that we can compare it against the slice length - let capacity = dfg.make_constant((slice.len() as u128).into(), Type::length_type()); + let capacity = dfg.make_constant((slice.len() as u128).into(), NumericType::length_type()); let len_equals_capacity_instr = Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Eq, rhs: capacity }); let len_equals_capacity = dfg @@ -495,7 +498,8 @@ fn simplify_slice_pop_back( let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); - let element_size = dfg.make_constant((element_count as u128).into(), Type::length_type()); + let element_size = + dfg.make_constant((element_count as u128).into(), NumericType::length_type()); let flattened_len_instr = Instruction::binary(BinaryOp::Mul, arguments[0], element_size); let mut flattened_len = dfg .insert_instruction_and_results(flattened_len_instr, block, None, call_stack.clone()) @@ -569,7 +573,7 @@ fn simplify_black_box_func( let result_array = make_constant_array( dfg, state_values, - Type::unsigned(64), + NumericType::Unsigned { bit_size: 64 }, block, call_stack, ); @@ -629,14 +633,14 @@ fn simplify_black_box_func( fn make_constant_array( dfg: &mut DataFlowGraph, results: impl Iterator, - typ: Type, + typ: NumericType, block: BasicBlockId, call_stack: &CallStack, ) -> ValueId { let result_constants: im::Vector<_> = - results.map(|element| dfg.make_constant(element, typ.clone())).collect(); + results.map(|element| dfg.make_constant(element, typ)).collect(); - let typ = Type::Array(Arc::new(vec![typ]), result_constants.len() as u32); + let typ = Type::Array(Arc::new(vec![Type::Numeric(typ)]), result_constants.len() as u32); make_array(dfg, result_constants, typ, block, call_stack) } @@ -652,23 +656,6 @@ fn make_array( dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } -fn make_constant_slice( - dfg: &mut DataFlowGraph, - results: Vec, - typ: Type, - block: BasicBlockId, - call_stack: &CallStack, -) -> (ValueId, ValueId) { - let result_constants = vecmap(results, |element| dfg.make_constant(element, typ.clone())); - - let typ = Type::Slice(Arc::new(vec![typ])); - let length = FieldElement::from(result_constants.len() as u128); - let length = dfg.make_constant(length, Type::length_type()); - - let slice = make_array(dfg, result_constants.into(), typ, block, call_stack); - (length, slice) -} - /// Returns a slice (represented by a tuple (len, slice)) of constants corresponding to the limbs of the radix decomposition. fn constant_to_radix( endian: Endian, @@ -733,8 +720,8 @@ fn simplify_hash( let hash_values = hash.iter().map(|byte| FieldElement::from_be_bytes_reduce(&[*byte])); - let result_array = - make_constant_array(dfg, hash_values, Type::unsigned(8), block, call_stack); + let u8_type = NumericType::Unsigned { bit_size: 8 }; + let result_array = make_constant_array(dfg, hash_values, u8_type, block, call_stack); SimplifyResult::SimplifiedTo(result_array) } _ => SimplifyResult::None, @@ -782,7 +769,7 @@ fn simplify_signature( signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature) .expect("Rust solvable black box function should not fail"); - let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + let valid_signature = dfg.make_constant(valid_signature.into(), NumericType::bool()); SimplifyResult::SimplifiedTo(valid_signature) } _ => SimplifyResult::None, @@ -812,15 +799,15 @@ fn simplify_derive_generators( num_generators, starting_index.try_to_u32().expect("argument is declared as u32"), ); - let is_infinite = dfg.make_constant(FieldElement::zero(), Type::bool()); + let is_infinite = dfg.make_constant(FieldElement::zero(), NumericType::bool()); let mut results = Vec::new(); for gen in generators { let x_big: BigUint = gen.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); let y_big: BigUint = gen.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); - results.push(dfg.make_constant(x, Type::field())); - results.push(dfg.make_constant(y, Type::field())); + results.push(dfg.make_constant(x, NumericType::NativeField)); + results.push(dfg.make_constant(y, NumericType::NativeField)); results.push(is_infinite); } let len = results.len() as u32; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index 016d7ffa25b..c58264dbe84 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::ssa::ir::instruction::BlackBoxFunc; +use crate::ssa::ir::types::NumericType; use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::{CallStack, DataFlowGraph}, @@ -47,9 +48,10 @@ pub(super) fn simplify_ec_add( return SimplifyResult::None; }; - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::field()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = + dfg.make_constant(result_is_infinity, NumericType::NativeField); let typ = Type::Array(Arc::new(vec![Type::field()]), 3); @@ -142,9 +144,10 @@ pub(super) fn simplify_msm( // If there are no variable term, we can directly return the constant result if var_scalars.is_empty() { - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::field()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = + dfg.make_constant(result_is_infinity, NumericType::NativeField); let elements = im::vector![result_x, result_y, result_is_infinity]; let typ = Type::Array(Arc::new(vec![Type::field()]), 3); @@ -163,14 +166,14 @@ pub(super) fn simplify_msm( return SimplifyResult::None; } // Add the constant part back to the non-constant part, if it is not null - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let zero = dfg.make_constant(FieldElement::zero(), Type::field()); + let one = dfg.make_constant(FieldElement::one(), NumericType::NativeField); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::NativeField); if result_is_infinity.is_zero() { var_scalars.push(one); var_scalars.push(zero); - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::bool()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = dfg.make_constant(result_is_infinity, NumericType::bool()); var_points.push(result_x); var_points.push(result_y); var_points.push(result_is_infinity); @@ -221,7 +224,7 @@ pub(super) fn simplify_poseidon2_permutation( }; let new_state = new_state.into_iter(); - let typ = Type::field(); + let typ = NumericType::NativeField; let result_array = make_constant_array(dfg, new_state, typ, block, call_stack); SimplifyResult::SimplifiedTo(result_array) @@ -246,7 +249,7 @@ pub(super) fn simplify_hash( let hash_values = hash.iter().map(|byte| FieldElement::from_be_bytes_reduce(&[*byte])); - let u8_type = Type::unsigned(8); + let u8_type = NumericType::Unsigned { bit_size: 8 }; let result_array = make_constant_array(dfg, hash_values, u8_type, block, call_stack); SimplifyResult::SimplifiedTo(result_array) } @@ -296,7 +299,7 @@ pub(super) fn simplify_signature( signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature) .expect("Rust solvable black box function should not fail"); - let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + let valid_signature = dfg.make_constant(valid_signature.into(), NumericType::bool()); SimplifyResult::SimplifiedTo(valid_signature) } _ => SimplifyResult::None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs index ed588def1d7..ee2ab43aa5d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -7,7 +7,7 @@ use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value /// that value is returned. Otherwise None is returned. pub(super) fn simplify_cast( value: ValueId, - dst_typ: &Type, + dst_typ: NumericType, dfg: &mut DataFlowGraph, ) -> SimplifyResult { use SimplifyResult::*; @@ -15,60 +15,55 @@ pub(super) fn simplify_cast( if let Value::Instruction { instruction, .. } = &dfg[value] { if let Instruction::Cast(original_value, _) = &dfg[*instruction] { - return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ.clone())); + return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ)); } } if let Some(constant) = dfg.get_numeric_constant(value) { - let src_typ = dfg.type_of_value(value); + let src_typ = dfg.type_of_value(value).unwrap_numeric(); match (src_typ, dst_typ) { - (Type::Numeric(NumericType::NativeField), Type::Numeric(NumericType::NativeField)) => { + (NumericType::NativeField, NumericType::NativeField) => { // Field -> Field: use src value SimplifiedTo(value) } ( - Type::Numeric(NumericType::Unsigned { .. } | NumericType::Signed { .. }), - Type::Numeric(NumericType::NativeField), + NumericType::Unsigned { .. } | NumericType::Signed { .. }, + NumericType::NativeField, ) => { // Unsigned/Signed -> Field: redefine same constant as Field - SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(constant, dst_typ)) } ( - Type::Numeric( - NumericType::NativeField - | NumericType::Unsigned { .. } - | NumericType::Signed { .. }, - ), - Type::Numeric(NumericType::Unsigned { bit_size }), + NumericType::NativeField + | NumericType::Unsigned { .. } + | NumericType::Signed { .. }, + NumericType::Unsigned { bit_size }, ) => { // Field/Unsigned -> unsigned: truncate - let integer_modulus = BigUint::from(2u128).pow(*bit_size); + let integer_modulus = BigUint::from(2u128).pow(bit_size); let constant: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); let truncated = constant % integer_modulus; let truncated = FieldElement::from_be_bytes_reduce(&truncated.to_bytes_be()); - SimplifiedTo(dfg.make_constant(truncated, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(truncated, dst_typ)) } ( - Type::Numeric( - NumericType::NativeField - | NumericType::Unsigned { .. } - | NumericType::Signed { .. }, - ), - Type::Numeric(NumericType::Signed { bit_size }), + NumericType::NativeField + | NumericType::Unsigned { .. } + | NumericType::Signed { .. }, + NumericType::Signed { bit_size }, ) => { // Field/Unsigned -> signed // We only simplify to signed when we are below the maximum signed integer of the destination type. - let integer_modulus = BigUint::from(2u128).pow(*bit_size - 1); + let integer_modulus = BigUint::from(2u128).pow(bit_size - 1); let constant_uint: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); if constant_uint < integer_modulus { - SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(constant, dst_typ)) } else { None } } - _ => None, } - } else if *dst_typ == dfg.type_of_value(value) { + } else if Type::Numeric(dst_typ) == dfg.type_of_value(value) { SimplifiedTo(value) } else { None diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 66f50440d64..5ae6a642a57 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,5 +1,7 @@ use acvm::{acir::AcirField, FieldElement}; +use crate::ssa::ir::types::NumericType; + use super::{Binary, BinaryOp, ConstrainError, DataFlowGraph, Instruction, Type, Value, ValueId}; /// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains @@ -20,7 +22,7 @@ pub(super) fn decompose_constrain( match (&dfg[lhs], &dfg[rhs]) { (Value::NumericConstant { constant, typ }, Value::Instruction { instruction, .. }) | (Value::Instruction { instruction, .. }, Value::NumericConstant { constant, typ }) - if *typ == Type::bool() => + if *typ == NumericType::bool() => { match dfg[*instruction] { Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Eq }) @@ -61,7 +63,7 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let one = FieldElement::one(); - let one = dfg.make_constant(one, Type::bool()); + let one = dfg.make_constant(one, NumericType::bool()); [ decompose_constrain(lhs, one, msg, dfg), @@ -89,7 +91,7 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let zero = FieldElement::zero(); - let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); + let zero = dfg.make_constant(zero, dfg.type_of_value(lhs).unwrap_numeric()); [ decompose_constrain(lhs, zero, msg, dfg), @@ -112,7 +114,8 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let reversed_constant = FieldElement::from(!constant.is_one()); - let reversed_constant = dfg.make_constant(reversed_constant, Type::bool()); + let reversed_constant = + dfg.make_constant(reversed_constant, NumericType::bool()); decompose_constrain(value, reversed_constant, msg, dfg) } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 4e4f7e8aa62..0dd7fd92ee5 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -30,6 +30,31 @@ impl NumericType { } } + /// Creates a NumericType::Signed type + pub(crate) fn signed(bit_size: u32) -> NumericType { + NumericType::Signed { bit_size } + } + + /// Creates a NumericType::Unsigned type + pub(crate) fn unsigned(bit_size: u32) -> NumericType { + NumericType::Unsigned { bit_size } + } + + /// Creates the u1 type + pub(crate) fn bool() -> NumericType { + NumericType::Unsigned { bit_size: 1 } + } + + /// Creates the char type, represented as u8. + pub(crate) fn char() -> NumericType { + NumericType::Unsigned { bit_size: 8 } + } + + /// Creates the type of an array's length. + pub(crate) fn length_type() -> NumericType { + NumericType::Unsigned { bit_size: SSA_WORD_SIZE } + } + /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. @@ -63,6 +88,10 @@ impl NumericType { NumericType::NativeField => None, } } + + pub(crate) fn is_unsigned(&self) -> bool { + matches!(self, NumericType::Unsigned { .. }) + } } /// All types representable in the IR. @@ -125,6 +154,14 @@ impl Type { Type::unsigned(SSA_WORD_SIZE) } + /// Returns the inner NumericType if this is one, or panics otherwise + pub(crate) fn unwrap_numeric(&self) -> NumericType { + match self { + Type::Numeric(numeric) => *numeric, + other => panic!("Expected NumericType, found {other}"), + } + } + /// Returns the bit size of the provided numeric type. /// /// # Panics diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index ef494200308..ec7a8e25246 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use acvm::FieldElement; use serde::{Deserialize, Serialize}; @@ -7,7 +9,7 @@ use super::{ function::FunctionId, instruction::{InstructionId, Intrinsic}, map::Id, - types::Type, + types::{NumericType, Type}, }; pub(crate) type ValueId = Id; @@ -34,7 +36,7 @@ pub(crate) enum Value { Param { block: BasicBlockId, position: usize, typ: Type }, /// This Value originates from a numeric constant - NumericConstant { constant: FieldElement, typ: Type }, + NumericConstant { constant: FieldElement, typ: NumericType }, /// This Value refers to a function in the IR. /// Functions always have the type Type::Function. @@ -55,14 +57,13 @@ pub(crate) enum Value { impl Value { /// Retrieves the type of this Value - pub(crate) fn get_type(&self) -> &Type { + pub(crate) fn get_type(&self) -> Cow { match self { - Value::Instruction { typ, .. } => typ, - Value::Param { typ, .. } => typ, - Value::NumericConstant { typ, .. } => typ, - Value::Function { .. } => &Type::Function, - Value::Intrinsic { .. } => &Type::Function, - Value::ForeignFunction { .. } => &Type::Function, + Value::Instruction { typ, .. } | Value::Param { typ, .. } => Cow::Borrowed(typ), + Value::NumericConstant { typ, .. } => Cow::Owned(Type::Numeric(*typ)), + Value::Function { .. } | Value::Intrinsic { .. } | Value::ForeignFunction { .. } => { + Cow::Owned(Type::Function) + } } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs index 75cdea349b7..c6cdffd3bc3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -2,7 +2,7 @@ use crate::ssa::{ ir::{ function::Function, instruction::{Instruction, InstructionId, Intrinsic}, - types::Type, + types::{NumericType, Type}, value::Value, }, ssa_gen::Ssa, @@ -71,7 +71,7 @@ fn replace_known_slice_lengths( // This isn't strictly necessary as a new result will be defined the next time for which the instruction // is reinserted but this avoids leaving the program in an invalid state. func.dfg.replace_result(instruction_id, original_slice_length); - let known_length = func.dfg.make_constant(known_length.into(), Type::length_type()); + let known_length = func.dfg.make_constant(known_length.into(), NumericType::length_type()); func.dfg.set_value_from_id(original_slice_length, known_length); }); } diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 56029a8fbd4..e3216654c05 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -43,7 +43,7 @@ use crate::{ dom::DominatorTree, function::{Function, FunctionId, RuntimeType}, instruction::{Instruction, InstructionId}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, ssa_gen::Ssa, @@ -276,7 +276,7 @@ impl<'brillig> Context<'brillig> { // Default side effect condition variable with an enabled state. let mut side_effects_enabled_var = - function.dfg.make_constant(FieldElement::one(), Type::bool()); + function.dfg.make_constant(FieldElement::one(), NumericType::bool()); for instruction_id in instructions { self.fold_constants_into_instruction( @@ -657,7 +657,7 @@ impl<'brillig> Context<'brillig> { dfg: &mut DataFlowGraph, ) -> ValueId { match typ { - Type::Numeric(_) => { + Type::Numeric(typ) => { let memory = memory_values[*memory_index]; *memory_index += 1; diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index cfeb8751f25..ded1f52d529 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -130,13 +130,14 @@ impl DefunctionalizationContext { // Change the type of all the values that are not call targets to NativeField let value_ids = vecmap(func.dfg.values_iter(), |(id, _)| id); for value_id in value_ids { - if let Type::Function = &func.dfg[value_id].get_type() { + if let Type::Function = func.dfg[value_id].get_type().as_ref() { match &func.dfg[value_id] { // If the value is a static function, transform it to the function id Value::Function(id) => { if !call_target_values.contains(&value_id) { + let field = NumericType::NativeField; let new_value = - func.dfg.make_constant(function_id_to_field(*id), Type::field()); + func.dfg.make_constant(function_id_to_field(*id), field); func.dfg.set_value_from_id(value_id, new_value); } } @@ -287,10 +288,8 @@ fn create_apply_function( let is_last = index == function_ids.len() - 1; let mut next_function_block = None; - let function_id_constant = function_builder.numeric_constant( - function_id_to_field(*function_id), - Type::Numeric(NumericType::NativeField), - ); + let function_id_constant = function_builder + .numeric_constant(function_id_to_field(*function_id), NumericType::NativeField); // If it's not the last function to dispatch, create an if statement if !is_last { diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index f7ac6f7b313..0cbf7d20cb1 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -12,10 +12,10 @@ use crate::ssa::{ function::Function, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic}, post_order::PostOrder, - types::Type, + types::NumericType, value::{Value, ValueId}, }, - ssa_gen::{Ssa, SSA_WORD_SIZE}, + ssa_gen::Ssa, }; impl Ssa { @@ -285,25 +285,25 @@ impl Context { let (lhs, rhs) = if function.dfg.get_numeric_constant(*index).is_some() { // If we are here it means the index is known but out of bounds. That's always an error! - let false_const = function.dfg.make_constant(false.into(), Type::bool()); - let true_const = function.dfg.make_constant(true.into(), Type::bool()); + let false_const = function.dfg.make_constant(false.into(), NumericType::bool()); + let true_const = function.dfg.make_constant(true.into(), NumericType::bool()); (false_const, true_const) } else { // `index` will be relative to the flattened array length, so we need to take that into account let array_length = function.dfg.type_of_value(*array).flattened_size(); // If we are here it means the index is dynamic, so let's add a check that it's less than length + let length_type = NumericType::length_type(); let index = function.dfg.insert_instruction_and_results( - Instruction::Cast(*index, Type::unsigned(SSA_WORD_SIZE)), + Instruction::Cast(*index, length_type), block_id, None, call_stack.clone(), ); let index = index.first(); - let array_typ = Type::unsigned(SSA_WORD_SIZE); let array_length = - function.dfg.make_constant((array_length as u128).into(), array_typ); + function.dfg.make_constant((array_length as u128).into(), length_type); let is_index_out_of_bounds = function.dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Lt, index, array_length), block_id, @@ -311,7 +311,7 @@ impl Context { call_stack.clone(), ); let is_index_out_of_bounds = is_index_out_of_bounds.first(); - let true_const = function.dfg.make_constant(true.into(), Type::bool()); + let true_const = function.dfg.make_constant(true.into(), NumericType::bool()); (is_index_out_of_bounds, true_const) }; @@ -495,12 +495,9 @@ fn apply_side_effects( // Condition needs to be cast to argument type in order to multiply them together. // In our case, lhs is always a boolean. - let casted_condition = dfg.insert_instruction_and_results( - Instruction::Cast(condition, Type::bool()), - block_id, - None, - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, NumericType::bool()); + let casted_condition = + dfg.insert_instruction_and_results(cast, block_id, None, call_stack.clone()); let casted_condition = casted_condition.first(); let lhs = dfg.insert_instruction_and_results( diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 3fbccf93ec9..dc7952979e5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -144,7 +144,7 @@ use crate::ssa::{ function::{Function, FunctionId, RuntimeType}, function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, ssa_gen::Ssa, @@ -332,11 +332,8 @@ impl<'f> Context<'f> { for instruction in instructions.iter() { if self.is_no_predicate(no_predicates, instruction) { // disable side effect for no_predicate functions - let one = self - .inserter - .function - .dfg - .make_constant(FieldElement::one(), Type::unsigned(1)); + let bool_type = NumericType::bool(); + let one = self.inserter.function.dfg.make_constant(FieldElement::one(), bool_type); self.insert_instruction_with_typevars( Instruction::EnableSideEffectsIf { condition: one }, None, @@ -540,7 +537,7 @@ impl<'f> Context<'f> { let else_condition = if let Some(branch) = cond_context.else_branch { branch.condition } else { - self.inserter.function.dfg.make_constant(FieldElement::zero(), Type::bool()) + self.inserter.function.dfg.make_constant(FieldElement::zero(), NumericType::bool()) }; let block = self.inserter.function.entry_block(); @@ -606,7 +603,7 @@ impl<'f> Context<'f> { let condition = match self.get_last_condition() { Some(cond) => cond, None => { - self.inserter.function.dfg.make_constant(FieldElement::one(), Type::unsigned(1)) + self.inserter.function.dfg.make_constant(FieldElement::one(), NumericType::bool()) } }; let enable_side_effects = Instruction::EnableSideEffectsIf { condition }; @@ -653,13 +650,9 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(lhs); - // Sanity check that we're not constraining non-primitive types - assert!(matches!(argument_type, Type::Numeric(_))); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let lhs = self.insert_instruction( Instruction::binary(BinaryOp::Mul, lhs, casted_condition), @@ -708,10 +701,8 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(value); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let value = self.insert_instruction( Instruction::binary(BinaryOp::Mul, value, casted_condition), @@ -725,10 +716,8 @@ impl<'f> Context<'f> { let field = arguments[0]; let argument_type = self.inserter.function.dfg.type_of_value(field); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let field = self.insert_instruction( Instruction::binary(BinaryOp::Mul, field, casted_condition), call_stack.clone(), diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 6ea235b9414..c2b071a9c9a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -5,7 +5,7 @@ use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::{CallStack, DataFlowGraph, InsertInstructionResult}, instruction::{BinaryOp, Instruction}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }; @@ -95,8 +95,8 @@ impl<'a> ValueMerger<'a> { then_value: ValueId, else_value: ValueId, ) -> ValueId { - let then_type = dfg.type_of_value(then_value); - let else_type = dfg.type_of_value(else_value); + let then_type = dfg.type_of_value(then_value).unwrap_numeric(); + let else_type = dfg.type_of_value(else_value).unwrap_numeric(); assert_eq!( then_type, else_type, "Expected values merged to be of the same type but found {then_type} and {else_type}" @@ -112,22 +112,13 @@ impl<'a> ValueMerger<'a> { let call_stack = if then_call_stack.is_empty() { else_call_stack } else { then_call_stack }; // We must cast the bool conditions to the actual numeric type used by each value. - let then_condition = dfg - .insert_instruction_and_results( - Instruction::Cast(then_condition, then_type), - block, - None, - call_stack.clone(), - ) - .first(); - let else_condition = dfg - .insert_instruction_and_results( - Instruction::Cast(else_condition, else_type), - block, - None, - call_stack.clone(), - ) - .first(); + let cast = Instruction::Cast(then_condition, then_type); + let then_condition = + dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + + let cast = Instruction::Cast(else_condition, else_type); + let else_condition = + dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); let then_value = @@ -175,7 +166,7 @@ impl<'a> ValueMerger<'a> { for (element_index, element_type) in element_types.iter().enumerate() { let index = ((i * element_types.len() as u32 + element_index as u32) as u128).into(); - let index = self.dfg.make_constant(index, Type::field()); + let index = self.dfg.make_constant(index, NumericType::NativeField); let typevars = Some(vec![element_type.clone()]); @@ -243,7 +234,7 @@ impl<'a> ValueMerger<'a> { for (element_index, element_type) in element_types.iter().enumerate() { let index_u32 = i * element_types.len() as u32 + element_index as u32; let index_value = (index_u32 as u128).into(); - let index = self.dfg.make_constant(index_value, Type::field()); + let index = self.dfg.make_constant(index_value, NumericType::NativeField); let typevars = Some(vec![element_type.clone()]); @@ -295,7 +286,7 @@ impl<'a> ValueMerger<'a> { match typ { Type::Numeric(numeric_type) => { let zero = FieldElement::zero(); - self.dfg.make_constant(zero, Type::Numeric(*numeric_type)) + self.dfg.make_constant(zero, *numeric_type) } Type::Array(element_types, len) => { let mut array = im::Vector::new(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index ccf5bd9d9f8..872c7920a77 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -100,7 +100,7 @@ impl Context<'_> { bit_size: u32, ) -> ValueId { let base = self.field_constant(FieldElement::from(2_u128)); - let typ = self.function.dfg.type_of_value(lhs); + let typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let (max_bit, pow) = if let Some(rhs_constant) = self.function.dfg.get_numeric_constant(rhs) { // Happy case is that we know precisely by how many bits the integer will @@ -115,29 +115,29 @@ impl Context<'_> { return InsertInstructionResult::SimplifiedTo(zero).first(); } } - let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2), typ.clone()); + let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2), typ); let max_lhs_bits = self.function.dfg.get_value_max_num_bits(lhs); (max_lhs_bits + bit_shift_size, pow) } else { // we use a predicate to nullify the result in case of overflow - let bit_size_var = - self.numeric_constant(FieldElement::from(bit_size as u128), Type::unsigned(8)); + let u8_type = NumericType::unsigned(8); + let bit_size_var = self.numeric_constant(FieldElement::from(bit_size as u128), u8_type); let overflow = self.insert_binary(rhs, BinaryOp::Lt, bit_size_var); - let predicate = self.insert_cast(overflow, typ.clone()); + let predicate = self.insert_cast(overflow, typ); // we can safely cast to unsigned because overflow_checks prevent bit-shift with a negative value - let rhs_unsigned = self.insert_cast(rhs, Type::unsigned(bit_size)); + let rhs_unsigned = self.insert_cast(rhs, NumericType::unsigned(bit_size)); let pow = self.pow(base, rhs_unsigned); - let pow = self.insert_cast(pow, typ.clone()); + let pow = self.insert_cast(pow, typ); (FieldElement::max_num_bits(), self.insert_binary(predicate, BinaryOp::Mul, pow)) }; if max_bit <= bit_size { self.insert_binary(lhs, BinaryOp::Mul, pow) } else { - let lhs_field = self.insert_cast(lhs, Type::field()); - let pow_field = self.insert_cast(pow, Type::field()); + let lhs_field = self.insert_cast(lhs, NumericType::NativeField); + let pow_field = self.insert_cast(pow, NumericType::NativeField); let result = self.insert_binary(lhs_field, BinaryOp::Mul, pow_field); let result = self.insert_truncate(result, bit_size, max_bit); self.insert_cast(result, typ) @@ -153,7 +153,7 @@ impl Context<'_> { rhs: ValueId, bit_size: u32, ) -> ValueId { - let lhs_typ = self.function.dfg.type_of_value(lhs); + let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); if lhs_typ.is_unsigned() { @@ -161,14 +161,14 @@ impl Context<'_> { self.insert_binary(lhs, BinaryOp::Div, pow) } else { // Get the sign of the operand; positive signed operand will just do a division as well - let zero = self.numeric_constant(FieldElement::zero(), Type::signed(bit_size)); + let zero = self.numeric_constant(FieldElement::zero(), NumericType::signed(bit_size)); let lhs_sign = self.insert_binary(lhs, BinaryOp::Lt, zero); - let lhs_sign_as_field = self.insert_cast(lhs_sign, Type::field()); - let lhs_as_field = self.insert_cast(lhs, Type::field()); + let lhs_sign_as_field = self.insert_cast(lhs_sign, NumericType::NativeField); + let lhs_as_field = self.insert_cast(lhs, NumericType::NativeField); // For negative numbers, convert to 1-complement using wrapping addition of a + 1 let one_complement = self.insert_binary(lhs_sign_as_field, BinaryOp::Add, lhs_as_field); let one_complement = self.insert_truncate(one_complement, bit_size, bit_size + 1); - let one_complement = self.insert_cast(one_complement, Type::signed(bit_size)); + let one_complement = self.insert_cast(one_complement, NumericType::signed(bit_size)); // Performs the division on the 1-complement (or the operand if positive) let shifted_complement = self.insert_binary(one_complement, BinaryOp::Div, pow); // Convert back to 2-complement representation if operand is negative @@ -203,8 +203,8 @@ impl Context<'_> { let idx = self.field_constant(FieldElement::from((bit_size - i) as i128)); let b = self.insert_array_get(rhs_bits, idx, Type::bool()); let not_b = self.insert_not(b); - let b = self.insert_cast(b, Type::field()); - let not_b = self.insert_cast(not_b, Type::field()); + let b = self.insert_cast(b, NumericType::NativeField); + let not_b = self.insert_cast(not_b, NumericType::NativeField); let r1 = self.insert_binary(a, BinaryOp::Mul, b); let r2 = self.insert_binary(r_squared, BinaryOp::Mul, not_b); r = self.insert_binary(r1, BinaryOp::Add, r2); @@ -216,14 +216,14 @@ impl Context<'_> { } pub(crate) fn field_constant(&mut self, constant: FieldElement) -> ValueId { - self.function.dfg.make_constant(constant, Type::field()) + self.function.dfg.make_constant(constant, NumericType::NativeField) } /// Insert a numeric constant into the current function pub(crate) fn numeric_constant( &mut self, value: impl Into, - typ: Type, + typ: NumericType, ) -> ValueId { self.function.dfg.make_constant(value.into(), typ) } @@ -260,7 +260,7 @@ impl Context<'_> { /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. - pub(crate) fn insert_cast(&mut self, value: ValueId, typ: Type) -> ValueId { + pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { self.insert_instruction(Instruction::Cast(value, typ), None).first() } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index ce5534ecc7a..79910db148e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -18,7 +18,7 @@ use crate::ssa::{ dfg::DataFlowGraph, function::{Function, RuntimeType}, instruction::{BinaryOp, Hint, Instruction, Intrinsic}, - types::Type, + types::NumericType, value::Value, }, ssa_gen::Ssa, @@ -70,7 +70,8 @@ impl Context { ) { let instructions = function.dfg[block].take_instructions(); - let mut active_condition = function.dfg.make_constant(FieldElement::one(), Type::bool()); + let one = FieldElement::one(); + let mut active_condition = function.dfg.make_constant(one, NumericType::bool()); let mut last_side_effects_enabled_instruction = None; let mut new_instructions = Vec::with_capacity(instructions.len()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index f93f63c1fbb..45b7f9072d8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -5,6 +5,7 @@ use fxhash::FxHashMap as HashMap; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::instruction::Hint; +use crate::ssa::ir::types::NumericType; use crate::ssa::ir::value::ValueId; use crate::ssa::{ ir::{ @@ -63,7 +64,8 @@ impl Context { fn remove_if_else(&mut self, function: &mut Function) { let block = function.entry_block(); let instructions = function.dfg[block].take_instructions(); - let mut current_conditional = function.dfg.make_constant(FieldElement::one(), Type::bool()); + let one = FieldElement::one(); + let mut current_conditional = function.dfg.make_constant(one, NumericType::bool()); for instruction in instructions { match &function.dfg[instruction] { diff --git a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs index 3d40c88d704..87e680932c6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs @@ -2,12 +2,11 @@ use crate::ssa::{ ir::{ function::{Function, RuntimeType}, instruction::{Instruction, Intrinsic}, - types::Type, + types::NumericType, value::Value, }, ssa_gen::Ssa, }; -use acvm::FieldElement; use fxhash::FxHashSet as HashSet; impl Ssa { @@ -47,10 +46,9 @@ impl Function { // We replace the result with a fresh id. This will be unused, so the DIE pass will remove the leftover intrinsic call. self.dfg.replace_result(instruction_id, original_return_id); - let is_within_unconstrained = self.dfg.make_constant( - FieldElement::from(matches!(self.runtime(), RuntimeType::Brillig(_))), - Type::bool(), - ); + let is_unconstrained = matches!(self.runtime(), RuntimeType::Brillig(_)).into(); + let is_within_unconstrained = + self.dfg.make_constant(is_unconstrained, NumericType::bool()); // Replace all uses of the original return value with the constant self.dfg.set_value_from_id(original_return_id, is_within_unconstrained); } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 116e0de4ecd..7807658dabb 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -19,7 +19,6 @@ use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; use super::value::{Tree, Value, Values}; -use super::SSA_WORD_SIZE; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -281,37 +280,33 @@ impl<'a> FunctionContext<'a> { &mut self, value: impl Into, negative: bool, - typ: Type, + numeric_type: NumericType, ) -> Result { let value = value.into(); - if let Type::Numeric(numeric_type) = typ { - if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { - let call_stack = self.builder.get_call_stack(); - return Err(RuntimeError::IntegerOutOfBounds { - value: if negative { -value } else { value }, - typ: numeric_type, - range, - call_stack, - }); - } + if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { + let call_stack = self.builder.get_call_stack(); + return Err(RuntimeError::IntegerOutOfBounds { + value: if negative { -value } else { value }, + typ: numeric_type, + range, + call_stack, + }); + } - let value = if negative { - match numeric_type { - NumericType::NativeField => -value, - NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { - let base = 1_u128 << bit_size; - FieldElement::from(base) - value - } + let value = if negative { + match numeric_type { + NumericType::NativeField => -value, + NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + let base = 1_u128 << bit_size; + FieldElement::from(base) - value } - } else { - value - }; - - Ok(self.builder.numeric_constant(value, typ)) + } } else { - panic!("Expected type for numeric constant to be a numeric type, found {typ}"); - } + value + }; + + Ok(self.builder.numeric_constant(value, numeric_type)) } /// helper function which add instructions to the block computing the absolute value of the @@ -320,16 +315,16 @@ impl<'a> FunctionContext<'a> { assert_eq!(self.builder.type_of_value(sign), Type::bool()); // We compute the absolute value of lhs - let bit_width = - self.builder.numeric_constant(FieldElement::from(2_i128.pow(bit_size)), Type::field()); + let bit_width = FieldElement::from(2_i128.pow(bit_size)); + let bit_width = self.builder.numeric_constant(bit_width, NumericType::NativeField); let sign_not = self.builder.insert_not(sign); // We use unsafe casts here, this is fine as we're casting to a `field` type. - let as_field = self.builder.insert_cast(input, Type::field()); - let sign_field = self.builder.insert_cast(sign, Type::field()); + let as_field = self.builder.insert_cast(input, NumericType::NativeField); + let sign_field = self.builder.insert_cast(sign, NumericType::NativeField); let positive_predicate = self.builder.insert_binary(sign_field, BinaryOp::Mul, as_field); let two_complement = self.builder.insert_binary(bit_width, BinaryOp::Sub, as_field); - let sign_not_field = self.builder.insert_cast(sign_not, Type::field()); + let sign_not_field = self.builder.insert_cast(sign_not, NumericType::NativeField); let negative_predicate = self.builder.insert_binary(sign_not_field, BinaryOp::Mul, two_complement); self.builder.insert_binary(positive_predicate, BinaryOp::Add, negative_predicate) @@ -354,15 +349,18 @@ impl<'a> FunctionContext<'a> { operator: BinaryOpKind, location: Location, ) -> ValueId { - let result_type = self.builder.current_function.dfg.type_of_value(result); + let result_type = self.builder.current_function.dfg.type_of_value(result).unwrap_numeric(); match result_type { - Type::Numeric(NumericType::Signed { bit_size }) => { + NumericType::Signed { bit_size } => { match operator { BinaryOpKind::Add | BinaryOpKind::Subtract => { // Result is computed modulo the bit size let result = self.builder.insert_truncate(result, bit_size, bit_size + 1); - let result = - self.insert_safe_cast(result, Type::unsigned(bit_size), location); + let result = self.insert_safe_cast( + result, + NumericType::unsigned(bit_size), + location, + ); self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); self.insert_safe_cast(result, result_type, location) @@ -370,7 +368,7 @@ impl<'a> FunctionContext<'a> { BinaryOpKind::Multiply => { // Result is computed modulo the bit size let mut result = - self.builder.insert_cast(result, Type::unsigned(2 * bit_size)); + self.builder.insert_cast(result, NumericType::unsigned(2 * bit_size)); result = self.builder.insert_truncate(result, bit_size, 2 * bit_size); self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); @@ -382,7 +380,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("operator {} should not overflow", operator), } } - Type::Numeric(NumericType::Unsigned { bit_size }) => { + NumericType::Unsigned { bit_size } => { let dfg = &self.builder.current_function.dfg; let max_lhs_bits = dfg.get_value_max_num_bits(lhs); @@ -410,7 +408,7 @@ impl<'a> FunctionContext<'a> { result } - _ => result, + NumericType::NativeField => result, } } @@ -425,11 +423,11 @@ impl<'a> FunctionContext<'a> { bit_size: u32, location: Location, ) -> ValueId { - let one = self.builder.numeric_constant(FieldElement::one(), Type::bool()); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::bool()); assert!(self.builder.current_function.dfg.type_of_value(rhs) == Type::unsigned(8)); - let max = - self.builder.numeric_constant(FieldElement::from(bit_size as i128), Type::unsigned(8)); + let bit_size_field = FieldElement::from(bit_size as i128); + let max = self.builder.numeric_constant(bit_size_field, NumericType::unsigned(8)); let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); self.builder.set_location(location).insert_constrain( overflow, @@ -463,11 +461,11 @@ impl<'a> FunctionContext<'a> { let is_sub = operator == BinaryOpKind::Subtract; let half_width = self.builder.numeric_constant( FieldElement::from(2_i128.pow(bit_size - 1)), - Type::unsigned(bit_size), + NumericType::unsigned(bit_size), ); // We compute the sign of the operands. The overflow checks for signed integers depends on these signs - let lhs_as_unsigned = self.insert_safe_cast(lhs, Type::unsigned(bit_size), location); - let rhs_as_unsigned = self.insert_safe_cast(rhs, Type::unsigned(bit_size), location); + let lhs_as_unsigned = self.insert_safe_cast(lhs, NumericType::unsigned(bit_size), location); + let rhs_as_unsigned = self.insert_safe_cast(rhs, NumericType::unsigned(bit_size), location); let lhs_sign = self.builder.insert_binary(lhs_as_unsigned, BinaryOp::Lt, half_width); let mut rhs_sign = self.builder.insert_binary(rhs_as_unsigned, BinaryOp::Lt, half_width); let message = if is_sub { @@ -505,18 +503,19 @@ impl<'a> FunctionContext<'a> { bit_size, Some("attempt to multiply with overflow".to_string()), ); - let product = self.builder.insert_cast(product_field, Type::unsigned(bit_size)); + let product = + self.builder.insert_cast(product_field, NumericType::unsigned(bit_size)); // Then we check the signed product fits in a signed integer of bit_size-bits let not_same = self.builder.insert_not(same_sign); let not_same_sign_field = - self.insert_safe_cast(not_same, Type::unsigned(bit_size), location); + self.insert_safe_cast(not_same, NumericType::unsigned(bit_size), location); let positive_maximum_with_offset = self.builder.insert_binary(half_width, BinaryOp::Add, not_same_sign_field); let product_overflow_check = self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); - let one = self.builder.numeric_constant(FieldElement::one(), Type::bool()); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::bool()); self.builder.set_location(location).insert_constrain( product_overflow_check, one, @@ -595,7 +594,7 @@ impl<'a> FunctionContext<'a> { pub(super) fn insert_safe_cast( &mut self, mut value: ValueId, - typ: Type, + typ: NumericType, location: Location, ) -> ValueId { self.builder.set_location(location); @@ -614,7 +613,8 @@ impl<'a> FunctionContext<'a> { /// Create a const offset of an address for an array load or store pub(super) fn make_offset(&mut self, mut address: ValueId, offset: u128) -> ValueId { if offset != 0 { - let offset = self.builder.numeric_constant(offset, self.builder.type_of_value(address)); + let typ = self.builder.type_of_value(address).unwrap_numeric(); + let offset = self.builder.numeric_constant(offset, typ); address = self.builder.insert_binary(address, BinaryOp::Add, offset); } address @@ -622,7 +622,7 @@ impl<'a> FunctionContext<'a> { /// Array indexes are u32. This function casts values used as indexes to u32. pub(super) fn make_array_index(&mut self, index: ValueId) -> ValueId { - self.builder.insert_cast(index, Type::unsigned(SSA_WORD_SIZE)) + self.builder.insert_cast(index, NumericType::length_type()) } /// Define a local variable to be some Values that can later be retrieved @@ -870,12 +870,12 @@ impl<'a> FunctionContext<'a> { ) -> ValueId { let index = self.make_array_index(index); let element_size = - self.builder.numeric_constant(self.element_size(array), Type::unsigned(SSA_WORD_SIZE)); + self.builder.numeric_constant(self.element_size(array), NumericType::length_type()); // The actual base index is the user's index * the array element type's size let mut index = self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, element_size); - let one = self.builder.numeric_constant(FieldElement::one(), Type::unsigned(SSA_WORD_SIZE)); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::length_type()); new_value.for_each(|value| { let value = value.eval(self); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 91a49018f76..d341e5e9c4c 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -23,6 +23,7 @@ use self::{ }; use super::ir::instruction::ErrorType; +use super::ir::types::NumericType; use super::{ function_builder::data_bus::DataBus, ir::{ @@ -223,12 +224,12 @@ impl<'a> FunctionContext<'a> { } ast::Literal::Integer(value, negative, typ, location) => { self.builder.set_location(*location); - let typ = Self::convert_non_tuple_type(typ); + let typ = Self::convert_non_tuple_type(typ).unwrap_numeric(); self.checked_numeric_constant(*value, *negative, typ).map(Into::into) } ast::Literal::Bool(value) => { // Don't need to call checked_numeric_constant here since `value` can only be true or false - Ok(self.builder.numeric_constant(*value as u128, Type::bool()).into()) + Ok(self.builder.numeric_constant(*value as u128, NumericType::bool()).into()) } ast::Literal::Str(string) => Ok(self.codegen_string(string)), ast::Literal::FmtStr(fragments, number_of_fields, fields) => { @@ -272,7 +273,7 @@ impl<'a> FunctionContext<'a> { fn codegen_string(&mut self, string: &str) -> Values { let elements = vecmap(string.as_bytes(), |byte| { - let char = self.builder.numeric_constant(*byte as u128, Type::unsigned(8)); + let char = self.builder.numeric_constant(*byte as u128, NumericType::char()); (char.into(), false) }); let typ = Self::convert_non_tuple_type(&ast::Type::String(elements.len() as u32)); @@ -349,7 +350,7 @@ impl<'a> FunctionContext<'a> { UnaryOp::Minus => { let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); - let typ = self.builder.type_of_value(rhs); + let typ = self.builder.type_of_value(rhs).unwrap_numeric(); let zero = self.builder.numeric_constant(0u128, typ); Ok(self.insert_binary( zero, @@ -443,7 +444,7 @@ impl<'a> FunctionContext<'a> { let index = self.make_array_index(index); let type_size = Self::convert_type(element_type).size_of_type(); let type_size = - self.builder.numeric_constant(type_size as u128, Type::unsigned(SSA_WORD_SIZE)); + self.builder.numeric_constant(type_size as u128, NumericType::length_type()); let base_index = self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, type_size); @@ -482,7 +483,7 @@ impl<'a> FunctionContext<'a> { .make_array_index(length.expect("ICE: a length must be supplied for indexing slices")); let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); - let true_const = self.builder.numeric_constant(true, Type::bool()); + let true_const = self.builder.numeric_constant(true, NumericType::bool()); self.builder.insert_constrain( is_offset_out_of_bounds, @@ -493,7 +494,7 @@ impl<'a> FunctionContext<'a> { fn codegen_cast(&mut self, cast: &ast::Cast) -> Result { let lhs = self.codegen_non_tuple_expression(&cast.lhs)?; - let typ = Self::convert_non_tuple_type(&cast.r#type); + let typ = Self::convert_non_tuple_type(&cast.r#type).unwrap_numeric(); Ok(self.insert_safe_cast(lhs, typ, cast.location).into()) } @@ -730,7 +731,7 @@ impl<'a> FunctionContext<'a> { assert_payload: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; - let true_literal = self.builder.numeric_constant(true, Type::bool()); + let true_literal = self.builder.numeric_constant(true, NumericType::bool()); // Set the location here for any errors that may occur when we codegen the assert message self.builder.set_location(location); From aa0253171122d33007c8e2b4990d871501c49d8c Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 11 Dec 2024 09:16:28 -0600 Subject: [PATCH 4/5] Get unit tests compiling --- compiler/noirc_evaluator/src/acir/mod.rs | 15 ++++++++++----- .../src/brillig/brillig_gen/variable_liveness.rs | 10 +++++----- .../src/ssa/function_builder/mod.rs | 10 +++++----- .../noirc_evaluator/src/ssa/ir/instruction.rs | 4 ++-- .../src/ssa/opt/constant_folding.rs | 13 ++++++++----- compiler/noirc_evaluator/src/ssa/opt/die.rs | 9 ++++++--- compiler/noirc_evaluator/src/ssa/opt/inlining.rs | 13 ++++++------- .../src/ssa/opt/normalize_value_ids.rs | 2 +- compiler/noirc_evaluator/src/ssa/opt/rc.rs | 12 ++++++++---- .../src/ssa/opt/remove_enable_side_effects.rs | 6 +++--- .../noirc_evaluator/src/ssa/parser/into_ssa.rs | 4 ++-- 11 files changed, 56 insertions(+), 42 deletions(-) diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index b496bc6a735..fe46fbe72a1 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -2903,7 +2903,12 @@ mod test { brillig::Brillig, ssa::{ function_builder::FunctionBuilder, - ir::{function::FunctionId, instruction::BinaryOp, map::Id, types::Type}, + ir::{ + function::FunctionId, + instruction::BinaryOp, + map::Id, + types::{NumericType, Type}, + }, }, }; @@ -2931,7 +2936,7 @@ mod test { let foo_v1 = builder.add_parameter(Type::field()); let foo_equality_check = builder.insert_binary(foo_v0, BinaryOp::Eq, foo_v1); - let zero = builder.numeric_constant(0u128, Type::unsigned(1)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(1)); builder.insert_constrain(foo_equality_check, zero, None); builder.terminate_with_return(vec![foo_v0]); } @@ -3375,7 +3380,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); @@ -3448,7 +3453,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); @@ -3534,7 +3539,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index 87165c36dff..d6851a9ecf9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -372,7 +372,7 @@ mod test { let v3 = builder.insert_allocate(Type::field()); - let zero = builder.numeric_constant(0u128, Type::field()); + let zero = builder.field_constant(0u128); builder.insert_store(v3, zero); let v4 = builder.insert_binary(v0, BinaryOp::Eq, zero); @@ -381,7 +381,7 @@ mod test { builder.switch_to_block(b2); - let twenty_seven = builder.numeric_constant(27u128, Type::field()); + let twenty_seven = builder.field_constant(27u128); let v7 = builder.insert_binary(v0, BinaryOp::Add, twenty_seven); builder.insert_store(v3, v7); @@ -487,7 +487,7 @@ mod test { let v3 = builder.insert_allocate(Type::field()); - let zero = builder.numeric_constant(0u128, Type::field()); + let zero = builder.field_constant(0u128); builder.insert_store(v3, zero); builder.terminate_with_jmp(b1, vec![zero]); @@ -515,7 +515,7 @@ mod test { builder.switch_to_block(b5); - let twenty_seven = builder.numeric_constant(27u128, Type::field()); + let twenty_seven = builder.field_constant(27u128); let v10 = builder.insert_binary(v7, BinaryOp::Eq, twenty_seven); let v11 = builder.insert_not(v10); @@ -534,7 +534,7 @@ mod test { builder.switch_to_block(b8); - let one = builder.numeric_constant(1u128, Type::field()); + let one = builder.field_constant(1u128); let v15 = builder.insert_binary(v7, BinaryOp::Add, one); builder.terminate_with_jmp(b4, vec![v15]); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 7dc9671381e..80329ea4483 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -527,7 +527,7 @@ mod tests { use crate::ssa::ir::{ instruction::{Endian, Intrinsic}, map::Id, - types::Type, + types::{NumericType, Type}, }; use super::FunctionBuilder; @@ -539,12 +539,12 @@ mod tests { // let bits: [u1; 8] = x.to_le_bits(); let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id); - let one = builder.numeric_constant(FieldElement::one(), Type::bool()); - let zero = builder.numeric_constant(FieldElement::zero(), Type::bool()); + let one = builder.numeric_constant(FieldElement::one(), NumericType::bool()); + let zero = builder.numeric_constant(FieldElement::zero(), NumericType::bool()); let to_bits_id = builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); - let input = builder.numeric_constant(FieldElement::from(7_u128), Type::field()); - let length = builder.numeric_constant(FieldElement::from(8_u128), Type::field()); + let input = builder.field_constant(FieldElement::from(7_u128)); + let length = builder.field_constant(FieldElement::from(8_u128)); let result_types = vec![Type::Array(Arc::new(vec![Type::bool()]), 8)]; let call_results = builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 3bde8786dda..0e9f6e31a09 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -602,7 +602,7 @@ impl Instruction { rhs: f(binary.rhs), operator: binary.operator, }), - Instruction::Cast(value, typ) => Instruction::Cast(f(*value), typ.clone()), + Instruction::Cast(value, typ) => Instruction::Cast(f(*value), *typ), Instruction::Not(value) => Instruction::Not(f(*value)), Instruction::Truncate { value, bit_size, max_bit_size } => Instruction::Truncate { value: f(*value), @@ -759,7 +759,7 @@ impl Instruction { Value::NumericConstant { constant, typ } if typ.is_unsigned() => { // As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills. let value = !constant.to_u128() % (1 << typ.bit_size()); - SimplifiedTo(dfg.make_constant(value.into(), typ.clone())) + SimplifiedTo(dfg.make_constant(value.into(), *typ)) } Value::Instruction { instruction, .. } => { // !!v => v diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index e3216654c05..e2379043541 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -831,7 +831,10 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{map::Id, types::Type}, + ir::{ + map::Id, + types::{NumericType, Type}, + }, opt::assert_normalized_ssa_equals, Ssa, }; @@ -855,7 +858,7 @@ mod test { assert_eq!(instructions.len(), 2); // The final return is not counted let v0 = main.parameters()[0]; - let two = main.dfg.make_constant(2_u128.into(), Type::field()); + let two = main.dfg.make_constant(2_u128.into(), NumericType::NativeField); main.dfg.set_value_from_id(v0, two); @@ -891,7 +894,7 @@ mod test { // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result. let constant = 2_u128.pow(8); - let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); + let constant = main.dfg.make_constant(constant.into(), NumericType::unsigned(16)); main.dfg.set_value_from_id(v1, constant); @@ -929,7 +932,7 @@ mod test { // Note that this constant does not guarantee that `v0/constant < 2^8`. We must then truncate the result. let constant = 2_u128.pow(8) - 1; - let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); + let constant = main.dfg.make_constant(constant.into(), NumericType::unsigned(16)); main.dfg.set_value_from_id(v1, constant); @@ -1150,7 +1153,7 @@ mod test { // Compiling main let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(64)); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let typ = Type::Array(Arc::new(vec![Type::unsigned(64)]), 25); let array_contents = im::vector![ diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 0cbf7d20cb1..de3ac44652f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -527,7 +527,10 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{map::Id, types::Type}, + ir::{ + map::Id, + types::{NumericType, Type}, + }, opt::assert_normalized_ssa_equals, Ssa, }; @@ -636,7 +639,7 @@ mod test { // Compiling main let mut builder = FunctionBuilder::new("main".into(), main_id); - let zero = builder.numeric_constant(0u128, Type::unsigned(32)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(32)); let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2); let v1 = builder.insert_make_array(vector![zero, zero], array_type.clone()); let v2 = builder.insert_allocate(array_type.clone()); @@ -649,7 +652,7 @@ mod test { builder.switch_to_block(b1); let v3 = builder.insert_load(v2, array_type); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); let v5 = builder.insert_array_set(v3, zero, one); builder.terminate_with_return(vec![v5]); diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index f91487fd73e..37659ec7c98 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -469,7 +469,7 @@ impl<'function> PerFunctionContext<'function> { unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") } Value::NumericConstant { constant, typ } => { - self.context.builder.numeric_constant(*constant, typ.clone()) + self.context.builder.numeric_constant(*constant, *typ) } Value::Function(function) => self.context.builder.import_function(*function), Value::Intrinsic(intrinsic) => self.context.builder.import_intrinsic_id(*intrinsic), @@ -1062,10 +1062,10 @@ mod test { let join_block = builder.insert_block(); builder.terminate_with_jmpif(inner2_cond, then_block, else_block); builder.switch_to_block(then_block); - let one = builder.numeric_constant(FieldElement::one(), Type::field()); + let one = builder.field_constant(FieldElement::one()); builder.terminate_with_jmp(join_block, vec![one]); builder.switch_to_block(else_block); - let two = builder.numeric_constant(FieldElement::from(2_u128), Type::field()); + let two = builder.field_constant(FieldElement::from(2_u128)); builder.terminate_with_jmp(join_block, vec![two]); let join_param = builder.add_block_parameter(join_block, Type::field()); builder.switch_to_block(join_block); @@ -1177,17 +1177,16 @@ mod test { builder.terminate_with_return(v0); builder.new_brillig_function("bar".into(), bar_id, InlineType::default()); - let bar_v0 = - builder.numeric_constant(1_usize, Type::Numeric(NumericType::Unsigned { bit_size: 1 })); + let bar_v0 = builder.numeric_constant(1_usize, NumericType::bool()); let then_block = builder.insert_block(); let else_block = builder.insert_block(); let join_block = builder.insert_block(); builder.terminate_with_jmpif(bar_v0, then_block, else_block); builder.switch_to_block(then_block); - let one = builder.numeric_constant(FieldElement::one(), Type::field()); + let one = builder.field_constant(FieldElement::one()); builder.terminate_with_jmp(join_block, vec![one]); builder.switch_to_block(else_block); - let two = builder.numeric_constant(FieldElement::from(2_u128), Type::field()); + let two = builder.field_constant(FieldElement::from(2_u128)); builder.terminate_with_jmp(join_block, vec![two]); let join_param = builder.add_block_parameter(join_block, Type::field()); builder.switch_to_block(join_block); diff --git a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index a5b60fb5fcd..f5e96224260 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -177,7 +177,7 @@ impl IdMaps { } Value::NumericConstant { constant, typ } => { - new_function.dfg.make_constant(*constant, typ.clone()) + new_function.dfg.make_constant(*constant, *typ) } Value::Intrinsic(intrinsic) => new_function.dfg.import_intrinsic(*intrinsic), Value::ForeignFunction(name) => new_function.dfg.import_foreign_function(name), diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index ffe4ada39b7..64f6e2ddfea 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -160,8 +160,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::Instruction, map::Id, types::Type, + basic_block::BasicBlockId, + dfg::DataFlowGraph, + function::RuntimeType, + instruction::Instruction, + map::Id, + types::{NumericType, Type}, }, }; @@ -251,7 +255,7 @@ mod test { builder.insert_inc_rc(v0); let v2 = builder.insert_load(v1, array_type); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let five = builder.field_constant(5u128); let v7 = builder.insert_array_set(v2, zero, five); @@ -302,7 +306,7 @@ mod test { builder.insert_store(v0, v1); let v2 = builder.insert_load(v1, array_type.clone()); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let five = builder.field_constant(5u128); let v7 = builder.insert_array_set(v2, zero, five); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index 79910db148e..e85e2c4a441 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -204,7 +204,7 @@ mod test { ir::{ instruction::{BinaryOp, Instruction}, map::Id, - types::Type, + types::{NumericType, Type}, }, }; @@ -235,9 +235,9 @@ mod test { let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); - let two = builder.numeric_constant(2u128, Type::field()); + let two = builder.field_constant(2u128); - let one = builder.numeric_constant(1u128, Type::bool()); + let one = builder.numeric_constant(1u128, NumericType::bool()); builder.insert_enable_side_effects_if(one); builder.insert_binary(v0, BinaryOp::Mul, two); diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index e78cbbd75a1..b0003aa5f0f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -207,7 +207,7 @@ impl Translator { } ParsedInstruction::Cast { target, lhs, typ } => { let lhs = self.translate_value(lhs)?; - let value_id = self.builder.insert_cast(lhs, typ); + let value_id = self.builder.insert_cast(lhs, typ.unwrap_numeric()); self.define_variable(target, value_id)?; } ParsedInstruction::Constrain { lhs, rhs, assert_message } => { @@ -290,7 +290,7 @@ impl Translator { fn translate_value(&mut self, value: ParsedValue) -> Result { match value { ParsedValue::NumericConstant { constant, typ } => { - Ok(self.builder.numeric_constant(constant, typ)) + Ok(self.builder.numeric_constant(constant, typ.unwrap_numeric())) } ParsedValue::Variable(identifier) => self.lookup_variable(identifier), } From 1691cf0d768d58c8183a2a058a3c27c8e56d1466 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Wed, 11 Dec 2024 11:12:38 -0600 Subject: [PATCH 5/5] Fix compiler errors --- .../src/brillig/brillig_gen/brillig_block.rs | 15 +- .../check_for_underconstrained_values.rs | 2 +- .../src/ssa/function_builder/data_bus.rs | 2 +- .../src/ssa/function_builder/mod.rs | 7 +- .../noirc_evaluator/src/ssa/ir/basic_block.rs | 7 +- compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 44 ++-- .../src/ssa/ir/function_inserter.rs | 7 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 4 +- .../src/ssa/ir/instruction/binary.rs | 2 +- .../src/ssa/ir/instruction/call.rs | 24 +- .../src/ssa/ir/instruction/call/blackbox.rs | 20 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 8 +- compiler/noirc_evaluator/src/ssa/ir/types.rs | 8 - compiler/noirc_evaluator/src/ssa/ir/value.rs | 4 +- .../noirc_evaluator/src/ssa/opt/array_set.rs | 6 +- .../src/ssa/opt/as_slice_length.rs | 2 +- .../src/ssa/opt/assert_constant.rs | 2 +- .../src/ssa/opt/constant_folding.rs | 32 ++- .../src/ssa/opt/defunctionalize.rs | 18 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 14 +- .../src/ssa/opt/flatten_cfg.rs | 213 +++++++++--------- .../ssa/opt/flatten_cfg/capacity_tracker.rs | 7 +- .../src/ssa/opt/flatten_cfg/value_merger.rs | 81 +++---- .../noirc_evaluator/src/ssa/opt/inlining.rs | 46 ++-- .../src/ssa/opt/loop_invariant.rs | 2 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 6 +- .../src/ssa/opt/normalize_value_ids.rs | 6 - .../src/ssa/opt/remove_bit_shifts.rs | 19 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 4 +- 29 files changed, 277 insertions(+), 335 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index cc516b77dc0..ffc78915abe 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1279,8 +1279,8 @@ impl<'block> BrilligBlock<'block> { result_variable: SingleAddrVariable, ) { let binary_type = type_of_binary_operation( - dfg[binary.lhs].get_type().as_ref(), - dfg[binary.rhs].get_type().as_ref(), + &dfg.type_of_value(binary.lhs), + &dfg.type_of_value(binary.rhs), binary.operator, ); @@ -1788,8 +1788,7 @@ impl<'block> BrilligBlock<'block> { result: ValueId, dfg: &DataFlowGraph, ) -> BrilligVariable { - let typ = dfg[result].get_type(); - match typ.as_ref() { + match dfg.type_of_value(result) { Type::Numeric(_) => self.variables.define_variable( self.function_context, self.brillig_context, @@ -1797,7 +1796,7 @@ impl<'block> BrilligBlock<'block> { dfg, ), - Type::Array(..) => { + typ @ Type::Array(..) => { let variable = self.variables.define_variable( self.function_context, self.brillig_context, @@ -1805,7 +1804,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.allocate_foreign_call_result_array(typ.as_ref(), array); + self.allocate_foreign_call_result_array(&typ, array); variable } @@ -1825,9 +1824,7 @@ impl<'block> BrilligBlock<'block> { variable } - _ => { - unreachable!("ICE: unsupported return type for black box call {typ:?}") - } + typ => unreachable!("ICE: unsupported return type for black box call {typ:?}"), } } diff --git a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index f77c36f3eda..7b6613b3c95 100644 --- a/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -256,7 +256,7 @@ impl DependencyContext { Instruction::Store { address, value } => { self.memory_slots.insert(*address, function.dfg.resolve(*value)); } - Instruction::Load { address } => { + Instruction::Load { address, result_type: _ } => { // Recall the value stored at address as parent for the results if let Some(value_id) = self.memory_slots.get(address) { self.update_children(&[*value_id], &results); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index 10efa1f1567..d190a47bd64 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -119,7 +119,7 @@ impl FunctionBuilder { /// Insert a value into a data bus builder fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) { assert!(databus.databus.is_none(), "initializing finalized call data"); - let typ = self.current_function.dfg[value].get_type().into_owned(); + let typ = self.current_function.dfg.type_of_value(value); match typ { Type::Numeric(_) => { databus.values.push_back(value); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index f9740f5a283..02ebca69d96 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod data_bus; -use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; +use std::{borrow::Cow, collections::BTreeMap}; use acvm::{acir::circuit::ErrorSelector, FieldElement}; use noirc_errors::Location; @@ -189,7 +189,6 @@ impl FunctionBuilder { /// given amount of field elements. Returns the result of the allocate instruction, /// which is always a Reference to the allocated data. pub(crate) fn insert_allocate(&mut self, element_type: Type) -> ValueId { - let reference_type = Type::Reference(Arc::new(element_type)); self.insert_instruction(Instruction::Allocate { element_type }).first() } @@ -251,7 +250,7 @@ impl FunctionBuilder { /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { - self.insert_instruction(Instruction::Cast(value, typ), None).first() + self.insert_instruction(Instruction::Cast(value, typ)).first() } /// Insert a truncate instruction at the end of the current block. @@ -293,7 +292,7 @@ impl FunctionBuilder { arguments: Vec, result_types: Vec, ) -> Cow<[ValueId]> { - let call = Instruction::Call { func, arguments, result_types: Arc::new(result_types) }; + let call = Instruction::Call { func, arguments, result_types }; self.insert_instruction(call).results() } diff --git a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs index e9b2911a297..6ff7c7a9fdb 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/basic_block.rs @@ -38,7 +38,12 @@ impl BasicBlock { /// Create a new BasicBlock with no parameters. /// Parameters can be added later via BasicBlock::add_parameter pub(crate) fn new() -> Self { - Self { parameter_types: Vec::new(), parameters: Vec::new(), instructions: Vec::new(), terminator: None } + Self { + parameter_types: Vec::new(), + parameters: Vec::new(), + instructions: Vec::new(), + terminator: None, + } } /// Returns the parameters of this block diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 6420646379d..376e71c6cdc 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -115,17 +115,17 @@ impl DataFlowGraph { &mut self, block: BasicBlockId, ) -> BasicBlockId { - let parameters = self.blocks[block].parameters(); let new_block = self.make_block(); + let parameter_count = self.blocks[block].parameters().len(); + let mut parameter_types = self.blocks[block].parameter_types().to_vec(); - let parameters = vecmap(parameters.iter().enumerate(), |(position, param)| { - let typ = self.values[*param].get_type().into_owned(); - self.values.insert(Value::Param { block: new_block, position, typ }) + let parameters = vecmap(0..parameter_count, |position| { + self.values.insert(Value::Param { block: new_block, position }) }); let new_block_value = &mut self.blocks[new_block]; new_block_value.set_parameters(parameters); - new_block_value.parameter_types_mut().extend_from_slice(self.blocks[block].parameter_types()); + new_block_value.parameter_types_mut().append(&mut parameter_types); new_block } @@ -231,15 +231,18 @@ impl DataFlowGraph { match &self.values[value_id] { Value::Instruction { instruction, position } => { let position = *position; - match &mut self[*instruction] { + match &mut self.instructions[*instruction] { Instruction::Call { result_types, .. } => { result_types[position] = target_type; - }, + } Instruction::Load { result_type, .. } - | Instruction::Cast(_, result_type) | Instruction::ArrayGet { result_type, .. } => { *result_type = target_type; - }, + } + + Instruction::Cast(_, result_type) => { + *result_type = target_type.unwrap_numeric(); + } instruction @ (Instruction::Binary(_) | Instruction::Not(_) @@ -248,17 +251,21 @@ impl DataFlowGraph { | Instruction::Store { .. } | Instruction::ArraySet { .. } | Instruction::IfElse { .. } - | Instruction::MakeArray { .. }) => panic!("Can't set the type of {instruction:?}"), + | Instruction::MakeArray { .. }) => { + panic!("Can't set the type of {instruction:?}") + } Instruction::EnableSideEffectsIf { .. } | Instruction::Constrain(..) | Instruction::RangeCheck { .. } | Instruction::IncrementRc { .. } - | Instruction::DecrementRc { .. } => unreachable!("These instructions have no results"), + | Instruction::DecrementRc { .. } => { + unreachable!("These instructions have no results") + } } } Value::Param { block, position } => { - self[*block].parameter_types_mut()[*position] = target_type; + self.blocks[*block].parameter_types_mut()[*position] = target_type; } value => unreachable!("ICE: Cannot set type of {:?}", value), } @@ -324,7 +331,7 @@ impl DataFlowGraph { /// Returns the results of the instruction pub(crate) fn make_instruction_results(&mut self, instruction_id: InstructionId) { let result_count = self.instruction_result_count(instruction_id); - let results = vecmap(0 .. result_count, |position| { + let results = vecmap(0..result_count, |position| { let instruction = instruction_id; self.values.insert(Value::Instruction { position, instruction }) }); @@ -346,7 +353,7 @@ impl DataFlowGraph { InstructionResultType::Known(_) => 1, InstructionResultType::Operand(_) => 1, InstructionResultType::None => 0, - InstructionResultType::Multiple(types) => types.len() + InstructionResultType::Multiple(types) => types.len(), } } @@ -359,7 +366,7 @@ impl DataFlowGraph { InstructionResultType::Operand(value) => self.type_of_value(value), InstructionResultType::Known(typ) => typ, InstructionResultType::None => unreachable!("Instruction has no results"), - InstructionResultType::Multiple(types) => types[*position].clone() + InstructionResultType::Multiple(types) => types[*position].clone(), } } Value::Param { block, position } => self[*block].type_of_parameter(*position).clone(), @@ -407,10 +414,9 @@ impl DataFlowGraph { .position(|&id| id == prev_value_id) .expect("Result id not found while replacing"); - let value_id = self.values.insert(Value::Instruction { - position: res_position, - instruction: instruction_id, - }); + let value_id = self + .values + .insert(Value::Instruction { position: res_position, instruction: instruction_id }); // Replace the value in list of results for this instruction results[res_position] = value_id; diff --git a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs index f8c9df5f7a1..d3b9de24c74 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -144,11 +144,8 @@ impl<'f> FunctionInserter<'f> { None }; - let new_results = self.function.dfg.insert_instruction_and_results( - instruction, - block, - call_stack, - ); + let new_results = + self.function.dfg.insert_instruction_and_results(instruction, block, call_stack); // Cache an array in the fresh_array_cache if array caching is enabled. // The fresh cache isn't used for deduplication until an external pass confirms we diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 28d285f53d6..793641fe260 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -271,7 +271,7 @@ pub(crate) enum Instruction { RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option }, /// Performs a function call with a list of its arguments. - Call { func: ValueId, arguments: Vec, result_types: Arc> }, + Call { func: ValueId, arguments: Vec, result_types: Vec }, /// Allocates a region of memory. Note that this is not concerned with /// the type of memory, the type of element is determined when loading this memory. @@ -1131,7 +1131,7 @@ pub(crate) enum InstructionResultType { Known(Type), /// Function calls are a special case, they may return multiple values - Multiple(Arc>), + Multiple(Vec), /// This instruction does not return any results. None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 8527dae16d3..0f52168a30d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -93,7 +93,7 @@ impl Binary { if let (Some(lhs), Some(rhs)) = (lhs, rhs) { return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { Some((result, result_type)) => { - let value = dfg.make_constant(result, result_type.unwrap_numeric()); + let value = dfg.make_constant(result, result_type); SimplifyResult::SimplifiedTo(value) } None => SimplifyResult::None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index f0ca501944e..9150b7ba67a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -53,10 +53,10 @@ pub(super) fn simplify_call( let simplified_result = match intrinsic { Intrinsic::ToBits(endian) => { // TODO: simplify to a range constraint if `limb_count == 1` - if let (Some(constant_args), Some(return_type)) = (constant_args, return_type.clone()) { + if let (Some(constant_args), Some(return_type)) = (constant_args, return_type) { let field = constant_args[0]; let limb_count = if let Type::Array(_, array_len) = return_type { - array_len + *array_len } else { unreachable!("ICE: Intrinsic::ToRadix return type must be array") }; @@ -75,11 +75,11 @@ pub(super) fn simplify_call( } Intrinsic::ToRadix(endian) => { // TODO: simplify to a range constraint if `limb_count == 1` - if let (Some(constant_args), Some(return_type)) = (constant_args, return_type.clone()) { + if let (Some(constant_args), Some(return_type)) = (constant_args, return_type) { let field = constant_args[0]; let radix = constant_args[1].to_u128() as u32; let limb_count = if let Type::Array(_, array_len) = return_type { - array_len + *array_len } else { unreachable!("ICE: Intrinsic::ToRadix return type must be array") }; @@ -334,7 +334,7 @@ pub(super) fn simplify_call( } Intrinsic::Hint(Hint::BlackBox) => SimplifyResult::None, Intrinsic::BlackBox(bb_func) => { - simplify_black_box_func(bb_func, arguments, dfg, block, call_stack) + simplify_black_box_func(bb_func, arguments, return_types, dfg, block, call_stack) } Intrinsic::AsField => { let instruction = Instruction::Cast(arguments[0], NumericType::NativeField); @@ -342,7 +342,7 @@ pub(super) fn simplify_call( } Intrinsic::FromField => { let incoming_type = Type::field(); - let target_type = return_type.clone().unwrap(); + let target_type = return_type.unwrap(); let truncate = Instruction::Truncate { value: arguments[0], @@ -358,8 +358,8 @@ pub(super) fn simplify_call( Intrinsic::AsWitness => SimplifyResult::None, Intrinsic::IsUnconstrained => SimplifyResult::None, Intrinsic::DerivePedersenGenerators => { - if let Some(Type::Array(_, len)) = return_type.clone() { - simplify_derive_generators(dfg, arguments, len, block, call_stack) + if let Some(Type::Array(_, len)) = return_type { + simplify_derive_generators(dfg, arguments, *len, block, call_stack) } else { unreachable!("Derive Pedersen Generators must return an array"); } @@ -383,7 +383,7 @@ pub(super) fn simplify_call( { assert_eq!( dfg.type_of_value(*result), - expected_types, + *expected_types, "Simplification should not alter return type" ); } @@ -495,7 +495,8 @@ fn simplify_slice_pop_back( flattened_len = update_slice_length(flattened_len, dfg, BinaryOp::Sub, block); // We must pop multiple elements in the case of a slice of tuples - for result_type in element_types { + for result_type in element_types.iter() { + let result_type = result_type.clone(); let get_last_elem_instr = Instruction::ArrayGet { array: arguments[1], index: flattened_len, result_type }; @@ -518,6 +519,7 @@ fn simplify_slice_pop_back( fn simplify_black_box_func( bb_func: BlackBoxFunc, arguments: &[ValueId], + result_types: &[Type], dfg: &mut DataFlowGraph, block: BasicBlockId, call_stack: &CallStack, @@ -584,7 +586,7 @@ fn simplify_black_box_func( ), BlackBoxFunc::MultiScalarMul => { - blackbox::simplify_msm(dfg, solver, arguments, block, call_stack) + blackbox::simplify_msm(dfg, solver, arguments, result_types, block, call_stack) } BlackBoxFunc::EmbeddedCurveAdd => { blackbox::simplify_ec_add(dfg, solver, arguments, block, call_stack) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index c58264dbe84..d570ebd41cf 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -58,7 +58,7 @@ pub(super) fn simplify_ec_add( let elements = im::vector![result_x, result_y, result_is_infinity]; let instruction = Instruction::MakeArray { elements, typ }; let result_array = - dfg.insert_instruction_and_results(instruction, block, None, call_stack.clone()); + dfg.insert_instruction_and_results(instruction, block, call_stack.clone()); SimplifyResult::SimplifiedTo(result_array.first()) } @@ -70,6 +70,7 @@ pub(super) fn simplify_msm( dfg: &mut DataFlowGraph, solver: impl BlackBoxFunctionSolver, arguments: &[ValueId], + result_types: &[Type], block: BasicBlockId, call_stack: &CallStack, ) -> SimplifyResult { @@ -152,12 +153,8 @@ pub(super) fn simplify_msm( let elements = im::vector![result_x, result_y, result_is_infinity]; let typ = Type::Array(Arc::new(vec![Type::field()]), 3); let instruction = Instruction::MakeArray { elements, typ }; - let result_array = dfg.insert_instruction_and_results( - instruction, - block, - None, - call_stack.clone(), - ); + let result_array = + dfg.insert_instruction_and_results(instruction, block, call_stack.clone()); return SimplifyResult::SimplifiedTo(result_array.first()); } @@ -181,17 +178,18 @@ pub(super) fn simplify_msm( // Construct the simplified MSM expression let typ = Type::Array(Arc::new(vec![Type::field()]), var_scalars.len() as u32); let scalars = Instruction::MakeArray { elements: var_scalars.into(), typ }; - let scalars = dfg - .insert_instruction_and_results(scalars, block, None, call_stack.clone()) - .first(); + let scalars = + dfg.insert_instruction_and_results(scalars, block, call_stack.clone()).first(); let typ = Type::Array(Arc::new(vec![Type::field()]), var_points.len() as u32); let points = Instruction::MakeArray { elements: var_points.into(), typ }; let points = - dfg.insert_instruction_and_results(points, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(points, block, call_stack.clone()).first(); let msm = dfg.import_intrinsic(Intrinsic::BlackBox(BlackBoxFunc::MultiScalarMul)); + SimplifyResult::SimplifiedToInstruction(Instruction::Call { func: msm, arguments: vec![points, scalars], + result_types: result_types.to_vec(), }) } _ => SimplifyResult::None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 29e79728303..ee720ab4cb5 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -150,14 +150,14 @@ fn display_instruction_inner( writeln!(f) } } - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, result_types: _ } => { let arguments = value_list(function, arguments); writeln!(f, "call {}({}){}", show(*func), arguments, result_types(function, results)) } - Instruction::Allocate => { + Instruction::Allocate { element_type: _ } => { writeln!(f, "allocate{}", result_types(function, results)) } - Instruction::Load { address } => { + Instruction::Load { address, result_type: _ } => { writeln!(f, "load {}{}", show(*address), result_types(function, results)) } Instruction::Store { address, value } => { @@ -166,7 +166,7 @@ fn display_instruction_inner( Instruction::EnableSideEffectsIf { condition } => { writeln!(f, "enable_side_effects {}", show(*condition)) } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { writeln!( f, "array_get {}, index {}{}", diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 06ad7bd44bb..335248dd79c 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -154,14 +154,6 @@ impl Type { Type::Numeric(NumericType::length_type()) } - /// Unwrap the inner NumericType or panic - pub(crate) fn unwrap_numeric(&self) -> NumericType { - match self { - Type::Numeric(numeric) => *numeric, - other => panic!("Expected NumericType, found {other:?}"), - } - } - /// Returns the inner NumericType if this is one, or panics otherwise pub(crate) fn unwrap_numeric(&self) -> NumericType { match self { diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index 753c4215fe9..8cb654cc9dd 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use acvm::FieldElement; use serde::{Deserialize, Serialize}; @@ -9,7 +7,7 @@ use super::{ function::FunctionId, instruction::{InstructionId, Intrinsic}, map::Id, - types::{NumericType, Type}, + types::NumericType, }; pub(crate) type ValueId = Id; diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs index 09339cf0797..946ab5e7d50 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -142,9 +142,9 @@ impl<'f> Context<'f> { } } } - Instruction::Load { address } => { - let result = self.dfg.instruction_results(*instruction_id)[0]; - if matches!(self.dfg.type_of_value(result), Array { .. } | Slice { .. }) { + Instruction::Load { address, result_type } => { + if matches!(result_type, Array { .. } | Slice { .. }) { + let result = *self.dfg.instruction_results(*instruction_id).last().unwrap(); let is_reference_param = self.dfg.block_parameters(block_id).contains(address); self.arrays_from_load.insert(result, is_reference_param); diff --git a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs index c6cdffd3bc3..497d9d4b90f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -39,7 +39,7 @@ fn known_slice_lengths(func: &Function) -> HashMap { let block = &func.dfg[block_id]; for instruction_id in block.instructions() { let (target_func, arguments) = match &func.dfg[*instruction_id] { - Instruction::Call { func, arguments } => (func, arguments), + Instruction::Call { func, arguments, .. } => (func, arguments), _ => continue, }; diff --git a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs index 348c78683a0..681df027b18 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -67,7 +67,7 @@ fn check_instruction( let assert_constant_id = function.dfg.import_intrinsic(Intrinsic::AssertConstant); let static_assert_id = function.dfg.import_intrinsic(Intrinsic::StaticAssert); match &function.dfg[instruction] { - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, .. } => { if *func == assert_constant_id { evaluate_assert_constant(function, instruction, arguments) } else if *func == static_assert_id { diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index e2379043541..1a1836d1c0e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -112,7 +112,7 @@ impl Ssa { for block_id in function.reachable_blocks() { for instruction_id in function.dfg[block_id].instructions() { let instruction = &function.dfg[*instruction_id]; - let Instruction::Call { func: func_id, arguments: _ } = instruction else { + let Instruction::Call { func: func_id, .. } = instruction else { continue; }; @@ -318,7 +318,7 @@ impl<'brillig> Context<'brillig> { let value = *cached.last().unwrap(); let inc_rc = Instruction::IncrementRc { value }; let call_stack = dfg.get_call_stack(id); - dfg.insert_instruction_and_results(inc_rc, block, None, call_stack); + dfg.insert_instruction_and_results(inc_rc, block, call_stack); } Self::replace_result_ids(dfg, &old_results, cached); @@ -416,19 +416,13 @@ impl<'brillig> Context<'brillig> { block: BasicBlockId, dfg: &mut DataFlowGraph, ) -> Vec { - let ctrl_typevars = instruction - .requires_ctrl_typevars() - .then(|| vecmap(old_results, |result| dfg.type_of_value(*result))); - let call_stack = dfg.get_call_stack(id); - let new_results = - match dfg.insert_instruction_and_results(instruction, block, ctrl_typevars, call_stack) - { - InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result], - InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results, - InsertInstructionResult::Results(_, new_results) => new_results.to_vec(), - InsertInstructionResult::InstructionRemoved => vec![], - }; + let new_results = match dfg.insert_instruction_and_results(instruction, block, call_stack) { + InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result], + InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results, + InsertInstructionResult::Results(_, new_results) => new_results.to_vec(), + InsertInstructionResult::InstructionRemoved => vec![], + }; // Optimizations while inserting the instruction should not change the number of results. assert_eq!(old_results.len(), new_results.len()); @@ -472,7 +466,11 @@ impl<'brillig> Context<'brillig> { self.use_constraint_info && instruction.requires_acir_gen_predicate(&function.dfg); let predicate = use_predicate.then_some(side_effects_enabled_var); - let array_get = Instruction::ArrayGet { array: instruction_results[0], index: *index }; + let array_get = Instruction::ArrayGet { + array: instruction_results[0], + index: *index, + result_type: function.dfg.type_of_value(*value), + }; self.cached_instruction_results .entry(array_get) @@ -586,7 +584,7 @@ impl<'brillig> Context<'brillig> { brillig_functions: &BTreeMap, dfg: &mut DataFlowGraph, ) -> EvaluationResult { - let Instruction::Call { func: func_id, arguments } = instruction else { + let Instruction::Call { func: func_id, arguments, .. } = instruction else { return EvaluationResult::NotABrilligCall; }; @@ -686,7 +684,7 @@ impl<'brillig> Context<'brillig> { elements: new_array_values, typ: Type::Array(types, length), }; - let instruction_id = dfg.make_instruction(instruction, None); + let instruction_id = dfg.make_instruction(instruction); dfg[block_id].instructions_mut().push(instruction_id); *dfg.instruction_results(instruction_id).first().unwrap() } diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index ded1f52d529..4afd89aff43 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -87,9 +87,7 @@ impl DefunctionalizationContext { let mut replacement_instruction = None; // Operate on call instructions let (target_func_id, arguments) = match &instruction { - Instruction::Call { func: target_func_id, arguments } => { - (*target_func_id, arguments) - } + Instruction::Call { func, arguments, .. } => (*func, arguments), _ => continue, }; @@ -98,9 +96,11 @@ impl DefunctionalizationContext { Value::Param { .. } | Value::Instruction { .. } => { let mut arguments = arguments.clone(); let results = func.dfg.instruction_results(instruction_id); + let returns = vecmap(results, |result| func.dfg.type_of_value(*result)); + let signature = Signature { params: vecmap(&arguments, |param| func.dfg.type_of_value(*param)), - returns: vecmap(results, |result| func.dfg.type_of_value(*result)), + returns: returns.clone(), }; // Find the correct apply function @@ -114,7 +114,8 @@ impl DefunctionalizationContext { let func = apply_function_value_id; call_target_values.insert(func); - replacement_instruction = Some(Instruction::Call { func, arguments }); + replacement_instruction = + Some(Instruction::Call { func, arguments, result_types: returns }); } Value::Function(..) => { call_target_values.insert(target_func_id); @@ -130,7 +131,7 @@ impl DefunctionalizationContext { // Change the type of all the values that are not call targets to NativeField let value_ids = vecmap(func.dfg.values_iter(), |(id, _)| id); for value_id in value_ids { - if let Type::Function = func.dfg[value_id].get_type().as_ref() { + if let Type::Function = func.dfg.type_of_value(value_id) { match &func.dfg[value_id] { // If the value is a static function, transform it to the function id Value::Function(id) => { @@ -229,12 +230,11 @@ fn find_dynamic_dispatches(func: &Function) -> BTreeSet { for instruction_id in block.instructions() { let instruction = &func.dfg[*instruction_id]; match instruction { - Instruction::Call { func: target, arguments } => { + Instruction::Call { func: target, arguments, result_types } => { if let Value::Param { .. } | Value::Instruction { .. } = &func.dfg[*target] { - let results = func.dfg.instruction_results(*instruction_id); dispatches.insert(Signature { params: vecmap(arguments, |param| func.dfg.type_of_value(*param)), - returns: vecmap(results, |result| func.dfg.type_of_value(*result)), + returns: result_types.clone(), }); } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index de3ac44652f..919e39cb197 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -167,7 +167,7 @@ impl Context { if instruction.can_eliminate_if_unused(function) { let results = function.dfg.instruction_results(instruction_id); results.iter().all(|result| !self.used_values.contains(result)) - } else if let Instruction::Call { func, arguments } = instruction { + } else if let Instruction::Call { func, arguments, .. } = instruction { // TODO: make this more general for instructions which don't have results but have side effects "sometimes" like `Intrinsic::AsWitness` let as_witness_id = function.dfg.get_intrinsic(Intrinsic::AsWitness); as_witness_id == Some(func) && !self.used_values.contains(&arguments[0]) @@ -276,7 +276,7 @@ impl Context { // This is an instruction that might be out of bounds: let's add a constrain. let (array, index) = match instruction { - Instruction::ArrayGet { array, index } + Instruction::ArrayGet { array, index, .. } | Instruction::ArraySet { array, index, .. } => (array, index), _ => panic!("Expected an ArrayGet or ArraySet instruction here"), }; @@ -297,7 +297,6 @@ impl Context { let index = function.dfg.insert_instruction_and_results( Instruction::Cast(*index, length_type), block_id, - None, call_stack.clone(), ); let index = index.first(); @@ -307,7 +306,6 @@ impl Context { let is_index_out_of_bounds = function.dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Lt, index, array_length), block_id, - None, call_stack.clone(), ); let is_index_out_of_bounds = is_index_out_of_bounds.first(); @@ -328,7 +326,6 @@ impl Context { function.dfg.insert_instruction_and_results( Instruction::Constrain(lhs, rhs, message), block_id, - None, call_stack, ); inserted_check = true; @@ -368,7 +365,7 @@ fn instruction_might_result_in_out_of_bounds( ) -> bool { use Instruction::*; match instruction { - ArrayGet { array, index } | ArraySet { array, index, .. } => { + ArrayGet { array, index, .. } | ArraySet { array, index, .. } => { if function.dfg.try_get_array_length(*array).is_some() { if let Some(known_index) = function.dfg.get_numeric_constant(*index) { // `index` will be relative to the flattened array length, so we need to take that into account @@ -496,14 +493,12 @@ fn apply_side_effects( // Condition needs to be cast to argument type in order to multiply them together. // In our case, lhs is always a boolean. let cast = Instruction::Cast(condition, NumericType::bool()); - let casted_condition = - dfg.insert_instruction_and_results(cast, block_id, None, call_stack.clone()); + let casted_condition = dfg.insert_instruction_and_results(cast, block_id, call_stack.clone()); let casted_condition = casted_condition.first(); let lhs = dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Mul, lhs, casted_condition), block_id, - None, call_stack.clone(), ); let lhs = lhs.first(); @@ -511,7 +506,6 @@ fn apply_side_effects( let rhs = dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Mul, rhs, casted_condition), block_id, - None, call_stack, ); let rhs = rhs.first(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index dc7952979e5..90a7d40203d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -286,7 +286,7 @@ impl<'f> Context<'f> { let previous_branch = context.else_branch.as_ref().unwrap_or(&context.then_branch); let and = Instruction::binary(BinaryOp::And, previous_branch.condition, condition); let call_stack = self.inserter.function.dfg.get_value_call_stack(condition); - self.insert_instruction(and, call_stack) + self.insert_instruction(and, call_stack).first() } else { condition } @@ -334,9 +334,8 @@ impl<'f> Context<'f> { // disable side effect for no_predicate functions let bool_type = NumericType::bool(); let one = self.inserter.function.dfg.make_constant(FieldElement::one(), bool_type); - self.insert_instruction_with_typevars( + self.insert_instruction( Instruction::EnableSideEffectsIf { condition: one }, - None, im::Vector::new(), ); self.push_instruction(*instruction); @@ -448,10 +447,12 @@ impl<'f> Context<'f> { let condition_call_stack = self.inserter.function.dfg.get_value_call_stack(cond_context.condition); - let else_condition = self.insert_instruction( - Instruction::Not(cond_context.condition), - condition_call_stack.clone(), - ); + let else_condition = self + .insert_instruction( + Instruction::Not(cond_context.condition), + condition_call_stack.clone(), + ) + .first(); let else_condition = self.link_condition(else_condition); let old_allocations = std::mem::take(&mut self.local_allocations); @@ -553,7 +554,7 @@ impl<'f> Context<'f> { self.inserter .function .dfg - .insert_instruction_and_results(instruction, block, None, call_stack) + .insert_instruction_and_results(instruction, block, call_stack) .first() }); @@ -566,32 +567,14 @@ impl<'f> Context<'f> { /// Insert a new instruction into the function's entry block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStack) -> ValueId { - let block = self.inserter.function.entry_block(); - self.inserter - .function - .dfg - .insert_instruction_and_results(instruction, block, None, call_stack) - .first() - } - - /// Inserts a new instruction into the function's entry block, using the given - /// control type variables to specify result types if needed. - /// Unlike push_instruction, this function will not map any ValueIds. - /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction_with_typevars( + fn insert_instruction( &mut self, instruction: Instruction, - ctrl_typevars: Option>, call_stack: CallStack, ) -> InsertInstructionResult { let block = self.inserter.function.entry_block(); - self.inserter.function.dfg.insert_instruction_and_results( - instruction, - block, - ctrl_typevars, - call_stack, - ) + let dfg = &mut self.inserter.function.dfg; + dfg.insert_instruction_and_results(instruction, block, call_stack) } /// Checks the branch condition on the top of the stack and uses it to build and insert an @@ -608,7 +591,7 @@ impl<'f> Context<'f> { }; let enable_side_effects = Instruction::EnableSideEffectsIf { condition }; let call_stack = self.inserter.function.dfg.get_value_call_stack(condition); - self.insert_instruction_with_typevars(enable_side_effects, None, call_stack); + self.insert_instruction(enable_side_effects, call_stack); } /// Push the given instruction to the end of the entry block of the current function. @@ -625,7 +608,7 @@ impl<'f> Context<'f> { let (instruction, call_stack) = self.inserter.map_instruction(id); let instruction = self.handle_instruction_side_effects(instruction, call_stack.clone()); - let instruction_is_allocate = matches!(&instruction, Instruction::Allocate); + let instruction_is_allocate = matches!(&instruction, Instruction::Allocate { .. }); let entry = self.inserter.function.entry_block(); let results = self.inserter.push_instruction_value(instruction, id, entry, call_stack); @@ -652,16 +635,21 @@ impl<'f> Context<'f> { let argument_type = self.inserter.function.dfg.type_of_value(lhs); let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); + let casted_condition = + self.insert_instruction(cast, call_stack.clone()).first(); - let lhs = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, lhs, casted_condition), - call_stack.clone(), - ); - let rhs = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, rhs, casted_condition), - call_stack, - ); + let lhs = self + .insert_instruction( + Instruction::binary(BinaryOp::Mul, lhs, casted_condition), + call_stack.clone(), + ) + .first(); + let rhs = self + .insert_instruction( + Instruction::binary(BinaryOp::Mul, rhs, casted_condition), + call_stack, + ) + .first(); Instruction::Constrain(lhs, rhs, message) } @@ -673,17 +661,13 @@ impl<'f> Context<'f> { } else { // Instead of storing `value`, store `if condition { value } else { previous_value }` let typ = self.inserter.function.dfg.type_of_value(value); - let load = Instruction::Load { address }; - let previous_value = self - .insert_instruction_with_typevars( - load, - Some(vec![typ]), - call_stack.clone(), - ) - .first(); + let load = Instruction::Load { address, result_type: typ }; + let previous_value = + self.insert_instruction(load, call_stack.clone()).first(); let else_condition = self - .insert_instruction(Instruction::Not(condition), call_stack.clone()); + .insert_instruction(Instruction::Not(condition), call_stack.clone()) + .first(); let instruction = Instruction::IfElse { then_condition: condition, @@ -692,7 +676,8 @@ impl<'f> Context<'f> { else_value: previous_value, }; - let updated_value = self.insert_instruction(instruction, call_stack); + let updated_value = + self.insert_instruction(instruction, call_stack).first(); Instruction::Store { address, value: updated_value } } } @@ -702,62 +687,71 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(value); let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); + let casted_condition = + self.insert_instruction(cast, call_stack.clone()).first(); - let value = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, value, casted_condition), - call_stack.clone(), - ); + let value = self + .insert_instruction( + Instruction::binary(BinaryOp::Mul, value, casted_condition), + call_stack.clone(), + ) + .first(); Instruction::RangeCheck { value, max_bit_size, assert_message } } - Instruction::Call { func, mut arguments } => match self.inserter.function.dfg[func] - { - Value::Intrinsic(Intrinsic::ToBits(_) | Intrinsic::ToRadix(_)) => { - let field = arguments[0]; - let argument_type = self.inserter.function.dfg.type_of_value(field); - - let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); - let casted_condition = self.insert_instruction(cast, call_stack.clone()); - let field = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, field, casted_condition), - call_stack.clone(), - ); - - arguments[0] = field; - - Instruction::Call { func, arguments } - } - //Issue #5045: We set curve points to infinity if condition is false - Value::Intrinsic(Intrinsic::BlackBox(BlackBoxFunc::EmbeddedCurveAdd)) => { - arguments[2] = self.var_or_one(arguments[2], condition, call_stack.clone()); - arguments[5] = self.var_or_one(arguments[5], condition, call_stack.clone()); - - Instruction::Call { func, arguments } - } - Value::Intrinsic(Intrinsic::BlackBox(BlackBoxFunc::MultiScalarMul)) => { - let points_array_idx = if matches!( - self.inserter.function.dfg.type_of_value(arguments[0]), - Type::Array { .. } - ) { - 0 - } else { - // if the first argument is not an array, we assume it is a slice - // which means the array is the second argument - 1 - }; - let (elements, typ) = self.apply_predicate_to_msm_argument( - arguments[points_array_idx], - condition, - call_stack.clone(), - ); - - let instruction = Instruction::MakeArray { elements, typ }; - let array = self.insert_instruction(instruction, call_stack); - arguments[points_array_idx] = array; - Instruction::Call { func, arguments } + Instruction::Call { func, mut arguments, result_types } => { + match self.inserter.function.dfg[func] { + Value::Intrinsic(Intrinsic::ToBits(_) | Intrinsic::ToRadix(_)) => { + let field = arguments[0]; + let argument_type = self.inserter.function.dfg.type_of_value(field); + + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = + self.insert_instruction(cast, call_stack.clone()).first(); + let field = self + .insert_instruction( + Instruction::binary(BinaryOp::Mul, field, casted_condition), + call_stack.clone(), + ) + .first(); + + arguments[0] = field; + + Instruction::Call { func, arguments, result_types } + } + //Issue #5045: We set curve points to infinity if condition is false + Value::Intrinsic(Intrinsic::BlackBox(BlackBoxFunc::EmbeddedCurveAdd)) => { + arguments[2] = + self.var_or_one(arguments[2], condition, call_stack.clone()); + arguments[5] = + self.var_or_one(arguments[5], condition, call_stack.clone()); + + Instruction::Call { func, arguments, result_types } + } + Value::Intrinsic(Intrinsic::BlackBox(BlackBoxFunc::MultiScalarMul)) => { + let points_array_idx = if matches!( + self.inserter.function.dfg.type_of_value(arguments[0]), + Type::Array { .. } + ) { + 0 + } else { + // if the first argument is not an array, we assume it is a slice + // which means the array is the second argument + 1 + }; + let (elements, typ) = self.apply_predicate_to_msm_argument( + arguments[points_array_idx], + condition, + call_stack.clone(), + ); + + let instruction = Instruction::MakeArray { elements, typ }; + let array = self.insert_instruction(instruction, call_stack).first(); + arguments[points_array_idx] = array; + Instruction::Call { func, arguments, result_types } + } + _ => Instruction::Call { func, arguments, result_types }, } - _ => Instruction::Call { func, arguments }, - }, + } other => other, } } else { @@ -801,16 +795,19 @@ impl<'f> Context<'f> { // Computes: if condition { var } else { 1 } fn var_or_one(&mut self, var: ValueId, condition: ValueId, call_stack: CallStack) -> ValueId { - let field = self.insert_instruction( - Instruction::binary(BinaryOp::Mul, var, condition), - call_stack.clone(), - ); + let field = self + .insert_instruction( + Instruction::binary(BinaryOp::Mul, var, condition), + call_stack.clone(), + ) + .first(); let not_condition = - self.insert_instruction(Instruction::Not(condition), call_stack.clone()); + self.insert_instruction(Instruction::Not(condition), call_stack.clone()).first(); self.insert_instruction( Instruction::binary(BinaryOp::Add, field, not_condition), call_stack, ) + .first() } } @@ -1181,7 +1178,7 @@ mod test { instructions.iter().position(|id| predicate(&main.dfg[*id])).unwrap() }; - let allocate_index = find_instruction(|i| matches!(i, Instruction::Allocate)); + let allocate_index = find_instruction(|i| matches!(i, Instruction::Allocate { .. })); let store_index = find_instruction(|i| matches!(i, Instruction::Store { .. })); let load_index = find_instruction(|i| matches!(i, Instruction::Load { .. })); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs index a01be691778..c73c4881217 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs @@ -53,7 +53,7 @@ impl<'a> SliceCapacityTracker<'a> { slice_sizes.insert(results[0], *capacity); } } - Instruction::Call { func, arguments } => { + Instruction::Call { func, arguments, result_types: _ } => { let func = &self.dfg[*func]; if let Value::Intrinsic(intrinsic) = func { let (argument_index, result_index) = match intrinsic { @@ -136,9 +136,8 @@ impl<'a> SliceCapacityTracker<'a> { slice_sizes.insert(*address, *value_capacity); } } - Instruction::Load { address } => { - let load_typ = self.dfg.type_of_value(*address); - if load_typ.contains_slice_element() { + Instruction::Load { address, result_type } => { + if result_type.contains_slice_element() { let result = results[0]; let address_capacity = slice_sizes.get(address).unwrap_or_else(|| { diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index c2b071a9c9a..9610701eabc 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -114,22 +114,20 @@ impl<'a> ValueMerger<'a> { // We must cast the bool conditions to the actual numeric type used by each value. let cast = Instruction::Cast(then_condition, then_type); let then_condition = - dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(cast, block, call_stack.clone()).first(); let cast = Instruction::Cast(else_condition, else_type); let else_condition = - dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + dfg.insert_instruction_and_results(cast, block, call_stack.clone()).first(); let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); - let then_value = - dfg.insert_instruction_and_results(mul, block, None, call_stack.clone()).first(); + let then_value = dfg.insert_instruction_and_results(mul, block, call_stack.clone()).first(); let mul = Instruction::binary(BinaryOp::Mul, else_condition, else_value); - let else_value = - dfg.insert_instruction_and_results(mul, block, None, call_stack.clone()).first(); + let else_value = dfg.insert_instruction_and_results(mul, block, call_stack.clone()).first(); let add = Instruction::binary(BinaryOp::Add, then_value, else_value); - dfg.insert_instruction_and_results(add, block, None, call_stack).first() + dfg.insert_instruction_and_results(add, block, call_stack).first() } /// Given an if expression that returns an array: `if c { array1 } else { array2 }`, @@ -168,22 +166,16 @@ impl<'a> ValueMerger<'a> { ((i * element_types.len() as u32 + element_index as u32) as u128).into(); let index = self.dfg.make_constant(index, NumericType::NativeField); - let typevars = Some(vec![element_type.clone()]); - - let mut get_element = |array, typevars| { - let get = Instruction::ArrayGet { array, index }; + let mut get_element = |array| { + let result_type = element_type.clone(); + let get = Instruction::ArrayGet { array, index, result_type }; self.dfg - .insert_instruction_and_results( - get, - self.block, - typevars, - self.call_stack.clone(), - ) + .insert_instruction_and_results(get, self.block, self.call_stack.clone()) .first() }; - let then_element = get_element(then_value, typevars.clone()); - let else_element = get_element(else_value, typevars); + let then_element = get_element(then_value); + let else_element = get_element(else_value); merged.push_back(self.merge_values( then_condition, @@ -196,7 +188,7 @@ impl<'a> ValueMerger<'a> { let instruction = Instruction::MakeArray { elements: merged, typ }; let call_stack = self.call_stack.clone(); - self.dfg.insert_instruction_and_results(instruction, self.block, None, call_stack).first() + self.dfg.insert_instruction_and_results(instruction, self.block, call_stack).first() } fn merge_slice_values( @@ -236,33 +228,27 @@ impl<'a> ValueMerger<'a> { let index_value = (index_u32 as u128).into(); let index = self.dfg.make_constant(index_value, NumericType::NativeField); - let typevars = Some(vec![element_type.clone()]); - - let mut get_element = |array, typevars, len| { + let mut get_element = |array, len| { // The smaller slice is filled with placeholder data. Codegen for slice accesses must // include checks against the dynamic slice length so that this placeholder data is not incorrectly accessed. if len <= index_u32 { self.make_slice_dummy_data(element_type) } else { - let get = Instruction::ArrayGet { array, index }; + let result_type = element_type.clone(); + let get = Instruction::ArrayGet { array, index, result_type }; self.dfg .insert_instruction_and_results( get, self.block, - typevars, self.call_stack.clone(), ) .first() } }; - let then_element = get_element( - then_value_id, - typevars.clone(), - then_len * element_types.len() as u32, - ); - let else_element = - get_element(else_value_id, typevars, else_len * element_types.len() as u32); + let element_count = element_types.len() as u32; + let then_element = get_element(then_value_id, then_len * element_count); + let else_element = get_element(else_value_id, else_len * element_count); merged.push_back(self.merge_values( then_condition, @@ -275,7 +261,7 @@ impl<'a> ValueMerger<'a> { let instruction = Instruction::MakeArray { elements: merged, typ }; let call_stack = self.call_stack.clone(); - self.dfg.insert_instruction_and_results(instruction, self.block, None, call_stack).first() + self.dfg.insert_instruction_and_results(instruction, self.block, call_stack).first() } /// Construct a dummy value to be attached to the smaller of two slices being merged. @@ -297,9 +283,7 @@ impl<'a> ValueMerger<'a> { } let instruction = Instruction::MakeArray { elements: array, typ: typ.clone() }; let call_stack = self.call_stack.clone(); - self.dfg - .insert_instruction_and_results(instruction, self.block, None, call_stack) - .first() + self.dfg.insert_instruction_and_results(instruction, self.block, call_stack).first() } Type::Slice(_) => { // TODO(#3188): Need to update flattening to use true user facing length of slices @@ -382,25 +366,18 @@ impl<'a> ValueMerger<'a> { let mut array = then_value; for (index, element_type, condition) in changed_indices { - let typevars = Some(vec![element_type.clone()]); - let instruction = Instruction::EnableSideEffectsIf { condition }; self.insert_instruction(instruction); - let mut get_element = |array, typevars| { - let get = Instruction::ArrayGet { array, index }; + let mut get_element = |array, result_type| { + let get = Instruction::ArrayGet { array, index, result_type }; self.dfg - .insert_instruction_and_results( - get, - self.block, - typevars, - self.call_stack.clone(), - ) + .insert_instruction_and_results(get, self.block, self.call_stack.clone()) .first() }; - let then_element = get_element(then_value, typevars.clone()); - let else_element = get_element(else_value, typevars); + let then_element = get_element(then_value, element_type.clone()); + let else_element = get_element(else_value, element_type); let value = self.merge_values(then_condition, else_condition, then_element, else_element); @@ -414,12 +391,7 @@ impl<'a> ValueMerger<'a> { } fn insert_instruction(&mut self, instruction: Instruction) -> InsertInstructionResult { - self.dfg.insert_instruction_and_results( - instruction, - self.block, - None, - self.call_stack.clone(), - ) + self.dfg.insert_instruction_and_results(instruction, self.block, self.call_stack.clone()) } fn insert_array_set( @@ -433,7 +405,6 @@ impl<'a> ValueMerger<'a> { let result = self.dfg.insert_instruction_and_results( instruction, self.block, - None, self.call_stack.clone(), ); diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 37659ec7c98..346f03f6610 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -600,28 +600,30 @@ impl<'function> PerFunctionContext<'function> { let block = &self.source_function.dfg[block_id]; for id in block.instructions() { match &self.source_function.dfg[*id] { - Instruction::Call { func, arguments } => match self.get_function(*func) { - Some(func_id) => { - if self.should_inline_call(ssa, func_id) { - self.inline_function(ssa, *id, func_id, arguments); - - // This is only relevant during handling functions with `InlineType::NoPredicates` as these - // can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`, - // resulting in predicates not being applied properly. - // - // Note that this doesn't cover the case in which there exists an `Instruction::EnabledSideEffects` - // within the function being inlined whilst the source function has not encountered one yet. - // In practice this isn't an issue as the last `Instruction::EnabledSideEffects` in the - // function being inlined will be to turn off predicates rather than to create one. - if let Some(condition) = side_effects_enabled { - self.context.builder.insert_enable_side_effects_if(condition); + Instruction::Call { func, arguments, result_types: _ } => { + match self.get_function(*func) { + Some(func_id) => { + if self.should_inline_call(ssa, func_id) { + self.inline_function(ssa, *id, func_id, arguments); + + // This is only relevant during handling functions with `InlineType::NoPredicates` as these + // can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`, + // resulting in predicates not being applied properly. + // + // Note that this doesn't cover the case in which there exists an `Instruction::EnabledSideEffects` + // within the function being inlined whilst the source function has not encountered one yet. + // In practice this isn't an issue as the last `Instruction::EnabledSideEffects` in the + // function being inlined will be to turn off predicates rather than to create one. + if let Some(condition) = side_effects_enabled { + self.context.builder.insert_enable_side_effects_if(condition); + } + } else { + self.push_instruction(*id); } - } else { - self.push_instruction(*id); } + None => self.push_instruction(*id), } - None => self.push_instruction(*id), - }, + } Instruction::EnableSideEffectsIf { condition } => { side_effects_enabled = Some(self.translate_value(*condition)); self.push_instruction(*id); @@ -683,13 +685,9 @@ impl<'function> PerFunctionContext<'function> { let results = self.source_function.dfg.instruction_results(id); let results = vecmap(results, |id| self.source_function.dfg.resolve(*id)); - let ctrl_typevars = instruction - .requires_ctrl_typevars() - .then(|| vecmap(&results, |result| self.source_function.dfg.type_of_value(*result))); - self.context.builder.set_call_stack(call_stack); - let new_results = self.context.builder.insert_instruction(instruction, ctrl_typevars); + let new_results = self.context.builder.insert_instruction(instruction); Self::insert_new_instruction_results(&mut self.values, &results, new_results); } diff --git a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 87e7f8bcff3..1f64c9fdfa9 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -205,7 +205,7 @@ impl<'f> LoopInvariantContext<'f> { /// we can safely hoist the array access. fn can_be_deduplicated_from_upper_bound(&self, instruction: &Instruction) -> bool { match instruction { - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, result_type: _ } => { let array_typ = self.inserter.function.dfg.type_of_value(*array); let upper_bound = self.outer_induction_variables.get(index); if let (Type::Array(_, len), Some(upper_bound)) = (array_typ, upper_bound) { diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 77ad53df9cf..466b84467ba 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -407,7 +407,7 @@ impl<'f> PerFunctionContext<'f> { } match &self.inserter.function.dfg[instruction] { - Instruction::Load { address } => { + Instruction::Load { address, result_type: _ } => { let address = self.inserter.function.dfg.resolve(*address); let result = self.inserter.function.dfg.instruction_results(instruction)[0]; @@ -427,7 +427,7 @@ impl<'f> PerFunctionContext<'f> { // Check whether the block has a repeat load from the same address (w/ no calls or stores in between the loads). // If we do have a repeat load, we can remove the current load and map its result to the previous load's result. if let Some(last_load) = references.last_loads.get(&address) { - let Instruction::Load { address: previous_address } = + let Instruction::Load { address: previous_address, result_type: _ } = &self.inserter.function.dfg[*last_load] else { panic!("Expected a Load instruction here"); @@ -474,7 +474,7 @@ impl<'f> PerFunctionContext<'f> { references.keep_last_load_for(address, self.inserter.function); references.last_stores.insert(address, instruction); } - Instruction::Allocate => { + Instruction::Allocate { element_type: _ } => { // Register the new reference let result = self.inserter.function.dfg.instruction_results(instruction)[0]; references.expressions.insert(result, Expression::Other(result)); diff --git a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index f5e96224260..bacdca154b2 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -11,7 +11,6 @@ use crate::ssa::{ ssa_gen::Ssa, }; use fxhash::FxHashMap as HashMap; -use iter_extended::vecmap; impl Ssa { /// This is a debugging pass which re-inserts each instruction @@ -89,14 +88,9 @@ impl Context { let call_stack = old_function.dfg.get_call_stack(old_instruction_id); let old_results = old_function.dfg.instruction_results(old_instruction_id); - let ctrl_typevars = instruction - .requires_ctrl_typevars() - .then(|| vecmap(old_results, |result| old_function.dfg.type_of_value(*result))); - let new_results = new_function.dfg.insert_instruction_and_results( instruction, new_block_id, - ctrl_typevars, call_stack, ); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 872c7920a77..84d76b9af1d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -237,13 +237,13 @@ impl Context<'_> { rhs: ValueId, ) -> ValueId { let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); - self.insert_instruction(instruction, None).first() + self.insert_instruction(instruction).first() } /// Insert a not instruction at the end of the current block. /// Returns the result of the instruction. pub(crate) fn insert_not(&mut self, rhs: ValueId) -> ValueId { - self.insert_instruction(Instruction::Not(rhs), None).first() + self.insert_instruction(Instruction::Not(rhs)).first() } /// Insert a truncate instruction at the end of the current block. @@ -254,14 +254,13 @@ impl Context<'_> { bit_size: u32, max_bit_size: u32, ) -> ValueId { - self.insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }, None) - .first() + self.insert_instruction(Instruction::Truncate { value, bit_size, max_bit_size }).first() } /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { - self.insert_instruction(Instruction::Cast(value, typ), None).first() + self.insert_instruction(Instruction::Cast(value, typ)).first() } /// Insert a call instruction at the end of the current block and return @@ -272,7 +271,8 @@ impl Context<'_> { arguments: Vec, result_types: Vec, ) -> Cow<[ValueId]> { - self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results() + let call = Instruction::Call { func, arguments, result_types }; + self.insert_instruction(call).results() } /// Insert an instruction to extract an element from an array @@ -280,21 +280,18 @@ impl Context<'_> { &mut self, array: ValueId, index: ValueId, - element_type: Type, + result_type: Type, ) -> ValueId { - let element_type = Some(vec![element_type]); - self.insert_instruction(Instruction::ArrayGet { array, index }, element_type).first() + self.insert_instruction(Instruction::ArrayGet { array, index, result_type }).first() } pub(crate) fn insert_instruction( &mut self, instruction: Instruction, - ctrl_typevars: Option>, ) -> InsertInstructionResult { let result = self.function.dfg.insert_instruction_and_results( instruction, self.block, - ctrl_typevars, self.call_stack.clone(), ); diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 4119494b287..2f8edc02be6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -455,7 +455,7 @@ impl Loop { // We insert into a fresh block first and move instructions into the unroll_into block later // only once we verify the jmpif instruction has a constant condition. If it does not, we can // just discard this fresh block and leave the loop unmodified. - let fresh_block = function.dfg.make_block(Vec::new()); + let fresh_block = function.dfg.make_block(); let mut context = LoopIteration::new(function, self, fresh_block, self.header); let source_block = &context.dfg()[context.source_block]; @@ -561,7 +561,7 @@ impl Loop { let allocations = blocks.iter().flat_map(|block| { let instructions = function.dfg[*block].instructions().iter(); instructions - .filter(|i| matches!(&function.dfg[**i], Instruction::Allocate)) + .filter(|i| matches!(&function.dfg[**i], Instruction::Allocate { .. })) // Get the value into which the allocation was stored. .map(|i| function.dfg.instruction_results(*i)[0]) });