Skip to content

Commit 4bee979

Browse files
vezenovmjfecherkevaundray
authored
feat(ssa_refactor)!: Add Slices (#1728)
* initial slices work on frontend * cargo clippy * develop comment cleanup * working push_back and len slices commands * cargo clippy * cleanup * fix clippy * empty slice being processed * cargo clippy * delete old comment * remove unused import * add enable_slices flag to avoid slices in old SSA * add new SSA type for slices * missing Slice conversion * Update crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs Co-authored-by: jfecher <jake@aztecprotocol.com> * PR comments, fix slice/array subtyping * hack to handle duplicate methods and mismatched types when compiling w/ old SSA pass that does not handle slices * cleanup enable_slices tech debt to be part of the NodeInterner * reference enable_slices flag issue * cleanup name * remove SSA Value::Slice * remove dbg * fix array len from slice params * remove old debug * unwrap_array_element_type method in monomorphization pass * cargo clippy * mark issue in TODO comment for tech debt that removes slice module in stdlib * Update crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs Co-authored-by: jfecher <jake@aztecprotocol.com> * fix stdlib crate check * Update noir_stdlib/src/slice.nr Co-authored-by: jfecher <jake@aztecprotocol.com> * Update noir_stdlib/src/slice.nr Co-authored-by: jfecher <jake@aztecprotocol.com> * Update noir_stdlib/src/slice.nr Co-authored-by: jfecher <jake@aztecprotocol.com> * Update noir_stdlib/src/slice.nr Co-authored-by: jfecher <jake@aztecprotocol.com> * PR comments and stdlib crate assert * cargo fmt * keep enable slices check in expr type check * increase timeout time * increase timeout to 60m * back to 30m timeout * excldue 6_array from old ssa test and move 6_array into ssa_refactor tests * unwrap_or_else for first_elem_type * only test new ssa :D * add back old ssa tests * remove Vec type * remove Vec type check * remove Vec from stdlib as we have no primitive type until we add in Vec using mutable refs * try w/ a pub input to 6_array * im dumb and misplaced the pub * remove pub * temp: remove unused tests for debugging * Revert "temp: remove unused tests for debugging" This reverts commit daac684. --------- Co-authored-by: jfecher <jake@aztecprotocol.com> Co-authored-by: kevaundray <kevtheappdev@gmail.com>
1 parent 26d078d commit 4bee979

File tree

36 files changed

+337
-150
lines changed

36 files changed

+337
-150
lines changed

crates/fm/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl FileManager {
7070
// Unwrap as we ensure that all file_id's map to a corresponding file in the file map
7171
self.file_map.get_file(file_id).unwrap()
7272
}
73-
fn path(&mut self, file_id: FileId) -> &Path {
73+
pub fn path(&mut self, file_id: FileId) -> &Path {
7474
// Unwrap as we ensure that all file_ids are created by the file manager
7575
// So all file_ids will points to a corresponding path
7676
self.id_to_path.get(&file_id).unwrap().0.as_path()

crates/lsp/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fn on_code_lens_request(
139139

140140
// We ignore the warnings and errors produced by compilation for producing codelenses
141141
// because we can still get the test functions even if compilation fails
142-
let _ = check_crate(&mut state.context, false);
142+
let _ = check_crate(&mut state.context, false, false);
143143

144144
let fm = &state.context.file_manager;
145145
let files = fm.as_simple_files();
@@ -224,7 +224,7 @@ fn on_did_save_text_document(
224224

225225
let mut diagnostics = Vec::new();
226226

227-
let file_diagnostics = match check_crate(&mut state.context, false) {
227+
let file_diagnostics = match check_crate(&mut state.context, false, false) {
228228
Ok(warnings) => warnings,
229229
Err(errors_and_warnings) => errors_and_warnings,
230230
};

crates/nargo_cli/src/cli/check_cmd.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn check_from_path<B: Backend>(
3737
compile_options: &CompileOptions,
3838
) -> Result<(), CliError<B>> {
3939
let mut context = resolve_root_manifest(program_dir)?;
40-
check_crate_and_report_errors(&mut context, compile_options.deny_warnings)?;
40+
check_crate_and_report_errors(&mut context, compile_options.deny_warnings, compile_options.experimental_ssa)?;
4141

4242
// XXX: We can have a --overwrite flag to determine if you want to overwrite the Prover/Verifier.toml files
4343
if let Some((parameters, return_type)) = compute_function_signature(&context) {
@@ -214,7 +214,8 @@ d2 = ["", "", ""]
214214
pub(crate) fn check_crate_and_report_errors(
215215
context: &mut Context,
216216
deny_warnings: bool,
217+
enable_slices: bool,
217218
) -> Result<(), ReportedErrors> {
218-
let result = check_crate(context, deny_warnings).map(|warnings| ((), warnings));
219+
let result = check_crate(context, deny_warnings, enable_slices).map(|warnings| ((), warnings));
219220
super::compile_cmd::report_errors(result, context, deny_warnings)
220221
}

crates/nargo_cli/src/cli/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ mod tests {
132132
let mut context = Context::default();
133133
create_local_crate(&mut context, &root_file, CrateType::Binary);
134134

135-
let result = check_crate(&mut context, false);
135+
let result = check_crate(&mut context, false, false);
136136
let success = result.is_ok();
137137

138138
let errors = match result {

crates/nargo_cli/src/cli/test_cmd.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn run_tests<B: Backend>(
4141
compile_options: &CompileOptions,
4242
) -> Result<(), CliError<B>> {
4343
let mut context = resolve_root_manifest(program_dir)?;
44-
check_crate_and_report_errors(&mut context, compile_options.deny_warnings)?;
44+
check_crate_and_report_errors(&mut context, compile_options.deny_warnings, compile_options.experimental_ssa)?;
4545

4646
let test_functions = context.get_all_test_functions_in_crate_matching(&LOCAL_CRATE, test_name);
4747
println!("Running {} test functions...", test_functions.len());

crates/nargo_cli/tests/test_data_ssa_refactor/6_array/Prover.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ t = "10"
55

66
#7128
77
#15309
8-
#16349
9-
8+
#16349

crates/nargo_cli/tests/test_data_ssa_refactor/6_array/src/main.nr

-1
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,3 @@ fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) {
5151
}
5252
}
5353
}
54-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
authors = [""]
3+
compiler_version = "0.6.0"
4+
5+
[dependencies]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
x = "5"
2+
y = "10"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use dep::std::slice;
2+
3+
fn main(x : Field, y : pub Field) {
4+
5+
let mut slice: [Field] = [0; 2];
6+
7+
assert(slice[0] == 0);
8+
assert(slice[0] != 1);
9+
slice[0] = x;
10+
assert(slice[0] == x);
11+
12+
let slice_plus_10 = slice.push_back(y);
13+
assert(slice_plus_10[2] == 10);
14+
assert(slice_plus_10[2] != 8);
15+
assert(slice_plus_10.len() == 3);
16+
17+
let mut new_slice: [Field] = [];
18+
for i in 0..5 {
19+
new_slice = new_slice.push_back(i);
20+
}
21+
assert(new_slice.len() == 5);
22+
}
23+

crates/noirc_driver/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ pub fn propagate_dep(
157157
pub fn check_crate(
158158
context: &mut Context,
159159
deny_warnings: bool,
160+
enable_slices: bool,
160161
) -> Result<Warnings, ErrorsAndWarnings> {
161162
// Add the stdlib before we check the crate
162163
// TODO: This should actually be done when constructing the driver and then propagated to each dependency when added;
@@ -167,6 +168,8 @@ pub fn check_crate(
167168
let std_crate = create_non_local_crate(context, path_to_std_lib_file, CrateType::Library);
168169
propagate_dep(context, std_crate, &CrateName::new(std_crate_name).unwrap());
169170

171+
context.def_interner.enable_slices = enable_slices;
172+
170173
let mut errors = vec![];
171174
CrateDefMap::collect_defs(LOCAL_CRATE, context, &mut errors);
172175

@@ -195,7 +198,7 @@ pub fn compile_main(
195198
is_opcode_supported: &impl Fn(&Opcode) -> bool,
196199
options: &CompileOptions,
197200
) -> Result<(CompiledProgram, Warnings), ErrorsAndWarnings> {
198-
let warnings = check_crate(context, options.deny_warnings)?;
201+
let warnings = check_crate(context, options.deny_warnings, options.experimental_ssa)?;
199202

200203
let main = match context.get_main_function(&LOCAL_CRATE) {
201204
Some(m) => m,
@@ -226,7 +229,7 @@ pub fn compile_contracts(
226229
is_opcode_supported: &impl Fn(&Opcode) -> bool,
227230
options: &CompileOptions,
228231
) -> Result<(Vec<CompiledContract>, Warnings), ErrorsAndWarnings> {
229-
let warnings = check_crate(context, options.deny_warnings)?;
232+
let warnings = check_crate(context, options.deny_warnings, options.experimental_ssa)?;
230233

231234
let contracts = context.get_all_contracts(&LOCAL_CRATE);
232235
let mut compiled_contracts = vec![];

crates/noirc_evaluator/src/ssa/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ impl SsaContext {
12121212
Type::Function(..) => ObjectType::Function,
12131213
Type::Tuple(_) => todo!("Conversion to ObjectType is unimplemented for tuples"),
12141214
Type::String(_) => todo!("Conversion to ObjectType is unimplemented for strings"),
1215-
Type::Vec(_) => todo!("Conversion to ObjectType is unimplemented for Vecs"),
1215+
Type::Slice(_) => todo!("Conversion to ObjectType is unimplemented for slices"),
12161216
}
12171217
}
12181218

crates/noirc_evaluator/src/ssa/value.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Value {
9696
Type::Unit
9797
| Type::Function(..)
9898
| Type::Array(..)
99-
| Type::Vec(..)
99+
| Type::Slice(..)
100100
| Type::String(..)
101101
| Type::Integer(..)
102102
| Type::Bool

crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ impl Context {
578578
(_, Type::Array(..)) | (Type::Array(..), _) => {
579579
unreachable!("Arrays are invalid in binary operations")
580580
}
581+
(_, Type::Slice(..)) | (Type::Slice(..), _) => {
582+
unreachable!("Arrays are invalid in binary operations")
583+
}
581584
// If either side is a Field constant then, we coerce into the type
582585
// of the other operand
583586
(Type::Numeric(NumericType::NativeField), typ)
@@ -732,6 +735,7 @@ impl Context {
732735

733736
Self::convert_vars_to_values(out_vars, dfg, result_ids)
734737
}
738+
_ => todo!("expected a black box function"),
735739
}
736740
}
737741

@@ -743,6 +747,10 @@ impl Context {
743747
assert_eq!(elements.len(), 1);
744748
(&elements[0]).into()
745749
}
750+
Type::Slice(elements) => {
751+
assert_eq!(elements.len(), 1);
752+
(&elements[0]).into()
753+
}
746754
_ => unreachable!("Expected array type"),
747755
}
748756
}

crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,16 @@ impl DataFlowGraph {
238238

239239
/// Gets or creates a ValueId for the given Intrinsic.
240240
pub(crate) fn import_intrinsic(&mut self, intrinsic: Intrinsic) -> ValueId {
241-
if let Some(existing) = self.intrinsics.get(&intrinsic) {
241+
if let Some(existing) = self.get_intrinsic(intrinsic) {
242242
return *existing;
243243
}
244-
self.values.insert(Value::Intrinsic(intrinsic))
244+
let intrinsic_value_id = self.values.insert(Value::Intrinsic(intrinsic));
245+
self.intrinsics.insert(intrinsic, intrinsic_value_id);
246+
intrinsic_value_id
247+
}
248+
249+
pub(crate) fn get_intrinsic(&mut self, intrinsic: Intrinsic) -> Option<&ValueId> {
250+
self.intrinsics.get(&intrinsic)
245251
}
246252

247253
/// Attaches results to the instruction, clearing any previous results.
@@ -360,6 +366,20 @@ impl DataFlowGraph {
360366
}
361367
}
362368

369+
/// Returns the Type::Array associated with this ValueId if it refers to an array parameter.
370+
/// Otherwise, this returns None.
371+
pub(crate) fn get_array_parameter_type(
372+
&self,
373+
value: ValueId,
374+
) -> Option<(Rc<CompositeType>, usize)> {
375+
match &self.values[self.resolve(value)] {
376+
Value::Param { typ: Type::Array(element_type, size), .. } => {
377+
Some((element_type.clone(), *size))
378+
}
379+
_ => None,
380+
}
381+
}
382+
363383
/// Sets the terminator instruction for the given basic block
364384
pub(crate) fn set_block_terminator(
365385
&mut self,

crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs

+49-13
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub(crate) type InstructionId = Id<Instruction>;
3232
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
3333
pub(crate) enum Intrinsic {
3434
Sort,
35+
ArrayLen,
36+
SlicePushBack,
3537
Println,
3638
ToBits(Endian),
3739
ToRadix(Endian),
@@ -43,6 +45,8 @@ impl std::fmt::Display for Intrinsic {
4345
match self {
4446
Intrinsic::Println => write!(f, "println"),
4547
Intrinsic::Sort => write!(f, "arraysort"),
48+
Intrinsic::ArrayLen => write!(f, "array_len"),
49+
Intrinsic::SlicePushBack => write!(f, "slice_push_back"),
4650
Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"),
4751
Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"),
4852
Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"),
@@ -59,6 +63,8 @@ impl Intrinsic {
5963
match name {
6064
"println" => Some(Intrinsic::Println),
6165
"arraysort" => Some(Intrinsic::Sort),
66+
"array_len" => Some(Intrinsic::ArrayLen),
67+
"slice_push_back" => Some(Intrinsic::SlicePushBack),
6268
"to_le_radix" => Some(Intrinsic::ToRadix(Endian::Little)),
6369
"to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)),
6470
"to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)),
@@ -276,7 +282,6 @@ impl Instruction {
276282
Instruction::ArrayGet { array, index } => {
277283
let array = dfg.get_array_constant(*array);
278284
let index = dfg.get_numeric_constant(*index);
279-
280285
if let (Some((array, _)), Some(index)) = (array, index) {
281286
let index =
282287
index.try_to_u64().expect("Expected array index to fit in u64") as usize;
@@ -290,7 +295,6 @@ impl Instruction {
290295
Instruction::ArraySet { array, index, value } => {
291296
let array = dfg.get_array_constant(*array);
292297
let index = dfg.get_numeric_constant(*index);
293-
294298
if let (Some((array, element_type)), Some(index)) = (array, index) {
295299
let index =
296300
index.try_to_u64().expect("Expected array index to fit in u64") as usize;
@@ -375,23 +379,55 @@ fn simplify_call(func: ValueId, arguments: &[ValueId], dfg: &mut DataFlowGraph)
375379
Value::Intrinsic(intrinsic) => *intrinsic,
376380
_ => return None,
377381
};
382+
378383
let constant_args: Option<Vec<_>> =
379384
arguments.iter().map(|value_id| dfg.get_numeric_constant(*value_id)).collect();
380-
let constant_args = match constant_args {
381-
Some(constant_args) => constant_args,
382-
Option::None => return None,
383-
};
385+
384386
match intrinsic {
385387
Intrinsic::ToBits(endian) => {
386-
let field = constant_args[0];
387-
let limb_count = constant_args[1].to_u128() as u32;
388-
SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg))
388+
if let Some(constant_args) = constant_args {
389+
let field = constant_args[0];
390+
let limb_count = constant_args[1].to_u128() as u32;
391+
SimplifiedTo(constant_to_radix(endian, field, 2, limb_count, dfg))
392+
} else {
393+
None
394+
}
389395
}
390396
Intrinsic::ToRadix(endian) => {
391-
let field = constant_args[0];
392-
let radix = constant_args[1].to_u128() as u32;
393-
let limb_count = constant_args[2].to_u128() as u32;
394-
SimplifiedTo(constant_to_radix(endian, field, radix, limb_count, dfg))
397+
if let Some(constant_args) = constant_args {
398+
let field = constant_args[0];
399+
let radix = constant_args[1].to_u128() as u32;
400+
let limb_count = constant_args[2].to_u128() as u32;
401+
SimplifiedTo(constant_to_radix(endian, field, radix, limb_count, dfg))
402+
} else {
403+
None
404+
}
405+
}
406+
Intrinsic::ArrayLen => {
407+
let slice = dfg.get_array_constant(arguments[0]);
408+
if let Some((slice, _)) = slice {
409+
let slice_len =
410+
dfg.make_constant(FieldElement::from(slice.len() as u128), Type::field());
411+
SimplifiedTo(slice_len)
412+
} else if let Some((_, slice_len)) = dfg.get_array_parameter_type(arguments[0]) {
413+
let slice_len = dfg.make_constant(
414+
FieldElement::from(slice_len as u128),
415+
Type::Numeric(NumericType::NativeField),
416+
);
417+
SimplifiedTo(slice_len)
418+
} else {
419+
None
420+
}
421+
}
422+
Intrinsic::SlicePushBack => {
423+
let slice = dfg.get_array_constant(arguments[0]);
424+
if let (Some((mut slice, element_type)), elem) = (slice, arguments[1]) {
425+
slice.push_back(elem);
426+
let new_slice = dfg.make_array(slice, element_type);
427+
SimplifiedTo(new_slice)
428+
} else {
429+
None
430+
}
395431
}
396432
Intrinsic::BlackBox(_) | Intrinsic::Println | Intrinsic::Sort => None,
397433
}

crates/noirc_evaluator/src/ssa_refactor/ir/types.rs

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub(crate) enum Type {
2929
/// An immutable array value with the given element type and length
3030
Array(Rc<CompositeType>, usize),
3131

32+
/// An immutable slice value with a given element type
33+
Slice(Rc<CompositeType>),
34+
3235
/// A function that may be called directly
3336
Function,
3437
}
@@ -74,6 +77,10 @@ impl std::fmt::Display for Type {
7477
let elements = vecmap(element.iter(), |element| element.to_string());
7578
write!(f, "[{}; {length}]", elements.join(", "))
7679
}
80+
Type::Slice(element) => {
81+
let elements = vecmap(element.iter(), |element| element.to_string());
82+
write!(f, "[{}]", elements.join(", "))
83+
}
7784
Type::Function => write!(f, "function"),
7885
}
7986
}

crates/noirc_evaluator/src/ssa_refactor/opt/flatten_cfg.rs

+1
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ impl<'f> Context<'f> {
388388
then_value,
389389
else_value,
390390
),
391+
Type::Slice(_) => panic!("Cannot return slices from an if expression"),
391392
Type::Reference => panic!("Cannot return references from an if expression"),
392393
Type::Function => panic!("Cannot return functions from an if expression"),
393394
}

crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -206,16 +206,15 @@ impl<'a> FunctionContext<'a> {
206206
ast::Type::Unit => panic!("convert_non_tuple_type called on a unit type"),
207207
ast::Type::Tuple(_) => panic!("convert_non_tuple_type called on a tuple: {typ}"),
208208
ast::Type::Function(_, _) => Type::Function,
209+
ast::Type::Slice(element) => {
210+
let element_types = Self::convert_type(element).flatten();
211+
Type::Slice(Rc::new(element_types))
212+
}
209213
ast::Type::MutableReference(element) => {
210214
// Recursive call to panic if element is a tuple
211215
Self::convert_non_tuple_type(element);
212216
Type::Reference
213217
}
214-
215-
// How should we represent Vecs?
216-
// Are they a struct of array + length + capacity?
217-
// Or are they just references?
218-
ast::Type::Vec(_) => Type::Reference,
219218
}
220219
}
221220

0 commit comments

Comments
 (0)