Skip to content

Commit 339c17b

Browse files
authored
feat: Extract brillig slice ops to reusable procedures (#6002)
# Description ## Problem\* Calling the same slice operation more than once won't duplicate the bytecode anymore, will just jump to it. ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent 8712f4c commit 339c17b

File tree

9 files changed

+600
-361
lines changed

9 files changed

+600
-361
lines changed

compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs

+73-345
Large diffs are not rendered by default.

compiler/noirc_evaluator/src/brillig/brillig_ir.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,6 @@ impl<F: AcirField + DebugToString> BrilligContext<F, Stack> {
112112
can_call_procedures: true,
113113
}
114114
}
115-
/// Allows disabling procedures so tests don't need a linking pass
116-
pub(crate) fn disable_procedures(&mut self) {
117-
self.can_call_procedures = false;
118-
}
119115
}
120116

121117
/// Special brillig context to codegen compiler intrinsic shared procedures
@@ -165,7 +161,8 @@ pub(crate) mod tests {
165161
use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext};
166162
use crate::ssa::ir::function::FunctionId;
167163

168-
use super::artifact::{BrilligParameter, GeneratedBrillig, Label};
164+
use super::artifact::{BrilligParameter, GeneratedBrillig, Label, LabelType};
165+
use super::procedures::compile_procedure;
169166
use super::registers::Stack;
170167
use super::{BrilligOpcode, ReservedRegisters};
171168

@@ -237,13 +234,17 @@ pub(crate) mod tests {
237234
returns: Vec<BrilligParameter>,
238235
) -> GeneratedBrillig<FieldElement> {
239236
let artifact = context.artifact();
240-
let mut entry_point_artifact = BrilligContext::new_entry_point_artifact(
241-
arguments,
242-
returns,
243-
FunctionId::test_new(0),
244-
true,
245-
);
237+
let mut entry_point_artifact =
238+
BrilligContext::new_entry_point_artifact(arguments, returns, FunctionId::test_new(0));
246239
entry_point_artifact.link_with(&artifact);
240+
while let Some(unresolved_fn_label) = entry_point_artifact.first_unresolved_function_call()
241+
{
242+
let LabelType::Procedure(procedure_id) = unresolved_fn_label.label_type else {
243+
panic!("Test functions cannot be linked with other functions");
244+
};
245+
let procedure_artifact = compile_procedure(procedure_id);
246+
entry_point_artifact.link_with(&procedure_artifact);
247+
}
247248
entry_point_artifact.finish()
248249
}
249250

compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@ impl<F: AcirField + DebugToString> BrilligContext<F, Stack> {
1818
arguments: Vec<BrilligParameter>,
1919
return_parameters: Vec<BrilligParameter>,
2020
target_function: FunctionId,
21-
disable_procedures: bool,
2221
) -> BrilligArtifact<F> {
2322
let mut context = BrilligContext::new(false);
24-
if disable_procedures {
25-
context.disable_procedures();
26-
}
23+
2724
context.codegen_entry_point(&arguments, &return_parameters);
2825

2926
context.add_external_call_instruction(target_function);

compiler/noirc_evaluator/src/brillig/brillig_ir/procedures/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
mod array_copy;
22
mod array_reverse;
33
mod mem_copy;
4+
mod prepare_vector_insert;
5+
mod prepare_vector_push;
46
mod vector_copy;
7+
mod vector_pop;
8+
mod vector_remove;
59

610
use array_copy::compile_array_copy_procedure;
711
use array_reverse::compile_array_reverse_procedure;
812
use mem_copy::compile_mem_copy_procedure;
13+
use prepare_vector_insert::compile_prepare_vector_insert_procedure;
14+
use prepare_vector_push::compile_prepare_vector_push_procedure;
915
use vector_copy::compile_vector_copy_procedure;
16+
use vector_pop::compile_vector_pop_procedure;
17+
use vector_remove::compile_vector_remove_procedure;
1018

1119
use crate::brillig::brillig_ir::AcirField;
1220

@@ -25,6 +33,10 @@ pub(crate) enum ProcedureId {
2533
ArrayReverse,
2634
VectorCopy,
2735
MemCopy,
36+
PrepareVectorPush(bool),
37+
VectorPop(bool),
38+
PrepareVectorInsert,
39+
VectorRemove,
2840
}
2941

3042
pub(crate) fn compile_procedure<F: AcirField + DebugToString>(
@@ -38,6 +50,16 @@ pub(crate) fn compile_procedure<F: AcirField + DebugToString>(
3850
ProcedureId::ArrayCopy => compile_array_copy_procedure(&mut brillig_context),
3951
ProcedureId::ArrayReverse => compile_array_reverse_procedure(&mut brillig_context),
4052
ProcedureId::VectorCopy => compile_vector_copy_procedure(&mut brillig_context),
53+
ProcedureId::PrepareVectorPush(push_back) => {
54+
compile_prepare_vector_push_procedure(&mut brillig_context, push_back);
55+
}
56+
ProcedureId::VectorPop(pop_back) => {
57+
compile_vector_pop_procedure(&mut brillig_context, pop_back);
58+
}
59+
ProcedureId::PrepareVectorInsert => {
60+
compile_prepare_vector_insert_procedure(&mut brillig_context);
61+
}
62+
ProcedureId::VectorRemove => compile_vector_remove_procedure(&mut brillig_context),
4163
};
4264

4365
brillig_context.stop_instruction();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use std::vec;
2+
3+
use acvm::{acir::brillig::MemoryAddress, AcirField};
4+
5+
use super::ProcedureId;
6+
use crate::brillig::brillig_ir::{
7+
brillig_variable::{BrilligVector, SingleAddrVariable},
8+
debug_show::DebugToString,
9+
registers::{RegisterAllocator, ScratchSpace},
10+
BrilligBinaryOp, BrilligContext,
11+
};
12+
13+
impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<F, Registers> {
14+
/// It prepares a vector for a insert operation, leaving a hole at the index position which is returned as the write_pointer.
15+
pub(crate) fn call_prepare_vector_insert_procedure(
16+
&mut self,
17+
source_vector: BrilligVector,
18+
destination_vector: BrilligVector,
19+
index: SingleAddrVariable,
20+
write_pointer: MemoryAddress,
21+
item_count: usize,
22+
) {
23+
let source_vector_pointer_arg = MemoryAddress::from(ScratchSpace::start());
24+
let index_arg = MemoryAddress::from(ScratchSpace::start() + 1);
25+
let item_count_arg = MemoryAddress::from(ScratchSpace::start() + 2);
26+
let new_vector_pointer_return = MemoryAddress::from(ScratchSpace::start() + 3);
27+
let write_pointer_return = MemoryAddress::from(ScratchSpace::start() + 4);
28+
29+
self.mov_instruction(source_vector_pointer_arg, source_vector.pointer);
30+
self.mov_instruction(index_arg, index.address);
31+
self.usize_const_instruction(item_count_arg, item_count.into());
32+
33+
self.add_procedure_call_instruction(ProcedureId::PrepareVectorInsert);
34+
35+
self.mov_instruction(destination_vector.pointer, new_vector_pointer_return);
36+
self.mov_instruction(write_pointer, write_pointer_return);
37+
}
38+
}
39+
40+
pub(super) fn compile_prepare_vector_insert_procedure<F: AcirField + DebugToString>(
41+
brillig_context: &mut BrilligContext<F, ScratchSpace>,
42+
) {
43+
let source_vector_pointer_arg = MemoryAddress::from(ScratchSpace::start());
44+
let index_arg = MemoryAddress::from(ScratchSpace::start() + 1);
45+
let item_count_arg = MemoryAddress::from(ScratchSpace::start() + 2);
46+
let new_vector_pointer_return = MemoryAddress::from(ScratchSpace::start() + 3);
47+
let write_pointer_return = MemoryAddress::from(ScratchSpace::start() + 4);
48+
49+
brillig_context.set_allocated_registers(vec![
50+
source_vector_pointer_arg,
51+
index_arg,
52+
item_count_arg,
53+
new_vector_pointer_return,
54+
write_pointer_return,
55+
]);
56+
57+
let source_vector = BrilligVector { pointer: source_vector_pointer_arg };
58+
let target_vector = BrilligVector { pointer: new_vector_pointer_return };
59+
let index = SingleAddrVariable::new_usize(index_arg);
60+
61+
// First we need to allocate the target vector incrementing the size by items.len()
62+
let source_size = brillig_context.codegen_make_vector_length(source_vector);
63+
64+
let target_size = SingleAddrVariable::new_usize(brillig_context.allocate_register());
65+
brillig_context.memory_op_instruction(
66+
source_size.address,
67+
item_count_arg,
68+
target_size.address,
69+
BrilligBinaryOp::Add,
70+
);
71+
72+
brillig_context.codegen_initialize_vector(target_vector, target_size);
73+
74+
// Copy the elements to the left of the index
75+
let source_vector_items_pointer =
76+
brillig_context.codegen_make_vector_items_pointer(source_vector);
77+
let target_vector_items_pointer =
78+
brillig_context.codegen_make_vector_items_pointer(target_vector);
79+
80+
brillig_context.codegen_mem_copy(
81+
source_vector_items_pointer,
82+
target_vector_items_pointer,
83+
index,
84+
);
85+
86+
// Compute the source pointer just at the index
87+
let source_pointer_at_index = brillig_context.allocate_register();
88+
brillig_context.memory_op_instruction(
89+
source_vector_items_pointer,
90+
index_arg,
91+
source_pointer_at_index,
92+
BrilligBinaryOp::Add,
93+
);
94+
95+
// Compute the target pointer after the inserted elements
96+
brillig_context.memory_op_instruction(
97+
target_vector_items_pointer,
98+
index.address,
99+
write_pointer_return,
100+
BrilligBinaryOp::Add,
101+
);
102+
let target_pointer_after_index = brillig_context.allocate_register();
103+
104+
brillig_context.memory_op_instruction(
105+
write_pointer_return,
106+
item_count_arg,
107+
target_pointer_after_index,
108+
BrilligBinaryOp::Add,
109+
);
110+
111+
// Compute the number of elements to the right of the index
112+
let item_count = brillig_context.allocate_register();
113+
brillig_context.memory_op_instruction(
114+
source_size.address,
115+
index.address,
116+
item_count,
117+
BrilligBinaryOp::Sub,
118+
);
119+
120+
// Copy the elements to the right of the index
121+
brillig_context.codegen_mem_copy(
122+
source_pointer_at_index,
123+
target_pointer_after_index,
124+
SingleAddrVariable::new_usize(item_count),
125+
);
126+
127+
brillig_context.deallocate_register(source_pointer_at_index);
128+
brillig_context.deallocate_register(target_pointer_after_index);
129+
brillig_context.deallocate_register(item_count);
130+
brillig_context.deallocate_single_addr(source_size);
131+
brillig_context.deallocate_single_addr(target_size);
132+
brillig_context.deallocate_register(source_vector_items_pointer);
133+
brillig_context.deallocate_register(target_vector_items_pointer);
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use std::vec;
2+
3+
use acvm::{acir::brillig::MemoryAddress, AcirField};
4+
5+
use super::ProcedureId;
6+
use crate::brillig::brillig_ir::{
7+
brillig_variable::{BrilligVector, SingleAddrVariable},
8+
debug_show::DebugToString,
9+
registers::{RegisterAllocator, ScratchSpace},
10+
BrilligBinaryOp, BrilligContext,
11+
};
12+
13+
impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<F, Registers> {
14+
/// Prepares a vector for a push operation, allocating a larger vector and copying the source vector into the destination vector.
15+
/// It returns the write pointer to where to put the new items.
16+
pub(crate) fn call_prepare_vector_push_procedure(
17+
&mut self,
18+
source_vector: BrilligVector,
19+
destination_vector: BrilligVector,
20+
write_pointer: MemoryAddress,
21+
item_push_count: usize,
22+
back: bool,
23+
) {
24+
let source_vector_pointer_arg = MemoryAddress::from(ScratchSpace::start());
25+
let item_push_count_arg = MemoryAddress::from(ScratchSpace::start() + 1);
26+
let new_vector_pointer_return = MemoryAddress::from(ScratchSpace::start() + 2);
27+
let write_pointer_return = MemoryAddress::from(ScratchSpace::start() + 3);
28+
29+
self.mov_instruction(source_vector_pointer_arg, source_vector.pointer);
30+
self.usize_const_instruction(item_push_count_arg, item_push_count.into());
31+
32+
self.add_procedure_call_instruction(ProcedureId::PrepareVectorPush(back));
33+
34+
self.mov_instruction(destination_vector.pointer, new_vector_pointer_return);
35+
self.mov_instruction(write_pointer, write_pointer_return);
36+
}
37+
}
38+
39+
pub(super) fn compile_prepare_vector_push_procedure<F: AcirField + DebugToString>(
40+
brillig_context: &mut BrilligContext<F, ScratchSpace>,
41+
push_back: bool,
42+
) {
43+
let source_vector_pointer_arg = MemoryAddress::from(ScratchSpace::start());
44+
let item_push_count_arg = MemoryAddress::from(ScratchSpace::start() + 1);
45+
let new_vector_pointer_return = MemoryAddress::from(ScratchSpace::start() + 2);
46+
let write_pointer_return = MemoryAddress::from(ScratchSpace::start() + 3);
47+
48+
brillig_context.set_allocated_registers(vec![
49+
source_vector_pointer_arg,
50+
item_push_count_arg,
51+
new_vector_pointer_return,
52+
write_pointer_return,
53+
]);
54+
55+
let source_vector = BrilligVector { pointer: source_vector_pointer_arg };
56+
let target_vector = BrilligVector { pointer: new_vector_pointer_return };
57+
58+
// First we need to allocate the target vector incrementing the size by item_push_count_arg
59+
let source_size = brillig_context.codegen_make_vector_length(source_vector);
60+
61+
let target_size = SingleAddrVariable::new_usize(brillig_context.allocate_register());
62+
brillig_context.memory_op_instruction(
63+
source_size.address,
64+
item_push_count_arg,
65+
target_size.address,
66+
BrilligBinaryOp::Add,
67+
);
68+
69+
brillig_context.codegen_initialize_vector(target_vector, target_size);
70+
71+
// Now we copy the source vector into the target vector
72+
let source_vector_items_pointer =
73+
brillig_context.codegen_make_vector_items_pointer(source_vector);
74+
let target_vector_items_pointer =
75+
brillig_context.codegen_make_vector_items_pointer(target_vector);
76+
77+
if push_back {
78+
brillig_context.codegen_mem_copy(
79+
source_vector_items_pointer,
80+
target_vector_items_pointer,
81+
source_size,
82+
);
83+
84+
brillig_context.memory_op_instruction(
85+
target_vector_items_pointer,
86+
source_size.address,
87+
write_pointer_return,
88+
BrilligBinaryOp::Add,
89+
);
90+
} else {
91+
brillig_context.mov_instruction(write_pointer_return, target_vector_items_pointer);
92+
93+
brillig_context.memory_op_instruction(
94+
target_vector_items_pointer,
95+
item_push_count_arg,
96+
target_vector_items_pointer,
97+
BrilligBinaryOp::Add,
98+
);
99+
100+
brillig_context.codegen_mem_copy(
101+
source_vector_items_pointer,
102+
target_vector_items_pointer,
103+
source_size,
104+
);
105+
}
106+
107+
brillig_context.deallocate_single_addr(source_size);
108+
brillig_context.deallocate_single_addr(target_size);
109+
brillig_context.deallocate_register(source_vector_items_pointer);
110+
brillig_context.deallocate_register(target_vector_items_pointer);
111+
}

0 commit comments

Comments
 (0)