Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ssa): Merge slices in if statements with witness conditions #2347

Merged
merged 73 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
504aad0
cleanup load instruction search w/ predecessors methods
vezenovm Aug 9, 2023
d129a0a
update regression_2218 test
vezenovm Aug 9, 2023
2106b6c
merge conflicts w/ master
vezenovm Aug 9, 2023
51abb14
initial merge slices work, weird bug right now with intrinsics, not g…
vezenovm Aug 10, 2023
17d66aa
cargo clippy
vezenovm Aug 10, 2023
06d7773
working slice merger, but broken index_lvalue and append
vezenovm Aug 10, 2023
a92d2c9
merge slices working w/ updated slice representation, have to fix som…
vezenovm Aug 10, 2023
e32e665
clean up recursive load fetch method
vezenovm Aug 10, 2023
ada6129
add test to mem2reg pass
vezenovm Aug 10, 2023
bedfa2a
cleaner search methods
vezenovm Aug 10, 2023
3d422e1
switch from recursive to while-loop based search for loads
vezenovm Aug 10, 2023
c12c5bb
add comments to mem2reg pass
vezenovm Aug 10, 2023
9988d91
fmt
vezenovm Aug 10, 2023
7cc6afe
remove dbg
vezenovm Aug 10, 2023
b4ee4fc
merge conflcits w/ mv/func-mem2reg
vezenovm Aug 10, 2023
24b2fe6
fixed ec_baby_jubjub but need fix to mem2reg pass to make it efficient
vezenovm Aug 10, 2023
fc7327a
move dom tree per func
vezenovm Aug 10, 2023
0bb6401
cargo fmt
vezenovm Aug 10, 2023
5baf9e8
make dom tree part of the func context
vezenovm Aug 10, 2023
990d593
merge w/ mv/func-mem2reg
vezenovm Aug 10, 2023
06256c6
remove old ocmment
vezenovm Aug 10, 2023
ae75b7a
Merge branch 'mv/func-mem2reg' into mv/merge-slices-2
vezenovm Aug 10, 2023
1f8d4bd
fixed mem2reg before flattening
vezenovm Aug 11, 2023
c8f9317
remove loops from mem2reg function context
vezenovm Aug 11, 2023
a824ed3
remove import
vezenovm Aug 11, 2023
e345f8c
Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs
jfecher Aug 11, 2023
fb53610
Update crates/noirc_evaluator/src/ssa/opt/mem2reg.rs
jfecher Aug 11, 2023
e07c999
m em2reg debug work
vezenovm Aug 12, 2023
265b883
working func mem2reg for various reference alias cases
vezenovm Aug 15, 2023
3906ca2
array dynamic back to original
vezenovm Aug 15, 2023
7ed502d
use reachable blocks
vezenovm Aug 15, 2023
bac1e30
merge conflicts w/ master
vezenovm Aug 15, 2023
d697231
merge conflicts from master
vezenovm Aug 15, 2023
71c5693
restore sha2_blocks test
vezenovm Aug 15, 2023
c612b45
remove unnecessary clone and unused methods
vezenovm Aug 15, 2023
4e47914
merge conflcits w/ mem2reg branch and quick change
vezenovm Aug 15, 2023
ef10232
bring back loops check inside fetch methods
vezenovm Aug 15, 2023
0a1925c
merge conflicts
vezenovm Aug 15, 2023
8d535eb
Update crates/nargo_cli/tests/execution_success/regression/src/main.nr
vezenovm Aug 15, 2023
bd678a9
all to_bits methods working for acir, fixing up brillig with new slic…
vezenovm Aug 15, 2023
f989fe9
guarantee that x == 3 in references regressio nfor 2218
vezenovm Aug 15, 2023
12ee7ad
remove leftover comments
vezenovm Aug 15, 2023
415e516
Merge branch 'mv/func-mem2reg' into mv/merge-slices-2
vezenovm Aug 15, 2023
6149e48
all brillig sslice intrinsics working except insert and remove
vezenovm Aug 15, 2023
2a1da3e
working brillig_nested_slices
vezenovm Aug 16, 2023
2a95fbf
missed save
vezenovm Aug 16, 2023
c05e92c
working brillig oracle with new slices
vezenovm Aug 16, 2023
3f37004
cleanup comments and dbgs
vezenovm Aug 16, 2023
97a4b2a
merge conflicts w/ master
vezenovm Aug 16, 2023
c89bb76
fixes to simplify_call
vezenovm Aug 16, 2023
48c8514
cleanup updating slice length in brillig block
vezenovm Aug 16, 2023
ff631e2
cleanup in simplify_call and cleanup
vezenovm Aug 16, 2023
c55befc
more integration tests for merge_slices
vezenovm Aug 16, 2023
394b5c5
small comments
vezenovm Aug 16, 2023
14b676d
remove dbg
vezenovm Aug 16, 2023
7999996
remove unnecessary noir test
vezenovm Aug 16, 2023
ffda9cc
remove check on failed_to_unroll
vezenovm Aug 16, 2023
9e4a065
cleanup index_lvalue
vezenovm Aug 16, 2023
2b82aca
cleanup codegen_literal
vezenovm Aug 16, 2023
89e553e
remove radix padding
vezenovm Aug 17, 2023
de2d962
merge conflicts w/ master
vezenovm Aug 17, 2023
7ba3be5
some pr comments and formatting
vezenovm Aug 18, 2023
caebc6b
Update crates/nargo_cli/tests/execution_success/brillig_to_le_bytes/s…
vezenovm Aug 18, 2023
a2a0898
Update crates/noirc_evaluator/src/ssa/ir/instruction/call.rs
vezenovm Aug 18, 2023
5327345
Update crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs
vezenovm Aug 18, 2023
cca2e34
Update crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
vezenovm Aug 18, 2023
892b435
Update crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
vezenovm Aug 18, 2023
eaa6e9c
Update crates/noirc_evaluator/src/ssa/ssa_gen/context.rs
vezenovm Aug 18, 2023
bfea324
comments
vezenovm Aug 18, 2023
f8c9c70
cleanup and comments from pr review
vezenovm Aug 18, 2023
b3a54c6
more specific comment for LValue::SliceIndex assign
vezenovm Aug 18, 2023
838c598
improve error in codegen_literal
vezenovm Aug 18, 2023
99b1c20
Update crates/noirc_evaluator/src/ssa/ssa_gen/context.rs
vezenovm Aug 18, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ fn main(x : Field, y : pub Field) {
} else {
slice = slice.push_back(x);
}
// This constraint should fail as the slice length is 2 and the index is 2
assert(slice[2] == 0);
// This constraint should fail as the slice length is 3 and the index is 3
// The right hand side AND case ensures that the circuit inputs have not changed
// and we always hit the else case in the if statement above.
assert((slice[3] == 0) & (slice[2] != y));
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ unconstrained fn main(x : Field) -> pub [u8; 31] {
// The result of this byte array will be little-endian
let byte_array = x.to_le_bytes(31);
assert(byte_array.len() == 31);

let mut bytes = [0; 31];
for i in 0..31 {
bytes[i] = byte_array[i];
Expand Down
54 changes: 33 additions & 21 deletions crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,14 @@
dfg,
);
let param_id = arguments[0];
// Slices are represented as a tuple in the form: (length, slice contents).
// Thus, we can expect the first argument to a field in the case of a slice
// or an array in the case of an array.
if let Type::Numeric(_) = dfg.type_of_value(param_id) {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
self.convert_ssa_value(arguments[0], dfg);
let len_variable = self.convert_ssa_value(arguments[0], dfg);
let len_register_index =
self.function_context.extract_register(len_variable);
self.brillig_context.mov_instruction(result_register, len_register_index);
} else {
self.convert_ssa_array_len(arguments[0], result_register, dfg);
}
Expand All @@ -346,14 +352,12 @@

let results = dfg.instruction_results(instruction_id);

let target_len = match self.function_context.get_or_create_variable(
let target_len_variable = self.function_context.get_or_create_variable(
self.brillig_context,
results[0],
dfg,
) {
RegisterOrMemory::RegisterIndex(register_index) => register_index,
_ => unreachable!("ICE: first value of a slice must be a register index"),
};
);
let target_len = self.function_context.extract_register(target_len_variable);

let target_slice = self.function_context.create_variable(
self.brillig_context,
Expand All @@ -362,9 +366,12 @@
);

let heap_vec = self.brillig_context.extract_heap_vector(target_slice);

// Update the user-facing slice length
self.brillig_context.mov_instruction(target_len, limb_count);

self.brillig_context.radix_instruction(
source,
target_len,
heap_vec,
radix,
limb_count,
Expand All @@ -377,14 +384,12 @@

let results = dfg.instruction_results(instruction_id);

let target_len = match self.function_context.get_or_create_variable(
let target_len_variable = self.function_context.get_or_create_variable(
self.brillig_context,
results[0],
dfg,
) {
RegisterOrMemory::RegisterIndex(register_index) => register_index,
_ => unreachable!("ICE: first value of a slice must be a register index"),
};
);
let target_len = self.function_context.extract_register(target_len_variable);

let target_slice = self.function_context.create_variable(
self.brillig_context,
Expand All @@ -394,9 +399,12 @@

let radix = self.brillig_context.make_constant(2_usize.into());
let heap_vec = self.brillig_context.extract_heap_vector(target_slice);

// Update the user-facing slice length
self.brillig_context.mov_instruction(target_len, limb_count);

self.brillig_context.radix_instruction(
source,
target_len,
heap_vec,
radix,
limb_count,
Expand Down Expand Up @@ -815,21 +823,25 @@
}
}

/// Slices have a tuple structure (slice length, slice contents) to enable logic
/// that uses dynamic slice lengths (such as with merging slices in the flattening pass).
/// This method codegens an update to the slice length.

Check warning on line 828 in crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (codegens)
///
/// The binary operation performed on the slice length is always an addition or subtraction of `1`.
/// This is because the slice length holds the user length (length as displayed by a `.len()` call),
/// and not a flattened length used internally to represent arrays of tuples.
/// The length inside of `RegisterOrMemory::HeapVector` represents the entire flattened number
/// of fields in the vector.
fn update_slice_length(
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
target_len: RegisterIndex,
source_value: ValueId,
dfg: &DataFlowGraph,
binary_op: BinaryIntOp,
) {
let source_len = match self.function_context.get_or_create_variable(
self.brillig_context,
source_value,
dfg,
) {
RegisterOrMemory::RegisterIndex(register_index) => register_index,
_ => unreachable!("ICE: first value of a slice must be a register index"),
};
let source_len_variable =
self.function_context.get_or_create_variable(self.brillig_context, source_value, dfg);
let source_len = self.function_context.extract_register(source_len_variable);

self.brillig_context.usize_op(source_len, target_len, binary_op, 1);
}
Expand Down
2 changes: 0 additions & 2 deletions crates/noirc_evaluator/src/brillig/brillig_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,13 +853,11 @@ impl BrilligContext {
pub(crate) fn radix_instruction(
&mut self,
source: RegisterIndex,
target_vector_len: RegisterIndex,
target_vector: HeapVector,
radix: RegisterIndex,
limb_count: RegisterIndex,
big_endian: bool,
) {
self.mov_instruction(target_vector_len, limb_count);
self.mov_instruction(target_vector.size, limb_count);
self.allocate_array_instruction(target_vector.pointer, target_vector.size);

Expand Down
5 changes: 3 additions & 2 deletions crates/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ pub(crate) fn optimize_into_acir(
.simplify_cfg()
.print(print_ssa_passes, "After Simplifying:")
// Run mem2reg before flattening to handle any promotion
// of values that can be accessed after loop unrolling
// If this pass is missed slice merging will fail inside of flattening
// of values that can be accessed after loop unrolling.
// If there are slice mergers uncovered by loop unrolling
// and this pass is missed, slice merging will fail inside of flattening.
jfecher marked this conversation as resolved.
Show resolved Hide resolved
.mem2reg()
.print(print_ssa_passes, "After Mem2Reg:")
.flatten_cfg()
Expand Down
22 changes: 10 additions & 12 deletions crates/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@
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::field()))
} else if let Some(length) = dfg.get_numeric_constant(arguments[0]) {
SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::field()))
} else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) {
SimplifyResult::SimplifiedTo(arguments[0])
} else {
Expand Down Expand Up @@ -222,19 +220,19 @@
}
}

// Slices have a tuple structure (slice length, slice contents) to enable logic
// that uses dynamic slice lengths (such as with merging slices in the flattening pass).
// This method codegens an update to the slice length.
/// Slices have a tuple structure (slice length, slice contents) to enable logic
/// that uses dynamic slice lengths (such as with merging slices in the flattening pass).

Check warning on line 224 in crates/noirc_evaluator/src/ssa/ir/instruction/call.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (codegens)
/// This method codegens an update to the slice length.
///
/// The binary operation performed on the slice length is always an addition or subtraction of `1`.
/// This is because the slice length holds the user length (length as displayed by a `.len()` call),
/// and not a flattened length used internally to represent arrays of tuples.
fn update_slice_length(slice_len: ValueId, dfg: &mut DataFlowGraph, operator: BinaryOp) -> ValueId {
let one = dfg.make_constant(FieldElement::one(), Type::field());
let block = dfg.make_block();
dfg.insert_instruction_and_results(
Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }),
block,
None,
dfg.get_value_call_stack(slice_len),
)
.first()
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()
}

/// Try to simplify this black box call. If the call can be simplified to a known value,
Expand Down
8 changes: 3 additions & 5 deletions crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ impl<'f> Context<'f> {
_ => panic!("Expected array value"),
};

let len = if len > else_len { len } else { else_len };
let len = len.max(else_len);

for i in 0..len {
for (element_index, element_type) in element_types.iter().enumerate() {
Expand All @@ -440,10 +440,8 @@ impl<'f> Context<'f> {
// 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 - 1) < index_value.to_u128() as usize {
self.inserter
.function
.dfg
.make_constant(FieldElement::zero(), Type::field())
let zero = FieldElement::zero();
self.inserter.function.dfg.make_constant(zero, Type::field())
} else {
let get = Instruction::ArrayGet { array, index };
self.insert_instruction_with_typevars(get, typevars).first()
Expand Down
67 changes: 35 additions & 32 deletions crates/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,7 @@ impl<'a> FunctionContext<'a> {
ast::Type::Unit => panic!("convert_non_tuple_type called on a unit type"),
ast::Type::Tuple(_) => panic!("convert_non_tuple_type called on a tuple: {typ}"),
ast::Type::Function(_, _, _) => Type::Function,
ast::Type::Slice(element) => {
let element_types = Self::convert_type(element).flatten();
Type::Slice(Rc::new(element_types))
}
ast::Type::Slice(_) => panic!("convert_non_tuple_type called on a slice: {typ}"),
ast::Type::MutableReference(element) => {
// Recursive call to panic if element is a tuple
Self::convert_non_tuple_type(element);
Expand Down Expand Up @@ -585,10 +582,10 @@ impl<'a> FunctionContext<'a> {
}

/// Compile the given `array[index]` expression as a reference.
/// This will return a triple of (array, index, lvalue_ref, Option<max_length>) where the lvalue_ref records the
/// This will return a triple of (array, index, lvalue_ref, Option<length>) where the lvalue_ref records the
/// structure of the lvalue expression for use by `assign_new_value`.
/// The optional max length is for the case where we are indexing a slice rather than an array as slices
/// are representing as the following tuple: (length, slice contents).
/// The optional length is for indexing slices rather than arrays since slices
/// are represented as a tuple in the form: (length, slice contents).
fn index_lvalue(
&mut self,
array: &ast::LValue,
Expand All @@ -599,14 +596,19 @@ impl<'a> FunctionContext<'a> {
let array_lvalue = Box::new(array_lvalue);
let array_values = old_array.clone().into_value_list(self);

// A slice is represented as a tuple (length, slice contents)
// We need to fetch the second
// A slice is represented as a tuple (length, slice contents).
// We need to fetch the second value.
if array_values.len() > 1 {
let slice_lvalue =
LValue::SliceIndex { old_slice: old_array, index, slice_lvalue: array_lvalue };
(array_values[1], index, slice_lvalue, Some(array_values[0]))
} else {
(array_values[0], index, LValue::Index { old_array: array_values[0], index, array_lvalue }, None)
(
array_values[0],
index,
LValue::Index { old_array: array_values[0], index, array_lvalue },
None,
)
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -650,34 +652,15 @@ impl<'a> FunctionContext<'a> {
match lvalue {
LValue::Ident => unreachable!("Cannot assign to a variable without a reference"),
LValue::Index { old_array: mut array, index, array_lvalue } => {
let element_size = self.builder.field_constant(self.element_size(array));

// The actual base index is the user's index * the array element type's size
let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size);
let one = self.builder.field_constant(FieldElement::one());

new_value.for_each(|value| {
let value = value.eval(self);
array = self.builder.insert_array_set(array, index, value);
index = self.builder.insert_binary(index, BinaryOp::Add, one);
});
array = self.assign_lvalue_index(new_value, array, index);
self.assign_new_value(*array_lvalue, array.into());
}
LValue::SliceIndex { old_slice: slice, index, slice_lvalue } => {
let mut slice_values = slice.into_value_list(self);

let element_size = self.builder.field_constant(self.element_size(slice_values[1]));
slice_values[1] = self.assign_lvalue_index(new_value, slice_values[1], index);

// The actual base index is the user's index * the array element type's size
let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size);
let one = self.builder.field_constant(FieldElement::one());

new_value.for_each(|value| {
let value = value.eval(self);
slice_values[1] = self.builder.insert_array_set(slice_values[1], index, value);
index = self.builder.insert_binary(index, BinaryOp::Add, one);
});
// The size of the slice does not change in an assign so we can reuse the same length value
// The size of the slice does not change in a slice index assignment so we can reuse the same length value
let new_slice = Tree::Branch(vec![slice_values[0].into(), slice_values[1].into()]);
self.assign_new_value(*slice_lvalue, new_slice);
}
Expand All @@ -691,6 +674,26 @@ impl<'a> FunctionContext<'a> {
}
}

fn assign_lvalue_index(
&mut self,
new_value: Values,
mut array: ValueId,
index: ValueId,
) -> ValueId {
let element_size = self.builder.field_constant(self.element_size(array));

// The actual base index is the user's index * the array element type's size
let mut index = self.builder.insert_binary(index, BinaryOp::Mul, element_size);
let one = self.builder.field_constant(FieldElement::one());

new_value.for_each(|value| {
let value = value.eval(self);
array = self.builder.insert_array_set(array, index, value);
index = self.builder.insert_binary(index, BinaryOp::Add, one);
});
array
}

fn element_size(&self, array: ValueId) -> FieldElement {
let size = self.builder.type_of_value(array).element_size();
FieldElement::from(size as u128)
Expand Down
Loading
Loading