Skip to content
23 changes: 3 additions & 20 deletions compiler/noirc_evaluator/src/acir/call/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use iter_extended::vecmap;

use crate::acir::{
arrays,
types::{AcirType, AcirValue},
};
use crate::acir::{arrays, types::AcirValue};
use crate::errors::RuntimeError;
use crate::ssa::ir::{
dfg::DataFlowGraph,
Expand Down Expand Up @@ -92,21 +89,6 @@ impl Context<'_> {
.bit_decompose(endian, field, array_length, result_type[0].clone().into())
.map(|array| vec![array])
}
Intrinsic::AsSlice => {
let array_contents = arguments[0];
let array_type = dfg.type_of_value(array_contents);
assert!(!array_type.is_nested_slice(), "ICE: Nested slice used in ACIR generation");
let Type::Array(_, slice_length) = array_type else {
unreachable!("Expected Array input for `as_slice` intrinsic");
};
let slice_length = self.acir_context.add_constant(slice_length);
let acir_value = self.convert_value(array_contents, dfg);
let result = self.read_array(acir_value)?;
Ok(vec![
AcirValue::Var(slice_length, AcirType::unsigned(32)),
AcirValue::Array(result),
])
}

Intrinsic::SlicePushBack => self.convert_slice_push_back(arguments, dfg),
Intrinsic::SlicePushFront => self.convert_slice_push_front(arguments, dfg),
Expand Down Expand Up @@ -141,7 +123,8 @@ impl Context<'_> {
| Intrinsic::StaticAssert
| Intrinsic::AssertConstant
| Intrinsic::ArrayRefCount
| Intrinsic::SliceRefCount => {
| Intrinsic::SliceRefCount
| Intrinsic::AsSlice => {
unreachable!("Expected {intrinsic} to have been removing during SSA optimizations")
}
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/noirc_evaluator/src/acir/tests/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ fn does_not_generate_memory_blocks_without_dynamic_accesses() {
let src = "
acir(inline) fn main f0 {
b0(v0: [Field; 2]):
v2, v3 = call as_slice(v0) -> (u32, [Field])
v1 = array_get v0, index u32 0 -> Field
v2 = array_get v0, index u32 1 -> Field
v3 = make_array [v1, v2] : [Field]
Comment thread
jfecher marked this conversation as resolved.
call f1(u32 2, v3)
v7 = array_get v0, index u32 0 -> Field
constrain v7 == Field 0
Expand Down
26 changes: 0 additions & 26 deletions compiler/noirc_evaluator/src/acir/tests/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,29 +511,3 @@ fn slice_remove_affected_by_predicate() {
let program_no_side_effects = ssa_to_acir_program(src_no_side_effects);
assert_ne!(program_side_effects, program_no_side_effects);
}

#[test]
fn as_slice_for_composite_slice() {
let src = "
acir(inline) predicate_pure fn main f0 {
b0():
v3 = make_array [Field 10, Field 20, Field 30, Field 40] : [(Field, Field); 2]
v4, v5 = call as_slice(v3) -> (u32, [(Field, Field)])
return v4
}
";
let program = ssa_to_acir_program(src);

// Note that 2 is returned, not 4 (as there are two `(Field, Field)` elements)
assert_circuit_snapshot!(program, @r"
func 0
private parameters: []
public parameters: []
return values: [w0]
ASSERT w1 = 10
ASSERT w2 = 20
ASSERT w3 = 30
ASSERT w4 = 40
ASSERT w0 = 2
");
}
132 changes: 115 additions & 17 deletions compiler/noirc_evaluator/src/ssa/ir/dfg/simplify/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,26 +106,59 @@ pub(super) fn simplify_call(
// Strings are already arrays of bytes in SSA
Intrinsic::ArrayAsStrUnchecked => SimplifyResult::SimplifiedTo(arguments[0]),
Intrinsic::AsSlice => {
let array_type = dfg.type_of_value(arguments[0]);
let Type::Array(element_types, length) = array_type else {
panic!("Expected as_slice input to be an array")
};

let array = dfg.get_array_constant(arguments[0]);
if let Some((array, array_type)) = array {
// Compute the resulting slice length by dividing the flattened
// array length by the size of each array element
let elements_size = array_type.element_size();
let inner_element_types = array_type.element_types();
assert_eq!(
0,
array.len() % elements_size,
"expected array length to be multiple of its elements size"
);
let slice_length_value = array.len() / elements_size;
let slice_length =
dfg.make_constant(slice_length_value.into(), NumericType::length_type());
if let Some((array, _array_type)) = array {
let slice_length = dfg.make_constant(length.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])
} else {
SimplifyResult::None
make_array(dfg, array, Type::Slice(element_types), block, call_stack);
return SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]);
}

// In ACIR we can simplify `as_slice(array)`, to:
//
// ```
// v0 = array_get array, index u32 0 -> T
// v1 = array_get array, index u32 1 -> T
// ...
// vN = make_array [v0, v1, ...]
// ```
//
// We don't do this for Brillig because it sometimes leads to more opcodes.
if dfg.runtime().is_acir() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reduce nesting here with an inverted condition and early return

let mut elements = im::Vector::default();
let mut index: u32 = 0;
for _ in 0..length {
for element_type in element_types.iter() {
let index_value =
dfg.make_constant(index.into(), NumericType::length_type());
let array_get =
Instruction::ArrayGet { array: arguments[0], index: index_value };
let element = dfg
.insert_instruction_and_results(
array_get,
block,
Some(vec![element_type.clone()]),
call_stack,
)
.first();
elements.push_back(element);
index += 1;
}
}
let make_array =
Instruction::MakeArray { elements, typ: Type::Slice(element_types.clone()) };
let slice_length = dfg.make_constant(length.into(), NumericType::length_type());
let new_slice =
dfg.insert_instruction_and_results(make_array, block, None, call_stack).first();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a make_array helper function

return SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]);
}

return SimplifyResult::None;
}
Intrinsic::SlicePushBack => {
let slice = dfg.get_array_constant(arguments[1]);
Expand Down Expand Up @@ -921,4 +954,69 @@ mod tests {
"#;
let _ = Ssa::from_str_simplifying(src).unwrap();
}

#[test]
fn simplifies_as_slice_for_known_array() {
let src = r#"
acir(inline) fn main func {
b0():
v0 = make_array [Field 1, Field 2, Field 3] : [Field; 3]
v1, v2 = call as_slice(v0) -> (u32, [Field])
return v1, v2
}
"#;
let ssa = Ssa::from_str_simplifying(src).unwrap();

assert_ssa_snapshot!(ssa, @r"
acir(inline) fn main f0 {
b0():
v3 = make_array [Field 1, Field 2, Field 3] : [Field; 3]
v4 = make_array [Field 1, Field 2, Field 3] : [Field]
return u32 3, v4
}
");
}

#[test]
fn simplifies_as_slice_for_unknown_array_in_acir() {
let src = r#"
acir(inline) fn main func {
b0(v0: [Field; 3]):
v1, v2 = call as_slice(v0) -> (u32, [Field])
return v1, v2
}
"#;
let ssa = Ssa::from_str_simplifying(src).unwrap();

assert_ssa_snapshot!(ssa, @r"
acir(inline) fn main f0 {
b0(v0: [Field; 3]):
v2 = array_get v0, index u32 0 -> Field
Comment thread
vezenovm marked this conversation as resolved.
v4 = array_get v0, index u32 1 -> Field
v6 = array_get v0, index u32 2 -> Field
v7 = make_array [v2, v4, v6] : [Field]
return u32 3, v7
}
");
}

#[test]
fn does_not_simplify_as_slice_for_unknown_array_in_brillig() {
let src = r#"
brillig(inline) fn main func {
b0(v0: [Field; 3]):
v1, v2 = call as_slice(v0) -> (u32, [Field])
return v1, v2
}
"#;
let ssa = Ssa::from_str_simplifying(src).unwrap();

assert_ssa_snapshot!(ssa, @r"
brillig(inline) fn main f0 {
b0(v0: [Field; 3]):
v2, v3 = call as_slice(v0) -> (u32, [Field])
return v2, v3
}
");
}
}
Loading