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,16 +1,13 @@
use iter_extended::vecmap;

use crate::acir::{arrays, types::AcirValue};
use crate::errors::RuntimeError;
use crate::ssa::ir::{
dfg::DataFlowGraph,
instruction::{Hint, Intrinsic},
types::Type,
value::ValueId,
};
use crate::{
acir::{arrays, types::AcirValue},
ssa::ir::types::NumericType,
};

use super::Context;

Expand Down Expand Up @@ -100,21 +97,6 @@ impl Context<'_> {
.bit_decompose(endian, field, array_length, numeric_type)
.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, NumericType::length_type()),
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 @@ -149,7 +131,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
");
}
129 changes: 112 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,56 @@ 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() {
return SimplifyResult::None;
}

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 new_slice =
make_array(dfg, elements, Type::Slice(element_types.clone()), block, call_stack);
let slice_length = dfg.make_constant(length.into(), NumericType::length_type());
SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice])
}
Intrinsic::SlicePushBack => {
let slice = dfg.get_array_constant(arguments[1]);
Expand Down Expand Up @@ -921,4 +951,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