Skip to content

Commit 2e6340b

Browse files
AztecBotTomAFrench
andauthored
feat: Sync from noir (#8867)
Automated pull of development from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE feat(perf): Remove redundant inc rc without instructions between (noir-lang/noir#6183) chore: reexport `CrateName` through `nargo` (noir-lang/noir#6177) feat(perf): Remove inc_rc instructions for arrays which are never mutably borrowed (noir-lang/noir#6168) chore(docs): Add link to more info about proving backend to Solidity verifier page (noir-lang/noir#5754) feat: let `Module::functions` and `Module::structs` return them in definition order (noir-lang/noir#6178) chore: split `test_program`s into modules (noir-lang/noir#6101) chore: remove `DefCollectorErrorKind::MacroError` (noir-lang/noir#6174) feat(ssa): Simplify signed casts (noir-lang/noir#6166) feat: visibility for modules (noir-lang/noir#6165) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French <tom@tomfren.ch>
1 parent 089dbad commit 2e6340b

File tree

49 files changed

+669
-211
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+669
-211
lines changed

.noir-sync-commit

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
877b806ee02cb640472c6bb2b1ed7bc76b861a9b
1+
be9dcfe56d808b1bd5ef552d41274705b2df7062

noir/noir-repo/compiler/noirc_driver/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use noirc_evaluator::create_program;
1414
use noirc_evaluator::errors::RuntimeError;
1515
use noirc_evaluator::ssa::SsaProgramArtifact;
1616
use noirc_frontend::debug::build_debug_crate_file;
17-
use noirc_frontend::graph::{CrateId, CrateName};
1817
use noirc_frontend::hir::def_map::{Contract, CrateDefMap};
1918
use noirc_frontend::hir::Context;
2019
use noirc_frontend::monomorphization::{
@@ -35,6 +34,7 @@ use debug::filter_relevant_files;
3534

3635
pub use contract::{CompiledContract, CompiledContractOutputs, ContractFunction};
3736
pub use debug::DebugFile;
37+
pub use noirc_frontend::graph::{CrateId, CrateName};
3838
pub use program::CompiledProgram;
3939

4040
const STD_CRATE_NAME: &str = "std";

noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ pub(super) fn simplify_cast(
2727
SimplifiedTo(value)
2828
}
2929
(
30-
Type::Numeric(NumericType::Unsigned { .. }),
30+
Type::Numeric(NumericType::Unsigned { .. } | NumericType::Signed { .. }),
3131
Type::Numeric(NumericType::NativeField),
3232
) => {
33-
// Unsigned -> Field: redefine same constant as Field
33+
// Unsigned/Signed -> Field: redefine same constant as Field
3434
SimplifiedTo(dfg.make_constant(constant, dst_typ.clone()))
3535
}
3636
(
@@ -48,6 +48,24 @@ pub(super) fn simplify_cast(
4848
let truncated = FieldElement::from_be_bytes_reduce(&truncated.to_bytes_be());
4949
SimplifiedTo(dfg.make_constant(truncated, dst_typ.clone()))
5050
}
51+
(
52+
Type::Numeric(
53+
NumericType::NativeField
54+
| NumericType::Unsigned { .. }
55+
| NumericType::Signed { .. },
56+
),
57+
Type::Numeric(NumericType::Signed { bit_size }),
58+
) => {
59+
// Field/Unsigned -> signed
60+
// We only simplify to signed when we are below the maximum signed integer of the destination type.
61+
let integer_modulus = BigUint::from(2u128).pow(*bit_size - 1);
62+
let constant_uint: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes());
63+
if constant_uint < integer_modulus {
64+
SimplifiedTo(dfg.make_constant(constant, dst_typ.clone()))
65+
} else {
66+
None
67+
}
68+
}
5169
_ => None,
5270
}
5371
} else if *dst_typ == dfg.type_of_value(value) {

noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs

+259-18
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,7 @@ impl Context {
106106

107107
let instructions_len = block.instructions().len();
108108

109-
// We can track IncrementRc instructions per block to determine whether they are useless.
110-
// IncrementRc and DecrementRc instructions are normally side effectual instructions, but we remove
111-
// them if their value is not used anywhere in the function. However, even when their value is used, their existence
112-
// is pointless logic if there is no array set between the increment and the decrement of the reference counter.
113-
// We track per block whether an IncrementRc instruction has a paired DecrementRc instruction
114-
// with the same value but no array set in between.
115-
// If we see an inc/dec RC pair within a block we can safely remove both instructions.
116-
let mut inc_rcs: HashMap<Type, Vec<RcInstruction>> = HashMap::default();
117-
let mut inc_rcs_to_remove = HashSet::default();
109+
let mut rc_tracker = RcTracker::default();
118110

119111
// Indexes of instructions that might be out of bounds.
120112
// We'll remove those, but before that we'll insert bounds checks for them.
@@ -143,17 +135,11 @@ impl Context {
143135
}
144136
}
145137

146-
self.track_inc_rcs_to_remove(
147-
*instruction_id,
148-
function,
149-
&mut inc_rcs,
150-
&mut inc_rcs_to_remove,
151-
);
138+
rc_tracker.track_inc_rcs_to_remove(*instruction_id, function);
152139
}
153140

154-
for id in inc_rcs_to_remove {
155-
self.instructions_to_remove.insert(id);
156-
}
141+
self.instructions_to_remove.extend(rc_tracker.get_non_mutated_arrays());
142+
self.instructions_to_remove.extend(rc_tracker.rc_pairs_to_remove);
157143

158144
// If there are some instructions that might trigger an out of bounds error,
159145
// first add constrain checks. Then run the DIE pass again, which will remove those
@@ -568,10 +554,109 @@ fn apply_side_effects(
568554
(lhs, rhs)
569555
}
570556

557+
#[derive(Default)]
558+
struct RcTracker {
559+
// We can track IncrementRc instructions per block to determine whether they are useless.
560+
// IncrementRc and DecrementRc instructions are normally side effectual instructions, but we remove
561+
// them if their value is not used anywhere in the function. However, even when their value is used, their existence
562+
// is pointless logic if there is no array set between the increment and the decrement of the reference counter.
563+
// We track per block whether an IncrementRc instruction has a paired DecrementRc instruction
564+
// with the same value but no array set in between.
565+
// If we see an inc/dec RC pair within a block we can safely remove both instructions.
566+
rcs_with_possible_pairs: HashMap<Type, Vec<RcInstruction>>,
567+
rc_pairs_to_remove: HashSet<InstructionId>,
568+
// We also separately track all IncrementRc instructions and all arrays which have been mutably borrowed.
569+
// If an array has not been mutably borrowed we can then safely remove all IncrementRc instructions on that array.
570+
inc_rcs: HashMap<ValueId, HashSet<InstructionId>>,
571+
mut_borrowed_arrays: HashSet<ValueId>,
572+
// The SSA often creates patterns where after simplifications we end up with repeat
573+
// IncrementRc instructions on the same value. We track whether the previous instruction was an IncrementRc,
574+
// and if the current instruction is also an IncrementRc on the same value we remove the current instruction.
575+
// `None` if the previous instruction was anything other than an IncrementRc
576+
previous_inc_rc: Option<ValueId>,
577+
}
578+
579+
impl RcTracker {
580+
fn track_inc_rcs_to_remove(&mut self, instruction_id: InstructionId, function: &Function) {
581+
let instruction = &function.dfg[instruction_id];
582+
583+
if let Instruction::IncrementRc { value } = instruction {
584+
if let Some(previous_value) = self.previous_inc_rc {
585+
if previous_value == *value {
586+
self.rc_pairs_to_remove.insert(instruction_id);
587+
}
588+
}
589+
self.previous_inc_rc = Some(*value);
590+
} else {
591+
self.previous_inc_rc = None;
592+
}
593+
594+
// DIE loops over a block in reverse order, so we insert an RC instruction for possible removal
595+
// when we see a DecrementRc and check whether it was possibly mutated when we see an IncrementRc.
596+
match instruction {
597+
Instruction::IncrementRc { value } => {
598+
if let Some(inc_rc) =
599+
pop_rc_for(*value, function, &mut self.rcs_with_possible_pairs)
600+
{
601+
if !inc_rc.possibly_mutated {
602+
self.rc_pairs_to_remove.insert(inc_rc.id);
603+
self.rc_pairs_to_remove.insert(instruction_id);
604+
}
605+
}
606+
607+
self.inc_rcs.entry(*value).or_default().insert(instruction_id);
608+
}
609+
Instruction::DecrementRc { value } => {
610+
let typ = function.dfg.type_of_value(*value);
611+
612+
// We assume arrays aren't mutated until we find an array_set
613+
let dec_rc =
614+
RcInstruction { id: instruction_id, array: *value, possibly_mutated: false };
615+
self.rcs_with_possible_pairs.entry(typ).or_default().push(dec_rc);
616+
}
617+
Instruction::ArraySet { array, .. } => {
618+
let typ = function.dfg.type_of_value(*array);
619+
if let Some(dec_rcs) = self.rcs_with_possible_pairs.get_mut(&typ) {
620+
for dec_rc in dec_rcs {
621+
dec_rc.possibly_mutated = true;
622+
}
623+
}
624+
625+
self.mut_borrowed_arrays.insert(*array);
626+
}
627+
Instruction::Store { value, .. } => {
628+
// We are very conservative and say that any store of an array value means it has the potential
629+
// to be mutated. This is done due to the tracking of mutable borrows still being per block.
630+
let typ = function.dfg.type_of_value(*value);
631+
if matches!(&typ, Type::Array(..) | Type::Slice(..)) {
632+
self.mut_borrowed_arrays.insert(*value);
633+
}
634+
}
635+
_ => {}
636+
}
637+
}
638+
639+
fn get_non_mutated_arrays(&self) -> HashSet<InstructionId> {
640+
self.inc_rcs
641+
.keys()
642+
.filter_map(|value| {
643+
if !self.mut_borrowed_arrays.contains(value) {
644+
Some(&self.inc_rcs[value])
645+
} else {
646+
None
647+
}
648+
})
649+
.flatten()
650+
.copied()
651+
.collect()
652+
}
653+
}
571654
#[cfg(test)]
572655
mod test {
573656
use std::sync::Arc;
574657

658+
use im::vector;
659+
575660
use crate::ssa::{
576661
function_builder::FunctionBuilder,
577662
ir::{
@@ -779,4 +864,160 @@ mod test {
779864

780865
assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3);
781866
}
867+
868+
#[test]
869+
fn keep_inc_rc_on_borrowed_array_store() {
870+
// acir(inline) fn main f0 {
871+
// b0():
872+
// v2 = allocate
873+
// inc_rc [u32 0, u32 0]
874+
// store [u32 0, u32 0] at v2
875+
// inc_rc [u32 0, u32 0]
876+
// jmp b1()
877+
// b1():
878+
// v3 = load v2
879+
// v5 = array_set v3, index u32 0, value u32 1
880+
// return v5
881+
// }
882+
let main_id = Id::test_new(0);
883+
884+
// Compiling main
885+
let mut builder = FunctionBuilder::new("main".into(), main_id);
886+
let zero = builder.numeric_constant(0u128, Type::unsigned(32));
887+
let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2);
888+
let array = builder.array_constant(vector![zero, zero], array_type.clone());
889+
let v2 = builder.insert_allocate(array_type.clone());
890+
builder.increment_array_reference_count(array);
891+
builder.insert_store(v2, array);
892+
builder.increment_array_reference_count(array);
893+
894+
let b1 = builder.insert_block();
895+
builder.terminate_with_jmp(b1, vec![]);
896+
builder.switch_to_block(b1);
897+
898+
let v3 = builder.insert_load(v2, array_type);
899+
let one = builder.numeric_constant(1u128, Type::unsigned(32));
900+
let v5 = builder.insert_array_set(v3, zero, one);
901+
builder.terminate_with_return(vec![v5]);
902+
903+
let ssa = builder.finish();
904+
let main = ssa.main();
905+
906+
// The instruction count never includes the terminator instruction
907+
assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4);
908+
assert_eq!(main.dfg[b1].instructions().len(), 2);
909+
910+
// We expect the output to be unchanged
911+
let ssa = ssa.dead_instruction_elimination();
912+
let main = ssa.main();
913+
914+
assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4);
915+
assert_eq!(main.dfg[b1].instructions().len(), 2);
916+
}
917+
918+
#[test]
919+
fn keep_inc_rc_on_borrowed_array_set() {
920+
// acir(inline) fn main f0 {
921+
// b0(v0: [u32; 2]):
922+
// inc_rc v0
923+
// v3 = array_set v0, index u32 0, value u32 1
924+
// inc_rc v0
925+
// inc_rc v0
926+
// inc_rc v0
927+
// v4 = array_get v3, index u32 1
928+
// return v4
929+
// }
930+
let main_id = Id::test_new(0);
931+
932+
// Compiling main
933+
let mut builder = FunctionBuilder::new("main".into(), main_id);
934+
let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2);
935+
let v0 = builder.add_parameter(array_type.clone());
936+
builder.increment_array_reference_count(v0);
937+
let zero = builder.numeric_constant(0u128, Type::unsigned(32));
938+
let one = builder.numeric_constant(1u128, Type::unsigned(32));
939+
let v3 = builder.insert_array_set(v0, zero, one);
940+
builder.increment_array_reference_count(v0);
941+
builder.increment_array_reference_count(v0);
942+
builder.increment_array_reference_count(v0);
943+
944+
let v4 = builder.insert_array_get(v3, one, Type::unsigned(32));
945+
946+
builder.terminate_with_return(vec![v4]);
947+
948+
let ssa = builder.finish();
949+
let main = ssa.main();
950+
951+
// The instruction count never includes the terminator instruction
952+
assert_eq!(main.dfg[main.entry_block()].instructions().len(), 6);
953+
954+
// We expect the output to be unchanged
955+
// Expected output:
956+
//
957+
// acir(inline) fn main f0 {
958+
// b0(v0: [u32; 2]):
959+
// inc_rc v0
960+
// v3 = array_set v0, index u32 0, value u32 1
961+
// inc_rc v0
962+
// v4 = array_get v3, index u32 1
963+
// return v4
964+
// }
965+
let ssa = ssa.dead_instruction_elimination();
966+
let main = ssa.main();
967+
968+
let instructions = main.dfg[main.entry_block()].instructions();
969+
// We expect only the repeated inc_rc instructions to be collapsed into a single inc_rc.
970+
assert_eq!(instructions.len(), 4);
971+
972+
assert!(matches!(&main.dfg[instructions[0]], Instruction::IncrementRc { .. }));
973+
assert!(matches!(&main.dfg[instructions[1]], Instruction::ArraySet { .. }));
974+
assert!(matches!(&main.dfg[instructions[2]], Instruction::IncrementRc { .. }));
975+
assert!(matches!(&main.dfg[instructions[3]], Instruction::ArrayGet { .. }));
976+
}
977+
978+
#[test]
979+
fn remove_inc_rcs_that_are_never_mutably_borrowed() {
980+
// acir(inline) fn main f0 {
981+
// b0(v0: [Field; 2]):
982+
// inc_rc v0
983+
// inc_rc v0
984+
// inc_rc v0
985+
// v2 = array_get v0, index u32 0
986+
// inc_rc v0
987+
// return v2
988+
// }
989+
let main_id = Id::test_new(0);
990+
991+
// Compiling main
992+
let mut builder = FunctionBuilder::new("main".into(), main_id);
993+
let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2));
994+
builder.increment_array_reference_count(v0);
995+
builder.increment_array_reference_count(v0);
996+
builder.increment_array_reference_count(v0);
997+
998+
let zero = builder.numeric_constant(0u128, Type::unsigned(32));
999+
let v2 = builder.insert_array_get(v0, zero, Type::field());
1000+
builder.increment_array_reference_count(v0);
1001+
builder.terminate_with_return(vec![v2]);
1002+
1003+
let ssa = builder.finish();
1004+
let main = ssa.main();
1005+
1006+
// The instruction count never includes the terminator instruction
1007+
assert_eq!(main.dfg[main.entry_block()].instructions().len(), 5);
1008+
1009+
// Expected output:
1010+
//
1011+
// acir(inline) fn main f0 {
1012+
// b0(v0: [Field; 2]):
1013+
// v2 = array_get v0, index u32 0
1014+
// return v2
1015+
// }
1016+
let ssa = ssa.dead_instruction_elimination();
1017+
let main = ssa.main();
1018+
1019+
let instructions = main.dfg[main.entry_block()].instructions();
1020+
assert_eq!(instructions.len(), 1);
1021+
assert!(matches!(&main.dfg[instructions[0]], Instruction::ArrayGet { .. }));
1022+
}
7821023
}

noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ pub trait Recoverable {
294294

295295
#[derive(Debug, PartialEq, Eq, Clone)]
296296
pub struct ModuleDeclaration {
297+
pub visibility: ItemVisibility,
297298
pub ident: Ident,
298299
pub outer_attributes: Vec<SecondaryAttribute>,
299300
}

0 commit comments

Comments
 (0)