diff --git a/compiler/noirc_evaluator/src/acir/arrays.rs b/compiler/noirc_evaluator/src/acir/arrays.rs index 81ceda052d5..7b67c390848 100644 --- a/compiler/noirc_evaluator/src/acir/arrays.rs +++ b/compiler/noirc_evaluator/src/acir/arrays.rs @@ -14,9 +14,9 @@ //! ACIR generation use twos different array types for representing arrays: //! //! [Constant arrays][AcirValue::Array] -//! - Known at compile time. +//! - Known at compile time. //! - Reads and writes may be folded into an [AcirValue] where possible. -//! - Useful for optimization (e.g., constant element lookups do not require laying down opcodes) +//! - Useful for optimization (e.g., constant element lookups do not require laying down opcodes) //! //! [Dynamic arrays][AcirValue::DynamicArray] //! - Referenced by a [unique identifier][BlockId] @@ -562,8 +562,8 @@ impl Context<'_> { index_side_effect: bool, ) -> Result<(), RuntimeError> { let block_id = self.ensure_array_is_initialized(array, dfg)?; - let results = dfg.instruction_results(instruction); - let res_typ = dfg.type_of_value(results[0]); + let [result] = dfg.instruction_result(instruction); + let res_typ = dfg.type_of_value(result); let value = self.load_array_value(array, block_id, var_index, &res_typ, dfg)?; diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index 38439f9b2c0..80f37dd901f 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -569,7 +569,7 @@ impl<'a> Context<'a> { Instruction::MakeArray { elements, typ: _ } => { let elements = elements.iter().map(|element| self.convert_value(*element, dfg)); let value = AcirValue::Array(elements.collect()); - let result = dfg.instruction_results(instruction_id)[0]; + let [result] = dfg.instruction_result(instruction_id); self.ssa_values.insert(result, value); } Instruction::Noop => (), @@ -586,8 +586,8 @@ impl<'a> Context<'a> { instruction: InstructionId, result: AcirValue, ) { - let result_ids = dfg.instruction_results(instruction); - self.ssa_values.insert(result_ids[0], result); + let [result_id] = dfg.instruction_result(instruction); + self.ssa_values.insert(result_id, result); } /// Remember the result of instruction returning a single numeric value @@ -597,8 +597,8 @@ impl<'a> Context<'a> { instruction: InstructionId, result: AcirVar, ) { - let result_ids = dfg.instruction_results(instruction); - let typ = dfg.type_of_value(result_ids[0]).into(); + let [result_id] = dfg.instruction_result(instruction); + let typ = dfg.type_of_value(result_id).into(); self.define_result(dfg, instruction, AcirValue::Var(result, typ)); } 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 fdeeda8e695..d1f77377de4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -339,10 +339,11 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { ); match instruction { Instruction::Binary(binary) => { + let [result_value] = dfg.instruction_result(instruction_id); let result_var = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, - dfg.instruction_results(instruction_id)[0], + result_value, dfg, ); self.convert_ssa_binary(binary, dfg, result_var); @@ -411,7 +412,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { } Instruction::Allocate => { - let result_value = dfg.instruction_results(instruction_id)[0]; + let [result_value] = dfg.instruction_result(instruction_id); let pointer = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, @@ -428,10 +429,12 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { .store_instruction(address_var.address, source_variable.extract_register()); } Instruction::Load { address } => { + let [result_value] = dfg.instruction_result(instruction_id); + let target_variable = self.variables.define_variable( self.function_context, self.brillig_context, - dfg.instruction_results(instruction_id)[0], + result_value, dfg, ); @@ -441,11 +444,12 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { .load_instruction(target_variable.extract_register(), address_variable.address); } Instruction::Not(value) => { + let [result_value] = dfg.instruction_result(instruction_id); let condition_register = self.convert_ssa_single_addr_value(*value, dfg); let result_register = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, - dfg.instruction_results(instruction_id)[0], + result_value, dfg, ); self.brillig_context.not_instruction(condition_register, result_register); @@ -538,10 +542,11 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { // can't automatically insert any missing cases match intrinsic { Intrinsic::ArrayLen => { + let [result_value] = dfg.instruction_result(instruction_id); let result_variable = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, - dfg.instruction_results(instruction_id)[0], + result_value, dfg, ); let param_id = arguments[0]; @@ -630,7 +635,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { ); } Intrinsic::ToBits(endianness) => { - let results = dfg.instruction_results(instruction_id); + let [result] = dfg.instruction_result(instruction_id); let source = self.convert_ssa_single_addr_value(arguments[0], dfg); @@ -639,7 +644,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { .define_variable( self.function_context, self.brillig_context, - results[0], + result, dfg, ) .extract_array(); @@ -660,7 +665,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { } Intrinsic::ToRadix(endianness) => { - let results = dfg.instruction_results(instruction_id); + let [result] = dfg.instruction_result(instruction_id); let source = self.convert_ssa_single_addr_value(arguments[0], dfg); let radix = self.convert_ssa_single_addr_value(arguments[1], dfg); @@ -670,7 +675,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { .define_variable( self.function_context, self.brillig_context, - results[0], + result, dfg, ) .extract_array(); @@ -755,13 +760,13 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { let rhs = self.convert_ssa_single_addr_value(arguments[1], dfg); assert!(rhs.bit_size == FieldElement::max_num_bits()); - let results = dfg.instruction_results(instruction_id); + let [result] = dfg.instruction_result(instruction_id); let destination = self .variables .define_variable( self.function_context, self.brillig_context, - results[0], + result, dfg, ) .extract_single_addr(); @@ -776,7 +781,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { } Intrinsic::ArrayRefCount => { let array = self.convert_ssa_value(arguments[0], dfg); - let result = dfg.instruction_results(instruction_id)[0]; + let [result] = dfg.instruction_result(instruction_id); let destination = self.variables.define_variable( self.function_context, @@ -790,7 +795,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { } Intrinsic::SliceRefCount => { let array = self.convert_ssa_value(arguments[1], dfg); - let result = dfg.instruction_results(instruction_id)[0]; + let [result] = dfg.instruction_result(instruction_id); let destination = self.variables.define_variable( self.function_context, @@ -821,11 +826,11 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { } }, Instruction::Truncate { value, bit_size, .. } => { - let result_ids = dfg.instruction_results(instruction_id); + let [result_id] = dfg.instruction_result(instruction_id); let destination_register = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, - result_ids[0], + result_id, dfg, ); let source_register = self.convert_ssa_single_addr_value(*value, dfg); @@ -836,22 +841,22 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { ); } Instruction::Cast(value, _) => { - let result_ids = dfg.instruction_results(instruction_id); + let [result_id] = dfg.instruction_result(instruction_id); let destination_variable = self.variables.define_single_addr_variable( self.function_context, self.brillig_context, - result_ids[0], + result_id, dfg, ); let source_variable = self.convert_ssa_single_addr_value(*value, dfg); self.convert_cast(destination_variable, source_variable); } Instruction::ArrayGet { array, index } => { - let result_ids = dfg.instruction_results(instruction_id); + let [result_id] = dfg.instruction_result(instruction_id); let destination_variable = self.variables.define_variable( self.function_context, self.brillig_context, - result_ids[0], + result_id, dfg, ); @@ -978,10 +983,11 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { let then_condition = self.convert_ssa_single_addr_value(*then_condition, dfg); let then_value = self.convert_ssa_value(*then_value, dfg); let else_value = self.convert_ssa_value(*else_value, dfg); + let [result_value] = dfg.instruction_result(instruction_id); let result = self.variables.define_variable( self.function_context, self.brillig_context, - dfg.instruction_results(instruction_id)[0], + result_value, dfg, ); match (then_value, else_value) { diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 4ea42f1e48d..68c8cb18ee3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -559,6 +559,19 @@ impl DataFlowGraph { self.results.get(&instruction_id).expect("expected a list of Values").as_slice() } + /// Returns N results, asserting that there are exactly N items. + pub(crate) fn instruction_result( + &self, + instruction_id: InstructionId, + ) -> [ValueId; N] { + let results = self.instruction_results(instruction_id); + if results.len() != N { + let instruction = &self[instruction_id]; + panic!("expected {instruction:?} to have {N} results; got {}", results.len()); + } + std::array::from_fn(|i| results[i]) + } + /// Remove an instruction by replacing it with a `Noop` instruction. /// Doing this avoids shifting over each instruction after this one in its block's instructions vector. #[allow(unused)] 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 fe056569368..da20e9a3333 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -52,8 +52,7 @@ impl Function { unreachable!("AsSlice called with non-array {}", array_typ); }; - let call_returns = context.dfg.instruction_results(instruction_id); - let original_slice_length = call_returns[0]; + let [original_slice_length, _] = context.dfg.instruction_result(instruction_id); let known_length = context.dfg.make_constant(length.into(), NumericType::length_type()); context.replace_value(original_slice_length, known_length); }); diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding/interpret.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding/interpret.rs index c1276f762e8..25a89e24982 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding/interpret.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding/interpret.rs @@ -144,7 +144,7 @@ fn interpreter_value_to_ir_value( let instruction_id = dfg.make_instruction(instruction, None); dfg[block_id].instructions_mut().push(instruction_id); - *dfg.instruction_results(instruction_id).first().unwrap() + dfg.instruction_result::<1>(instruction_id)[0] } Type::Slice(element_types) => { let array = match value { @@ -161,7 +161,7 @@ fn interpreter_value_to_ir_value( let instruction_id = dfg.make_instruction(instruction, None); dfg[block_id].instructions_mut().push(instruction_id); - *dfg.instruction_results(instruction_id).first().unwrap() + dfg.instruction_result::<1>(instruction_id)[0] } Type::Function | Type::Reference(_) => unreachable!("Cannot be a constant value"), } diff --git a/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs b/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs index 581cdcce118..7022eeb7063 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/expand_signed_checks.rs @@ -61,7 +61,7 @@ impl Function { // We remove the current instruction, as we will need to replace it with multiple new instructions. context.remove_current_instruction(); - let old_result = *context.dfg.instruction_results(instruction_id).first().unwrap(); + let [old_result] = context.dfg.instruction_result(instruction_id); let mut expansion_context = Context { context }; let new_result = match operator { diff --git a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index 8570c9954a8..01eb5241c84 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -432,8 +432,8 @@ impl<'f> LoopInvariantContext<'f> { // If we are hoisting a MakeArray instruction, // we need to issue an extra inc_rc in case they are mutated afterward. if insert_rc { - let result = - self.inserter.function.dfg.instruction_results(instruction_id)[0]; + let [result] = + self.inserter.function.dfg.instruction_result(instruction_id); let inc_rc = Instruction::IncrementRc { value: result }; let call_stack = self .inserter diff --git a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant/simplify.rs b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant/simplify.rs index 80f9ad0a3c1..448b8b5b9f9 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant/simplify.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant/simplify.rs @@ -80,10 +80,8 @@ impl LoopInvariantContext<'_> { // Simplify the instruction and update it in the DFG. match self.simplify_induction_variable(loop_context, block_context, instruction_id) { SimplifyResult::SimplifiedTo(id) => { - let results = - self.inserter.function.dfg.instruction_results(instruction_id).to_vec(); - assert!(results.len() == 1); - self.inserter.map_value(results[0], id); + let [result] = self.inserter.function.dfg.instruction_result(instruction_id); + self.inserter.map_value(result, id); true } SimplifyResult::SimplifiedToInstruction(instruction) => { diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index b6ef2dad0ee..132991b1b0d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -471,12 +471,12 @@ impl<'f> PerFunctionContext<'f> { Instruction::Load { address } => { let address = *address; - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); references.remember_dereference(self.inserter.function, address, result); // If the load is known, replace it with the known value and remove the load. if let Some(value) = references.get_known_value(address) { - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); self.inserter.map_value(result, value); self.instructions_to_remove.insert(instruction); } @@ -488,9 +488,9 @@ impl<'f> PerFunctionContext<'f> { else { panic!("Expected a Load instruction here"); }; - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; - let previous_result = - self.inserter.function.dfg.instruction_results(*last_load)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); + let [previous_result] = + self.inserter.function.dfg.instruction_result(*last_load); if *previous_address == address { self.inserter.map_value(result, previous_result); self.instructions_to_remove.insert(instruction); @@ -541,12 +541,12 @@ impl<'f> PerFunctionContext<'f> { } Instruction::Allocate => { // Register the new reference - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); references.expressions.insert(result, Expression::Other(result)); references.aliases.insert(Expression::Other(result), AliasSet::known(result)); } Instruction::ArrayGet { array, .. } => { - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); if self.inserter.function.dfg.type_of_value(result).contains_reference() { let array = *array; @@ -575,7 +575,7 @@ impl<'f> PerFunctionContext<'f> { let element_type = self.inserter.function.dfg.type_of_value(*value); if element_type.contains_reference() { - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); let array = *array; let expression = references.expressions.get(&array).copied(); @@ -620,7 +620,7 @@ impl<'f> PerFunctionContext<'f> { // If `array` is an array constant that contains reference types, then insert each element // as a potential alias to the array itself. if typ.contains_reference() { - let array = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [array] = self.inserter.function.dfg.instruction_result(instruction); let expr = Expression::ArrayElement(array); references.expressions.insert(array, expr); @@ -630,7 +630,7 @@ impl<'f> PerFunctionContext<'f> { } } Instruction::IfElse { then_value, else_value, .. } => { - let result = self.inserter.function.dfg.instruction_results(instruction)[0]; + let [result] = self.inserter.function.dfg.instruction_result(instruction); let result_type = self.inserter.function.dfg.type_of_value(result); if result_type.contains_reference() { @@ -2357,7 +2357,7 @@ mod tests { g9 = make_array [Field 1, Field 2, Field 3] : [Field; 3] g10 = make_array [Field 1, Field 2, Field 3] : [Field; 3] g11 = make_array [g10, g10, g10] : [[Field; 3]; 3] - + acir(inline) fn main f0 { b0(): v0 = allocate -> &mut [[Field; 3]; 3] @@ -2383,7 +2383,7 @@ mod tests { g9 = make_array [Field 1, Field 2, Field 3] : [Field; 3] g10 = make_array [Field 1, Field 2, Field 3] : [Field; 3] g11 = make_array [g10, g10, g10] : [[Field; 3]; 3] - + acir(inline) fn main f0 { b0(): v12 = allocate -> &mut [[Field; 3]; 3] 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 8a6c7c44798..67f6ed036d3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -113,7 +113,7 @@ impl Function { context.remove_current_instruction(); - let old_result = *context.dfg.instruction_results(instruction_id).first().unwrap(); + let [old_result] = context.dfg.instruction_result(instruction_id); let mut bitshift_context = Context { context }; bitshift_context.enforce_bitshift_rhs_lt_bit_size(rhs); 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 d61d81f9b54..f4983045492 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -201,8 +201,7 @@ impl Context { else_value, )?; - let results = context.dfg.instruction_results(instruction_id); - let result = results[0]; + let [result] = context.dfg.instruction_result(instruction_id); context.remove_current_instruction(); // The `IfElse` instruction is replaced by the merge done with the `ValueMerger` @@ -231,8 +230,7 @@ impl Context { } // Track slice sizes through array set instructions Instruction::ArraySet { array, .. } => { - let results = context.dfg.instruction_results(instruction_id); - let result = results[0]; + let [result] = context.dfg.instruction_result(instruction_id); self.set_capacity(context.dfg, *array, result, |c| c); } _ => (), diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_truncate_after_range_check.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_truncate_after_range_check.rs index c2bc01ce74c..7b3edec8028 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_truncate_after_range_check.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_truncate_after_range_check.rs @@ -49,9 +49,8 @@ impl Function { // v1 = truncate v0 to 32 bits, max_bit_size: 254 // // we need to remove the `truncate` and all references to `v1` should now be `v0`. - let result = - context.dfg.instruction_results(instruction_id).first().unwrap(); - context.replace_value(*result, *value); + let [result] = context.dfg.instruction_result(instruction_id); + context.replace_value(result, *value); context.remove_current_instruction(); } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_instructions.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_instructions.rs index 08acc96471f..def446ef608 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_instructions.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_unreachable_instructions.rs @@ -523,8 +523,8 @@ fn should_replace_instruction_with_defaults(context: &SimpleOptimizationContext) Type::Array(typ, _) | Type::Slice(typ) => typ, other => unreachable!("Array or Slice type expected; got {other:?}"), }; - let results = context.dfg.instruction_results(context.instruction_id).to_vec(); - let result_type = context.dfg.type_of_value(results[0]); + let [result] = context.dfg.instruction_result(context.instruction_id); + let result_type = context.dfg.type_of_value(result); // If the type doesn't agree then we should not use this any more, // as the type in the array will replace the type we wanted to get, // and cause problems further on. diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index f2c15407d9f..3a7c767df9d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -730,7 +730,7 @@ impl Loop { instructions .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]) + .map(|i| function.dfg.instruction_result::<1>(*i)[0]) }); // Collect reference parameters of the function itself.