diff --git a/compiler/noirc_evaluator/src/acir/arrays.rs b/compiler/noirc_evaluator/src/acir/arrays.rs index e99d46ed2e7..e4d7600f9a4 100644 --- a/compiler/noirc_evaluator/src/acir/arrays.rs +++ b/compiler/noirc_evaluator/src/acir/arrays.rs @@ -83,7 +83,7 @@ impl Context<'_> { // 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, offset: _ } => (array, index, None), Instruction::ArraySet { array, index, value, mutable } => { mutable_array_set = mutable; (array, index, Some(value)) 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 997d34441ac..ed5076f8b49 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -7,7 +7,7 @@ use crate::brillig::brillig_ir::registers::RegisterAllocator; use crate::brillig::brillig_ir::{ BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, BrilligBinaryOp, BrilligContext, ReservedRegisters, }; -use crate::ssa::ir::instruction::{ConstrainError, Hint}; +use crate::ssa::ir::instruction::{ArrayGetOffset, ConstrainError, Hint}; use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, @@ -792,7 +792,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { 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, offset } => { let result_ids = dfg.instruction_results(instruction_id); let destination_variable = self.variables.define_variable( self.function_context, @@ -806,6 +806,8 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> { let index_variable = self.convert_ssa_single_addr_value(*index, dfg); if dfg.is_constant(*index) { + // For constant indices it must be the case that they have been offseted during SSA + assert!(*offset != ArrayGetOffset::None); self.brillig_context.codegen_load_with_offset( array_variable.extract_register(), index_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 20aa96e5a70..4b16303e9d0 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 @@ -522,7 +522,7 @@ impl DependencyContext { // For array get operations, we check the Brillig calls for // results involving the array in question, to properly // populate the array element tainted sets - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { self.process_array_get(*array, *index, &results, function); // Record all the used arguments as parents of the results self.update_children(&arguments, &results); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 7ef7dfbe83a..5b4bff8e558 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -23,7 +23,7 @@ use super::{ basic_block::BasicBlock, dfg::{GlobalsGraph, InsertInstructionResult}, function::RuntimeType, - instruction::{ConstrainError, InstructionId, Intrinsic}, + instruction::{ArrayGetOffset, ConstrainError, InstructionId, Intrinsic}, types::NumericType, }, opt::pure::FunctionPurities, @@ -352,9 +352,21 @@ impl FunctionBuilder { array: ValueId, index: ValueId, element_type: Type, + ) -> ValueId { + self.insert_array_get_with_offset(array, index, ArrayGetOffset::None, element_type) + } + + /// Insert an instruction to extract an element from an array + pub fn insert_array_get_with_offset( + &mut self, + array: ValueId, + index: ValueId, + offset: ArrayGetOffset, + element_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, offset }, element_type) + .first() } /// Insert an instruction to create a new array with the given index replaced with a new value diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs index a5929035a78..e8b94fbe169 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/mod.rs @@ -5,7 +5,7 @@ use super::{ ir::{ dfg::DataFlowGraph, function::{Function, FunctionId, RuntimeType}, - instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, + instruction::{ArrayGetOffset, Binary, BinaryOp, Instruction, TerminatorInstruction}, types::Type, value::ValueId, }, @@ -443,8 +443,8 @@ impl<'ssa> Interpreter<'ssa> { self.side_effects_enabled = self.lookup_bool(*condition, "enable_side_effects")?; Ok(()) } - Instruction::ArrayGet { array, index } => { - self.interpret_array_get(*array, *index, results[0]) + Instruction::ArrayGet { array, index, offset } => { + self.interpret_array_get(*array, *index, *offset, results[0]) } Instruction::ArraySet { array, index, value, mutable } => { self.interpret_array_set(*array, *index, *value, *mutable, results[0]) @@ -736,11 +736,13 @@ impl<'ssa> Interpreter<'ssa> { &mut self, array: ValueId, index: ValueId, + offset: ArrayGetOffset, result: ValueId, ) -> IResult<()> { let element = if self.side_effects_enabled() { let array = self.lookup_array_or_slice(array, "array get")?; let index = self.lookup_u32(index, "array get index")?; + let index = index - offset.to_u32(); array.elements.borrow()[index as usize].clone() } else { let typ = self.dfg().type_of_value(result); diff --git a/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs b/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs index 3d8557fcd8e..df2e53f0e25 100644 --- a/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs +++ b/compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs @@ -623,6 +623,21 @@ fn array_get() { assert_eq!(value, from_constant(2_u32.into(), NumericType::NativeField)); } +#[test] +fn array_get_with_offset() { + let value = expect_value( + r#" + acir(inline) fn main f0 { + b0(): + v0 = make_array [Field 1, Field 2] : [Field; 2] + v1 = array_get v0, index u32 4 minus 3 -> Field + return v1 + } + "#, + ); + assert_eq!(value, from_constant(2_u32.into(), NumericType::NativeField)); +} + #[test] fn array_get_disabled_by_enable_side_effects() { let value = expect_value( diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify.rs index d8aed40f702..79993551656 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify.rs @@ -2,7 +2,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, instruction::{ - Binary, BinaryOp, Instruction, + ArrayGetOffset, Binary, BinaryOp, Instruction, binary::{truncate, truncate_field}, }, types::Type, @@ -110,7 +110,7 @@ pub(crate) fn simplify( } } Instruction::ConstrainNotEqual(..) => None, - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { if let Some(index) = dfg.get_numeric_constant(*index) { try_optimize_array_get_from_previous_set(dfg, *array, index) } else { @@ -375,8 +375,8 @@ fn try_optimize_array_set_from_previous_get( target_value: ValueId, ) -> SimplifyResult { let array_from_get = match dfg.get_local_or_global_instruction(target_value) { - Some(Instruction::ArrayGet { array, index }) => { - if *array == array_id && *index == target_index { + Some(Instruction::ArrayGet { array, index, offset }) => { + if *offset == ArrayGetOffset::None && *array == array_id && *index == target_index { // If array and index match from the value, we can immediately simplify return SimplifyResult::SimplifiedTo(array_id); } else if *index == target_index { diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs index 27b7a04422e..496b596b9d9 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs @@ -11,7 +11,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - instruction::{Binary, BinaryOp, Endian, Hint, Instruction, Intrinsic}, + instruction::{ArrayGetOffset, Binary, BinaryOp, Endian, Hint, Instruction, Intrinsic}, types::{NumericType, Type}, value::{Value, ValueId}, }, @@ -554,8 +554,11 @@ fn simplify_slice_pop_back( // We must pop multiple elements in the case of a slice of tuples // Iterating through element types in reverse here since we're popping from the end for element_type in element_types.iter().rev() { - let get_last_elem_instr = - Instruction::ArrayGet { array: arguments[1], index: flattened_len }; + let get_last_elem_instr = Instruction::ArrayGet { + array: arguments[1], + index: flattened_len, + offset: ArrayGetOffset::None, + }; let element_type = Some(vec![element_type.clone()]); let get_last_elem = dfg diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index eb2d04deae5..927b61e9f7a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -280,7 +280,8 @@ pub enum Instruction { EnableSideEffectsIf { condition: ValueId }, /// Retrieve a value from an array at the given index - ArrayGet { array: ValueId, index: ValueId }, + /// `offset` determines whether the index has been offseted by some offset. + ArrayGet { array: ValueId, index: ValueId, offset: ArrayGetOffset }, /// 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 @@ -376,7 +377,7 @@ impl Instruction { match self { Instruction::Binary(binary) => binary.requires_acir_gen_predicate(dfg), - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { // `ArrayGet`s which read from "known good" indices from an array should not need a predicate. !dfg.is_safe_index(*index, *array) } @@ -477,8 +478,8 @@ impl Instruction { Instruction::EnableSideEffectsIf { condition } => { Instruction::EnableSideEffectsIf { condition: f(*condition) } } - Instruction::ArrayGet { array, index } => { - Instruction::ArrayGet { array: f(*array), index: f(*index) } + Instruction::ArrayGet { array, index, offset } => { + Instruction::ArrayGet { array: f(*array), index: f(*index), offset: *offset } } Instruction::ArraySet { array, index, value, mutable } => Instruction::ArraySet { array: f(*array), @@ -548,7 +549,7 @@ impl Instruction { Instruction::EnableSideEffectsIf { condition } => { *condition = f(*condition); } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { *array = f(*array); *index = f(*index); } @@ -614,7 +615,7 @@ impl Instruction { f(*value); } Instruction::Allocate => (), - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { f(*array); f(*index); } @@ -647,6 +648,37 @@ impl Instruction { } } +/// Determines whether an ArrayGet index has been offseted by a given value. +/// Offsets are set during `crate::ssa::opt::brillig_array_gets` for brillig arrays +/// and vectors with constant indicces. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] +pub enum ArrayGetOffset { + None, + Array, + Slice, +} + +impl ArrayGetOffset { + pub fn from_u32(value: u32) -> Option { + match value { + 0 => Some(Self::None), + 1 => Some(Self::Array), + 3 => Some(Self::Slice), + _ => None, + } + } + + pub fn to_u32(self) -> u32 { + match self { + Self::None => 0, + // Arrays in brillig are represented as [RC, ...items] + Self::Array => 1, + // Slices in brillig are represented as [RC, Size, Capacity, ...items] + Self::Slice => 3, + } + } +} + impl Binary { pub(crate) fn requires_acir_gen_predicate(&self, dfg: &DataFlowGraph) -> bool { match self.operator { diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 9d9d6579783..8c4030bbb89 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -7,7 +7,10 @@ use iter_extended::vecmap; use crate::ssa::{ Ssa, - ir::types::{NumericType, Type}, + ir::{ + instruction::ArrayGetOffset, + types::{NumericType, Type}, + }, }; use super::{ @@ -230,12 +233,17 @@ fn display_instruction_inner( Instruction::EnableSideEffectsIf { condition } => { writeln!(f, "enable_side_effects {}", show(*condition)) } - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset } => { writeln!( f, - "array_get {}, index {}{}", + "array_get {}, index {}{}{}", show(*array), show(*index), + match offset { + ArrayGetOffset::None => String::new(), + ArrayGetOffset::Array | ArrayGetOffset::Slice => + format!(" minus {}", offset.to_u32()), + }, result_types(dfg, results) ) } diff --git a/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs b/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs index 43d6c023399..d09d3dc40c7 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/basic_conditional.rs @@ -200,7 +200,7 @@ fn block_cost(block: BasicBlockId, dfg: &DataFlowGraph) -> u32 { | Instruction::Store { .. } | Instruction::ArraySet { .. } => return u32::MAX, - Instruction::ArrayGet { array, index } => { + Instruction::ArrayGet { array, index, offset: _ } => { // A get can fail because of out-of-bound index let mut in_bound = false; // check if index is in bound diff --git a/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs b/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs index c3d6f3ed0c3..aae195f7661 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/brillig_array_gets.rs @@ -16,7 +16,7 @@ use crate::{ Ssa, ir::{ function::Function, - instruction::Instruction, + instruction::{ArrayGetOffset, Instruction}, types::{NumericType, Type}, }, }, @@ -39,10 +39,13 @@ impl Function { pub(super) fn brillig_array_gets(&mut self) { self.simple_reachable_blocks_optimization(|context| { let instruction = context.instruction(); - let Instruction::ArrayGet { array, index } = instruction else { + let Instruction::ArrayGet { array, index, offset } = instruction else { return; }; + // This pass should run at most once + assert!(*offset == ArrayGetOffset::None); + let array = *array; let index = *index; if !context.dfg.is_constant(index) { @@ -52,17 +55,15 @@ impl Function { let index_constant = context.dfg.get_numeric_constant(index).expect("ICE: Expected constant index"); let offset = if matches!(context.dfg.type_of_value(array), Type::Array(..)) { - // Brillig arrays are [RC, ...items] - 1u128 + ArrayGetOffset::Array } else { - // Brillig vectors are [RC, Size, Capacity, ...items] - 3u128 + ArrayGetOffset::Slice }; let index = context.dfg.make_constant( - index_constant + offset.into(), + index_constant + offset.to_u32().into(), NumericType::unsigned(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), ); - let new_instruction = Instruction::ArrayGet { array, index }; + let new_instruction = Instruction::ArrayGet { array, index, offset }; context.replace_current_instruction_with(new_instruction); }); } @@ -90,7 +91,7 @@ mod tests { assert_ssa_snapshot!(ssa, @r" brillig(inline) fn main f0 { b0(v0: [Field; 3]): - v2 = array_get v0, index u32 1 -> Field + v2 = array_get v0, index u32 1 minus 1 -> Field return v2 } "); @@ -142,7 +143,7 @@ mod tests { assert_ssa_snapshot!(ssa, @r" brillig(inline) fn main f0 { b0(v0: [Field]): - v2 = array_get v0, index u32 3 -> Field + v2 = array_get v0, index u32 3 minus 3 -> Field return v2 } "); diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 99a3ae54eab..894665500e0 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -37,7 +37,7 @@ use crate::{ dfg::{DataFlowGraph, InsertInstructionResult}, dom::DominatorTree, function::{Function, FunctionId, RuntimeType}, - instruction::{BinaryOp, Instruction, InstructionId}, + instruction::{ArrayGetOffset, BinaryOp, Instruction, InstructionId}, types::{NumericType, Type}, value::{Value, ValueId, ValueMapping}, }, @@ -478,7 +478,9 @@ impl<'brillig> Context<'brillig> { if let Instruction::ArraySet { index, value, .. } = &instruction { let predicate = self.use_constraint_info.then_some(side_effects_enabled_var); - let array_get = Instruction::ArrayGet { array: instruction_results[0], index: *index }; + let offset = ArrayGetOffset::None; + let array_get = + Instruction::ArrayGet { array: instruction_results[0], index: *index, offset }; self.cached_instruction_results .entry(array_get) @@ -884,7 +886,7 @@ fn has_side_effects(instruction: &Instruction, dfg: &DataFlowGraph) -> bool { Cast(_, _) | Not(_) | Truncate { .. } | IfElse { .. } => false, // `ArrayGet`s which read from "known good" indices from an array have no side effects - ArrayGet { array, index } => !dfg.is_safe_index(*index, *array), + ArrayGet { array, index, offset: _ } => !dfg.is_safe_index(*index, *array), // ArraySet has side effects ArraySet { .. } => true, 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 1e5480eda2d..bacbb5f3a43 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 noirc_errors::call_stack::CallStackId; use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - instruction::{BinaryOp, Instruction}, + instruction::{ArrayGetOffset, BinaryOp, Instruction}, types::{NumericType, Type}, value::ValueId, }; @@ -147,7 +147,8 @@ impl<'a> ValueMerger<'a> { let typevars = Some(vec![element_type.clone()]); let mut get_element = |array, typevars| { - let get = Instruction::ArrayGet { array, index }; + let offset = ArrayGetOffset::None; + let get = Instruction::ArrayGet { array, index, offset }; self.dfg .insert_instruction_and_results(get, self.block, typevars, self.call_stack) .first() @@ -216,7 +217,8 @@ impl<'a> ValueMerger<'a> { if len <= index_u32 { self.make_slice_dummy_data(element_type) } else { - let get = Instruction::ArrayGet { array, index }; + let offset = ArrayGetOffset::None; + let get = Instruction::ArrayGet { array, index, offset }; self.dfg .insert_instruction_and_results( get, diff --git a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs index b9e194f3b55..b050826208b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/loop_invariant.rs @@ -392,7 +392,7 @@ impl<'f> LoopInvariantContext<'f> { use Instruction::*; match instruction { - ArrayGet { array, index } => { + ArrayGet { array, index, offset: _ } => { let array_typ = self.inserter.function.dfg.type_of_value(*array); let upper_bound = self.outer_induction_variables.get(index).map(|bounds| bounds.1); if let (Type::Array(_, len), Some(upper_bound)) = (array_typ, upper_bound) { 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 7ebdb08ee40..30719e90f73 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -7,7 +7,7 @@ use crate::ssa::{ ir::{ dfg::InsertInstructionResult, function::Function, - instruction::{Binary, BinaryOp, Endian, Instruction, Intrinsic}, + instruction::{ArrayGetOffset, Binary, BinaryOp, Endian, Instruction, Intrinsic}, types::{NumericType, Type}, value::ValueId, }, @@ -330,7 +330,9 @@ impl Context<'_, '_, '_> { element_type: Type, ) -> ValueId { let element_type = Some(vec![element_type]); - self.insert_instruction(Instruction::ArrayGet { array, index }, element_type).first() + let offset = ArrayGetOffset::None; + let instruction = Instruction::ArrayGet { array, index, offset }; + self.insert_instruction(instruction, element_type).first() } pub(crate) fn insert_instruction( diff --git a/compiler/noirc_evaluator/src/ssa/parser/ast.rs b/compiler/noirc_evaluator/src/ssa/parser/ast.rs index 19eff00b24f..348fca8448d 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/ast.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/ast.rs @@ -4,7 +4,11 @@ use acvm::FieldElement; use noirc_errors::Span; use crate::ssa::{ - ir::{function::RuntimeType, instruction::BinaryOp, types::Type}, + ir::{ + function::RuntimeType, + instruction::{ArrayGetOffset, BinaryOp}, + types::Type, + }, opt::pure::Purity, }; @@ -84,6 +88,7 @@ pub(crate) enum ParsedInstruction { element_type: Type, array: ParsedValue, index: ParsedValue, + offset: ArrayGetOffset, }, ArraySet { target: Identifier, diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index e5fd0c5de46..58b7160fc02 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -208,10 +208,11 @@ impl Translator { let value_id = self.builder.insert_allocate(typ); self.define_variable(target, value_id)?; } - ParsedInstruction::ArrayGet { target, element_type, array, index } => { + ParsedInstruction::ArrayGet { target, element_type, array, index, offset } => { let array = self.translate_value(array)?; let index = self.translate_value(index)?; - let value_id = self.builder.insert_array_get(array, index, element_type); + let value_id = + self.builder.insert_array_get_with_offset(array, index, offset, element_type); self.define_variable(target, value_id)?; } ParsedInstruction::ArraySet { target, array, index, value, mutable } => { diff --git a/compiler/noirc_evaluator/src/ssa/parser/mod.rs b/compiler/noirc_evaluator/src/ssa/parser/mod.rs index bad08e74552..ba308a67359 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/mod.rs @@ -7,7 +7,7 @@ use std::{ use super::{ Ssa, ir::{ - instruction::BinaryOp, + instruction::{ArrayGetOffset, BinaryOp}, types::{NumericType, Type}, }, opt::pure::Purity, @@ -507,9 +507,25 @@ impl<'a> Parser<'a> { self.eat_or_error(Token::Comma)?; self.eat_or_error(Token::Keyword(Keyword::Index))?; let index = self.parse_value_or_error()?; + let offset = if self.eat_keyword(Keyword::Minus)? { + let token = self.token.token().clone(); + let span = self.token.span(); + let field = self.eat_int_or_error()?; + if let Some(offset) = field.try_to_u32().and_then(ArrayGetOffset::from_u32) { + if offset == ArrayGetOffset::None { + return self.unexpected_offset(token, span); + } else { + offset + } + } else { + return self.unexpected_offset(token, span); + } + } else { + ArrayGetOffset::None + }; self.eat_or_error(Token::Arrow)?; let element_type = self.parse_type()?; - return Ok(ParsedInstruction::ArrayGet { target, element_type, array, index }); + return Ok(ParsedInstruction::ArrayGet { target, element_type, array, index, offset }); } if self.eat_keyword(Keyword::ArraySet)? { @@ -1033,6 +1049,10 @@ impl<'a> Parser<'a> { Err(ParserError::ExpectedInt { found: self.token.token().clone(), span: self.token.span() }) } + fn unexpected_offset(&mut self, found: Token, span: Span) -> ParseResult { + Err(ParserError::UnexpectedOffset { found, span }) + } + fn expected_type(&mut self) -> ParseResult { Err(ParserError::ExpectedType { found: self.token.token().clone(), @@ -1099,6 +1119,8 @@ pub(crate) enum ParserError { ExpectedGlobalValue { found: Token, span: Span }, #[error("Multiple return values only allowed for call")] MultipleReturnValuesOnlyAllowedForCall { second_target: Identifier }, + #[error("Unexpected integer value for array_get offset")] + UnexpectedOffset { found: Token, span: Span }, } impl ParserError { @@ -1114,7 +1136,8 @@ impl ParserError { | ParserError::ExpectedStringOrData { span, .. } | ParserError::ExpectedByteString { span, .. } | ParserError::ExpectedValue { span, .. } - | ParserError::ExpectedGlobalValue { span, .. } => *span, + | ParserError::ExpectedGlobalValue { span, .. } + | ParserError::UnexpectedOffset { span, .. } => *span, ParserError::MultipleReturnValuesOnlyAllowedForCall { second_target, .. } => { second_target.span } diff --git a/compiler/noirc_evaluator/src/ssa/parser/tests.rs b/compiler/noirc_evaluator/src/ssa/parser/tests.rs index 32642c04166..c70664bba91 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/tests.rs @@ -341,6 +341,30 @@ fn test_array_get() { assert_ssa_roundtrip(src); } +#[test] +fn test_array_get_offseted_by_1() { + let src: &'static str = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index Field 3 minus 1 -> Field + return + } + "; + assert_ssa_roundtrip(src); +} + +#[test] +fn test_array_get_offseted_by_3() { + let src: &'static str = " + acir(inline) fn main f0 { + b0(v0: [Field; 3]): + v2 = array_get v0, index Field 6 minus 3 -> Field + return + } + "; + assert_ssa_roundtrip(src); +} + #[test] fn test_array_set() { let src = " diff --git a/compiler/noirc_evaluator/src/ssa/parser/token.rs b/compiler/noirc_evaluator/src/ssa/parser/token.rs index 54fcd888d53..dff169c0c5a 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/token.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/token.rs @@ -149,6 +149,7 @@ pub(crate) enum Keyword { Lt, MakeArray, MaxBitSize, + Minus, Mod, Mul, Mut, @@ -214,6 +215,7 @@ impl Keyword { "lt" => Keyword::Lt, "make_array" => Keyword::MakeArray, "max_bit_size" => Keyword::MaxBitSize, + "minus" => Keyword::Minus, "mod" => Keyword::Mod, "mul" => Keyword::Mul, "mut" => Keyword::Mut, @@ -283,6 +285,7 @@ impl Display for Keyword { Keyword::Lt => write!(f, "lt"), Keyword::MakeArray => write!(f, "make_array"), Keyword::MaxBitSize => write!(f, "max_bit_size"), + Keyword::Minus => write!(f, "minus"), Keyword::Mod => write!(f, "mod"), Keyword::Mul => write!(f, "mul"), Keyword::Mut => write!(f, "mut"),