Skip to content

Commit

Permalink
feat(stdlib): Add multiple slice builtins (#1888)
Browse files Browse the repository at this point in the history
* add multiple slice builtin funcs

* link merge_values issue in code for slices

* pr comments to remove unnecessary clones
  • Loading branch information
vezenovm authored Jul 10, 2023
1 parent 1b3eda1 commit 008a16b
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 3 deletions.
26 changes: 25 additions & 1 deletion crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use dep::std::slice;

use dep::std;
fn main(x : Field, y : pub Field) {
/// TODO(#1889): Using slices in if statements where the condition is a witness
/// is not yet supported

let mut slice: [Field] = [0; 2];

Expand All @@ -19,5 +21,27 @@ fn main(x : Field, y : pub Field) {
new_slice = new_slice.push_back(i);
}
assert(new_slice.len() == 5);

new_slice = new_slice.push_front(20);
assert(new_slice[0] == 20);
assert(new_slice.len() == 6);

let (popped_slice, last_elem) = new_slice.pop_back();
assert(last_elem == 4);
assert(popped_slice.len() == 5);

let (first_elem, rest_of_slice) = popped_slice.pop_front();
assert(first_elem == 20);
assert(rest_of_slice.len() == 4);

new_slice = rest_of_slice.insert(2, 100);
assert(new_slice[2] == 100);
assert(new_slice[4] == 3);
assert(new_slice.len() == 5);

let (remove_slice, removed_elem) = new_slice.remove(3);
assert(removed_elem == 2);
assert(remove_slice[3] == 3);
assert(remove_slice.len() == 4);
}

11 changes: 9 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ impl DataFlowGraph {
use InsertInstructionResult::*;
match instruction.simplify(self, block) {
SimplifyResult::SimplifiedTo(simplification) => SimplifiedTo(simplification),
SimplifyResult::SimplifiedToMultiple(simplification) => {
SimplifiedToMultiple(simplification)
}
SimplifyResult::Remove => InstructionRemoved,
SimplifyResult::None => {
let id = self.make_instruction(instruction, ctrl_typevars);
Expand Down Expand Up @@ -444,6 +447,7 @@ impl std::ops::IndexMut<BasicBlockId> for DataFlowGraph {
pub(crate) enum InsertInstructionResult<'dfg> {
Results(&'dfg [ValueId]),
SimplifiedTo(ValueId),
SimplifiedToMultiple(Vec<ValueId>),
InstructionRemoved,
}

Expand All @@ -452,6 +456,7 @@ impl<'dfg> InsertInstructionResult<'dfg> {
pub(crate) fn first(&self) -> ValueId {
match self {
InsertInstructionResult::SimplifiedTo(value) => *value,
InsertInstructionResult::SimplifiedToMultiple(values) => values[0],
InsertInstructionResult::Results(results) => results[0],
InsertInstructionResult::InstructionRemoved => {
panic!("Instruction was removed, no results")
Expand All @@ -461,10 +466,11 @@ impl<'dfg> InsertInstructionResult<'dfg> {

/// Return all the results contained in the internal results array.
/// This is used for instructions returning multiple results like function calls.
pub(crate) fn results(&self) -> Cow<'dfg, [ValueId]> {
pub(crate) fn results(self) -> Cow<'dfg, [ValueId]> {
match self {
InsertInstructionResult::Results(results) => Cow::Borrowed(results),
InsertInstructionResult::SimplifiedTo(result) => Cow::Owned(vec![*result]),
InsertInstructionResult::SimplifiedTo(result) => Cow::Owned(vec![result]),
InsertInstructionResult::SimplifiedToMultiple(results) => Cow::Owned(results),
InsertInstructionResult::InstructionRemoved => {
panic!("InsertInstructionResult::results called on a removed instruction")
}
Expand All @@ -475,6 +481,7 @@ impl<'dfg> InsertInstructionResult<'dfg> {
pub(crate) fn len(&self) -> usize {
match self {
InsertInstructionResult::SimplifiedTo(_) => 1,
InsertInstructionResult::SimplifiedToMultiple(results) => results.len(),
InsertInstructionResult::Results(results) => results.len(),
InsertInstructionResult::InstructionRemoved => 0,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ impl<'f> FunctionInserter<'f> {
InsertInstructionResult::SimplifiedTo(new_result) => {
values.insert(old_results[0], *new_result);
}
InsertInstructionResult::SimplifiedToMultiple(new_results) => {
for (old_result, new_result) in old_results.iter().zip(new_results) {
values.insert(*old_result, *new_result);
}
}
InsertInstructionResult::Results(new_results) => {
for (old_result, new_result) in old_results.iter().zip(*new_results) {
values.insert(*old_result, *new_result);
Expand Down
76 changes: 76 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pub(crate) enum Intrinsic {
Sort,
ArrayLen,
SlicePushBack,
SlicePushFront,
SlicePopBack,
SlicePopFront,
SliceInsert,
SliceRemove,
Println,
ToBits(Endian),
ToRadix(Endian),
Expand All @@ -47,6 +52,11 @@ impl std::fmt::Display for Intrinsic {
Intrinsic::Sort => write!(f, "arraysort"),
Intrinsic::ArrayLen => write!(f, "array_len"),
Intrinsic::SlicePushBack => write!(f, "slice_push_back"),
Intrinsic::SlicePushFront => write!(f, "slice_push_front"),
Intrinsic::SlicePopBack => write!(f, "slice_pop_back"),
Intrinsic::SlicePopFront => write!(f, "slice_pop_front"),
Intrinsic::SliceInsert => write!(f, "slice_insert"),
Intrinsic::SliceRemove => write!(f, "slice_remove"),
Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"),
Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"),
Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"),
Expand All @@ -65,6 +75,11 @@ impl Intrinsic {
"arraysort" => Some(Intrinsic::Sort),
"array_len" => Some(Intrinsic::ArrayLen),
"slice_push_back" => Some(Intrinsic::SlicePushBack),
"slice_push_front" => Some(Intrinsic::SlicePushFront),
"slice_pop_back" => Some(Intrinsic::SlicePopBack),
"slice_pop_front" => Some(Intrinsic::SlicePopFront),
"slice_insert" => Some(Intrinsic::SliceInsert),
"slice_remove" => Some(Intrinsic::SliceRemove),
"to_le_radix" => Some(Intrinsic::ToRadix(Endian::Little)),
"to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)),
"to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)),
Expand Down Expand Up @@ -429,6 +444,62 @@ fn simplify_call(func: ValueId, arguments: &[ValueId], dfg: &mut DataFlowGraph)
None
}
}
Intrinsic::SlicePushFront => {
let slice = dfg.get_array_constant(arguments[0]);
if let (Some((mut slice, element_type)), elem) = (slice, arguments[1]) {
slice.push_front(elem);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedTo(new_slice)
} else {
None
}
}
Intrinsic::SlicePopBack => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((mut slice, element_type)) = slice {
let elem =
slice.pop_back().expect("There are no elements in this slice to be removed");
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![new_slice, elem])
} else {
None
}
}
Intrinsic::SlicePopFront => {
let slice = dfg.get_array_constant(arguments[0]);
if let Some((mut slice, element_type)) = slice {
let elem =
slice.pop_front().expect("There are no elements in this slice to be removed");
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![elem, new_slice])
} else {
None
}
}
Intrinsic::SliceInsert => {
let slice = dfg.get_array_constant(arguments[0]);
let index = dfg.get_numeric_constant(arguments[1]);
if let (Some((mut slice, element_type)), Some(index), value) =
(slice, index, arguments[2])
{
slice.insert(index.to_u128() as usize, value);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedTo(new_slice)
} else {
None
}
}
Intrinsic::SliceRemove => {
let slice = dfg.get_array_constant(arguments[0]);
let index = dfg.get_numeric_constant(arguments[1]);
if let (Some((mut slice, element_type)), Some(index)) = (slice, index) {
let removed_elem = slice.remove(index.to_u128() as usize);
let new_slice = dfg.make_array(slice, element_type);
SimplifiedToMultiple(vec![new_slice, removed_elem])
} else {
None
}
}
Intrinsic::BlackBox(_) | Intrinsic::Println | Intrinsic::Sort => None,
}
}
Expand Down Expand Up @@ -831,6 +902,11 @@ pub(crate) enum SimplifyResult {
/// Replace this function's result with the given value
SimplifiedTo(ValueId),

/// Replace this function's results with the given values
/// Used for when there are multiple return values from
/// a function such as a tuple
SimplifiedToMultiple(Vec<ValueId>),

/// Remove the instruction, it is unnecessary
Remove,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl Context {
let new_results =
match function.dfg.insert_instruction_and_results(instruction, block, ctrl_typevars) {
InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result],
InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results,
InsertInstructionResult::Results(new_results) => new_results.to_vec(),
InsertInstructionResult::InstructionRemoved => vec![],
};
Expand Down
1 change: 1 addition & 0 deletions crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ impl<'f> Context<'f> {
then_value,
else_value,
),
// TODO(#1889)
Type::Slice(_) => panic!("Cannot return slices from an if expression"),
Type::Reference => panic!("Cannot return references from an if expression"),
Type::Function => panic!("Cannot return functions from an if expression"),
Expand Down
5 changes: 5 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@ impl<'function> PerFunctionContext<'function> {
InsertInstructionResult::SimplifiedTo(new_result) => {
values.insert(old_results[0], new_result);
}
InsertInstructionResult::SimplifiedToMultiple(new_results) => {
for (old_result, new_result) in old_results.iter().zip(new_results) {
values.insert(*old_result, new_result);
}
}
InsertInstructionResult::Results(new_results) => {
for (old_result, new_result) in old_results.iter().zip(new_results) {
values.insert(*old_result, *new_result);
Expand Down
27 changes: 27 additions & 0 deletions noir_stdlib/src/slice.nr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,33 @@ impl<T> [T] {
#[builtin(slice_push_back)]
fn push_back(_self: Self, _elem: T) -> Self { }

/// Push a new element to the front of the slice, returning a
/// new slice with a length one greater than the
/// original unmodified slice.
#[builtin(slice_push_front)]
fn push_front(_self: Self, _elem: T) -> Self { }

/// Remove the last element of the slice, returning the
/// popped slice and the element in a tuple
#[builtin(slice_pop_back)]
fn pop_back(_self: Self) -> (Self, T) { }

/// Remove the first element of the slice, returning the
/// element and the popped slice in a tuple
#[builtin(slice_pop_front)]
fn pop_front(_self: Self) -> (T, Self) { }

/// Insert an element at a specified index, shifting all elements
/// after it to the right
#[builtin(slice_insert)]
fn insert(_self: Self, _index: Field, _elem: T) -> Self { }

/// Remove an element at a specified index, shifting all elements
/// after it to the left, returning the altered slice and
/// the removed element
#[builtin(slice_remove)]
fn remove(_self: Self, _index: Field) -> (Self, T) { }

#[builtin(array_len)]
fn len(_self: Self) -> comptime Field {}

Expand Down

0 comments on commit 008a16b

Please sign in to comment.