Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/acir/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,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, offset: _ } => (array, index, None),
Instruction::ArraySet { array, index, value, mutable } => {
Instruction::ArraySet { array, index, value, mutable, offset: _ } => {
mutable_array_set = mutable;
(array, index, Some(value))
}
Expand Down
82 changes: 58 additions & 24 deletions compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{ArrayGetOffset, ConstrainError, Hint};
use crate::ssa::ir::instruction::{ArrayOffset, ConstrainError, Hint};
use crate::ssa::ir::{
basic_block::BasicBlockId,
dfg::DataFlowGraph,
Expand Down Expand Up @@ -802,30 +802,24 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
);

let array_variable = self.convert_ssa_value(*array, dfg);

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,
destination_variable.extract_register(),
);
let has_offset = if dfg.is_constant(*index) {
// For constant indices it must be the case that they have been offset during SSA
assert!(*offset != ArrayOffset::None);
true
} else {
let items_pointer = self
.brillig_context
.codegen_make_array_or_vector_items_pointer(array_variable);
self.brillig_context.codegen_load_with_offset(
items_pointer,
index_variable,
destination_variable.extract_register(),
);
self.brillig_context.deallocate_register(items_pointer);
}
false
};

self.convert_ssa_array_get(
array_variable,
index_variable,
destination_variable,
has_offset,
);
}
Instruction::ArraySet { array, index, value, mutable } => {
Instruction::ArraySet { array, index, value, mutable, offset } => {
let source_variable = self.convert_ssa_value(*array, dfg);
let index_register = self.convert_ssa_single_addr_value(*index, dfg);
let value_variable = self.convert_ssa_value(*value, dfg);
Expand All @@ -838,12 +832,21 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
dfg,
);

let has_offset = if dfg.is_constant(*index) {
// For constant indices it must be the case that they have been offset during SSA
assert!(*offset != ArrayOffset::None);
true
} else {
false
};

self.convert_ssa_array_set(
source_variable,
destination_variable,
index_register,
value_variable,
*mutable,
has_offset,
);
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
Expand Down Expand Up @@ -1110,6 +1113,30 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
}
}

fn convert_ssa_array_get(
&mut self,
array_variable: BrilligVariable,
index_variable: SingleAddrVariable,
destination_variable: BrilligVariable,
has_offset: bool,
) {
let items_pointer = if has_offset {
array_variable.extract_register()
} else {
self.brillig_context.codegen_make_array_or_vector_items_pointer(array_variable)
};

self.brillig_context.codegen_load_with_offset(
items_pointer,
index_variable,
destination_variable.extract_register(),
);

if !has_offset {
self.brillig_context.deallocate_register(items_pointer);
}
}

/// Array set operation in SSA returns a new array or slice that is a copy of the parameter array or slice
/// With a specific value changed.
///
Expand All @@ -1122,6 +1149,7 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
index_register: SingleAddrVariable,
value_variable: BrilligVariable,
mutable: bool,
has_offset: bool,
) {
assert!(index_register.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE);
match (source_variable, destination_variable) {
Expand All @@ -1146,9 +1174,13 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
}

let destination_for_store = if mutable { source_variable } else { destination_variable };

// Then set the value in the newly created array
let items_pointer =
self.brillig_context.codegen_make_array_or_vector_items_pointer(destination_for_store);
let items_pointer = if has_offset {
destination_for_store.extract_register()
} else {
self.brillig_context.codegen_make_array_or_vector_items_pointer(destination_for_store)
};

self.brillig_context.codegen_store_with_offset(
items_pointer,
Expand All @@ -1164,7 +1196,9 @@ impl<'block, Registers: RegisterAllocator> BrilligBlock<'block, Registers> {
);
}

self.brillig_context.deallocate_register(items_pointer);
if !has_offset {
self.brillig_context.deallocate_register(items_pointer);
}
}

/// Convert the SSA slice operations to brillig slice operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ mod tests {

let ssa = Ssa::from_str(src).unwrap();
// Need to run SSA pass that sets up Brillig array gets
let ssa = ssa.brillig_array_gets();
let ssa = ssa.brillig_array_get_and_set();
// Need to run DIE to generate the used globals map, which is necessary for Brillig globals generation.
let mut ssa = ssa.dead_instruction_elimination();

Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
// We can safely place the pass before DIE as that pass only removes instructions.
// We also need DIE's tracking of used globals in case the array get transformations
// end up using an existing constant from the globals space.
SsaPass::new(Ssa::brillig_array_gets, "Brillig Array Get Optimizations"),
SsaPass::new(Ssa::brillig_array_get_and_set, "Brillig Array Get and Set Optimizations"),
SsaPass::new(Ssa::dead_instruction_elimination, "Dead Instruction Elimination"),
// A function can be potentially unreachable post-DIE if all calls to that function were removed.
SsaPass::new(Ssa::remove_unreachable_functions, "Removing Unreachable Functions"),
Expand Down Expand Up @@ -248,7 +248,7 @@ pub fn minimal_passes() -> Vec<SsaPass<'static>> {
// We need a DIE pass to populate `used_globals`, otherwise it will panic later.
SsaPass::new(Ssa::dead_instruction_elimination, "Dead Instruction Elimination"),
// We need to add an offset to constant array indices in Brillig.
SsaPass::new(Ssa::brillig_array_gets, "Brillig Array Get Optimizations"),
SsaPass::new(Ssa::brillig_array_get_and_set, "Brillig Array Get and Set Optimizations"),
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{collections::BTreeMap, sync::Arc};

use crate::ssa::ir::{
function::RuntimeType,
instruction::ArrayOffset,
types::{NumericType, Type},
value::{ValueId, ValueMapping},
};
Expand Down Expand Up @@ -161,7 +162,9 @@ impl FunctionBuilder {
if let Type::Array(_, 0) = subitem_typ {
continue;
}
let element = self.insert_array_get(value, index_var, subitem_typ.clone());
let offset = ArrayOffset::None;
let element =
self.insert_array_get(value, index_var, offset, subitem_typ.clone());
index += match subitem_typ {
Type::Array(_, _) | Type::Slice(_) => subitem_typ.element_size(),
Type::Numeric(_) => 1,
Expand Down
28 changes: 7 additions & 21 deletions compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use super::{
basic_block::BasicBlock,
dfg::{GlobalsGraph, InsertInstructionResult},
function::RuntimeType,
instruction::{ArrayGetOffset, ConstrainError, InstructionId, Intrinsic},
instruction::{ArrayOffset, ConstrainError, InstructionId, Intrinsic},
types::NumericType,
},
opt::pure::FunctionPurities,
Expand Down Expand Up @@ -351,17 +351,7 @@ impl FunctionBuilder {
&mut self,
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,
offset: ArrayOffset,
element_type: Type,
) -> ValueId {
let element_type = Some(vec![element_type]);
Expand All @@ -370,20 +360,16 @@ impl FunctionBuilder {
}

/// Insert an instruction to create a new array with the given index replaced with a new value
pub fn insert_array_set(&mut self, array: ValueId, index: ValueId, value: ValueId) -> ValueId {
self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: false }, None)
.first()
}

#[cfg(test)]
pub fn insert_mutable_array_set(
pub fn insert_array_set(
&mut self,
array: ValueId,
index: ValueId,
value: ValueId,
mutable: bool,
offset: ArrayOffset,
) -> ValueId {
self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: true }, None)
.first()
let instruction = Instruction::ArraySet { array, index, value, mutable, offset };
self.insert_instruction(instruction, None).first()
}

/// Insert an instruction to increment an array's reference count. This only has an effect
Expand Down
10 changes: 6 additions & 4 deletions compiler/noirc_evaluator/src/ssa/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
ir::{
dfg::DataFlowGraph,
function::{Function, FunctionId, RuntimeType},
instruction::{ArrayGetOffset, Binary, BinaryOp, Instruction, TerminatorInstruction},
instruction::{ArrayOffset, Binary, BinaryOp, Instruction, TerminatorInstruction},
types::Type,
value::ValueId,
},
Expand Down Expand Up @@ -446,8 +446,8 @@
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])
Instruction::ArraySet { array, index, value, mutable, offset } => {
self.interpret_array_set(*array, *index, *value, *mutable, *offset, results[0])
}
Instruction::IncrementRc { value } => self.interpret_inc_rc(*value),
Instruction::DecrementRc { value } => self.interpret_dec_rc(*value),
Expand Down Expand Up @@ -736,7 +736,7 @@
&mut self,
array: ValueId,
index: ValueId,
offset: ArrayGetOffset,
offset: ArrayOffset,
result: ValueId,
) -> IResult<()> {
let element = if self.side_effects_enabled() {
Expand All @@ -758,12 +758,14 @@
index: ValueId,
value: ValueId,
mutable: bool,
offset: ArrayOffset,
result: ValueId,
) -> IResult<()> {
let array = self.lookup_array_or_slice(array, "array set")?;

let result_array = if self.side_effects_enabled() {
let index = self.lookup_u32(index, "array set index")?;
let index = index - offset.to_u32();
let value = self.lookup(value)?;

let should_mutate =
Expand Down Expand Up @@ -875,7 +877,7 @@
}
}

macro_rules! apply_int_binop {

Check warning on line 880 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (binop)
($lhs:expr, $rhs:expr, $binary:expr, $f:expr) => {{
use value::NumericValue::*;
match ($lhs, $rhs) {
Expand Down Expand Up @@ -906,7 +908,7 @@
}};
}

macro_rules! apply_int_binop_opt {

Check warning on line 911 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (binop)
($dfg:expr, $lhs:expr, $rhs:expr, $binary:expr, $f:expr) => {{
use value::NumericValue::*;

Expand Down Expand Up @@ -1006,7 +1008,7 @@
}));
}

// Disable this instruction if it is side-effectful and side effects are disabled.

Check warning on line 1011 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (effectful)
if !self.side_effects_enabled() && binary.requires_acir_gen_predicate(self.dfg()) {
let zero = NumericValue::zero(lhs.get_type());
return Ok(Value::Numeric(zero));
Expand All @@ -1023,13 +1025,13 @@
let dfg = self.dfg();
let result = match binary.operator {
BinaryOp::Add { unchecked: false } => {
apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedAdd::checked_add)

Check warning on line 1028 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (binop)
}
BinaryOp::Add { unchecked: true } => {
apply_int_binop!(lhs, rhs, binary, num_traits::WrappingAdd::wrapping_add)

Check warning on line 1031 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (binop)
}
BinaryOp::Sub { unchecked: false } => {
apply_int_binop_opt!(dfg, lhs, rhs, binary, num_traits::CheckedSub::checked_sub)

Check warning on line 1034 in compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (binop)
}
BinaryOp::Sub { unchecked: true } => {
apply_int_binop!(lhs, rhs, binary, num_traits::WrappingSub::wrapping_sub)
Expand Down
30 changes: 30 additions & 0 deletions compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,36 @@ fn array_set_disabled_by_enable_side_effects() {
assert_eq!(*v2.elements.borrow(), expected);
}

#[test]
fn array_set_with_offset() {
let values = expect_values(
"
acir(inline) fn main f0 {
b0():
v0 = make_array [Field 1, Field 2] : [Field; 2]
v1 = array_set v0, index u32 4 minus 3, value Field 5
return v0, v1
}
",
);

let v0 = values[0].as_array_or_slice().unwrap();
let v1 = values[1].as_array_or_slice().unwrap();

// acir function, so all rcs are 1
assert_eq!(*v0.rc.borrow(), 1);
assert_eq!(*v1.rc.borrow(), 1);

let one = from_constant(1u32.into(), NumericType::NativeField);
let two = from_constant(2u32.into(), NumericType::NativeField);
let five = from_constant(5u32.into(), NumericType::NativeField);

// v0 was not mutated
assert_eq!(*v0.elements.borrow(), vec![one.clone(), two.clone()]);
// v1 was mutated
assert_eq!(*v1.elements.borrow(), vec![one, five]);
}

#[test]
fn increment_rc() {
let value = expect_value(
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_evaluator/src/ssa/ir/dfg/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::ssa::{
ir::{
basic_block::BasicBlockId,
instruction::{
ArrayGetOffset, Binary, BinaryOp, Instruction,
ArrayOffset, Binary, BinaryOp, Instruction,
binary::{truncate, truncate_field},
},
types::Type,
Expand Down Expand Up @@ -376,7 +376,7 @@ fn try_optimize_array_set_from_previous_get(
) -> SimplifyResult {
let array_from_get = match dfg.get_local_or_global_instruction(target_value) {
Some(Instruction::ArrayGet { array, index, offset }) => {
if *offset == ArrayGetOffset::None && *array == array_id && *index == target_index {
if *offset == ArrayOffset::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 {
Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::ssa::{
ir::{
basic_block::BasicBlockId,
dfg::DataFlowGraph,
instruction::{ArrayGetOffset, Binary, BinaryOp, Endian, Hint, Instruction, Intrinsic},
instruction::{ArrayOffset, Binary, BinaryOp, Endian, Hint, Instruction, Intrinsic},
types::{NumericType, Type},
value::{Value, ValueId},
},
Expand Down Expand Up @@ -506,6 +506,7 @@ fn simplify_slice_push_back(
index: arguments[0],
value: arguments[2],
mutable: false,
offset: ArrayOffset::None,
};

let set_last_slice_value = dfg
Expand Down Expand Up @@ -557,7 +558,7 @@ fn simplify_slice_pop_back(
let get_last_elem_instr = Instruction::ArrayGet {
array: arguments[1],
index: flattened_len,
offset: ArrayGetOffset::None,
offset: ArrayOffset::None,
};

let element_type = Some(vec![element_type.clone()]);
Expand Down
Loading
Loading