From ce4641bdd57b41020269f57aa1c5253211d04c90 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 17:16:44 +0000 Subject: [PATCH 01/11] remove inc_rc instructions for arrays which are never borrowed --- .../noirc_evaluator/src/ssa/ir/printer.rs | 2 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 45 ++++++++++++++----- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 2b564c14aa7..eed7e0edb19 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -72,7 +72,7 @@ fn value(function: &Function, id: ValueId) -> String { Value::Intrinsic(intrinsic) => intrinsic.to_string(), Value::Array { array, .. } => { let elements = vecmap(array, |element| value(function, *element)); - format!("[{}]", elements.join(", ")) + format!("{id}=[{}]", elements.join(", ")) } Value::Param { .. } | Value::Instruction { .. } | Value::ForeignFunction(_) => { id.to_string() diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 02737f5645b..aa5410172fc 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -113,8 +113,11 @@ impl Context { // We track per block whether an IncrementRc instruction has a paired DecrementRc instruction // with the same value but no array set in between. // If we see an inc/dec RC pair within a block we can safely remove both instructions. - let mut inc_rcs: HashMap> = HashMap::default(); - let mut inc_rcs_to_remove = HashSet::default(); + let mut rcs_with_possible_pairs: HashMap> = HashMap::default(); + let mut rc_pairs_to_remove = HashSet::default(); + + let mut inc_rcs: HashMap> = HashMap::default(); + let mut borrowed_arrays: HashSet = HashSet::default(); // Indexes of instructions that might be out of bounds. // We'll remove those, but before that we'll insert bounds checks for them. @@ -146,15 +149,21 @@ impl Context { self.track_inc_rcs_to_remove( *instruction_id, function, + &mut rcs_with_possible_pairs, + &mut rc_pairs_to_remove, &mut inc_rcs, - &mut inc_rcs_to_remove, + &mut borrowed_arrays, ); } - for id in inc_rcs_to_remove { - self.instructions_to_remove.insert(id); + for inc_rc_value in inc_rcs.keys() { + if !borrowed_arrays.contains(inc_rc_value) { + self.instructions_to_remove.extend(&inc_rcs[inc_rc_value]); + } } + self.instructions_to_remove.extend(rc_pairs_to_remove); + // If there are some instructions that might trigger an out of bounds error, // first add constrain checks. Then run the DIE pass again, which will remove those // but leave the constrains (any any value needed by those constrains) @@ -181,36 +190,48 @@ impl Context { &self, instruction_id: InstructionId, function: &Function, - inc_rcs: &mut HashMap>, + rcs_with_possible_pairs: &mut HashMap>, inc_rcs_to_remove: &mut HashSet, + not_borrowed_rcs: &mut HashMap>, + borrowed_arrays: &mut HashSet, ) { let instruction = &function.dfg[instruction_id]; // DIE loops over a block in reverse order, so we insert an RC instruction for possible removal // when we see a DecrementRc and check whether it was possibly mutated when we see an IncrementRc. match instruction { Instruction::IncrementRc { value } => { - if let Some(inc_rc) = pop_rc_for(*value, function, inc_rcs) { + if let Some(inc_rc) = pop_rc_for(*value, function, rcs_with_possible_pairs) { if !inc_rc.possibly_mutated { inc_rcs_to_remove.insert(inc_rc.id); inc_rcs_to_remove.insert(instruction_id); } } + + not_borrowed_rcs.entry(*value).or_default().insert(instruction_id); } Instruction::DecrementRc { value } => { let typ = function.dfg.type_of_value(*value); // We assume arrays aren't mutated until we find an array_set - let inc_rc = + let dec_rc = RcInstruction { id: instruction_id, array: *value, possibly_mutated: false }; - inc_rcs.entry(typ).or_default().push(inc_rc); + rcs_with_possible_pairs.entry(typ).or_default().push(dec_rc); } Instruction::ArraySet { array, .. } => { let typ = function.dfg.type_of_value(*array); - if let Some(inc_rcs) = inc_rcs.get_mut(&typ) { - for inc_rc in inc_rcs { - inc_rc.possibly_mutated = true; + if let Some(dec_rcs) = rcs_with_possible_pairs.get_mut(&typ) { + for dec_rc in dec_rcs { + dec_rc.possibly_mutated = true; } } + + borrowed_arrays.insert(*array); + } + Instruction::Store { value, .. } => { + let typ = function.dfg.type_of_value(*value); + if matches!(&typ, Type::Array(..) | Type::Slice(..)) { + borrowed_arrays.insert(*value); + } } _ => {} } From 74d29f04972f67598dfabb184cc4e747575fc67a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 17:23:15 +0000 Subject: [PATCH 02/11] remove extra printer id --- compiler/noirc_evaluator/src/ssa/ir/printer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index eed7e0edb19..2b564c14aa7 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -72,7 +72,7 @@ fn value(function: &Function, id: ValueId) -> String { Value::Intrinsic(intrinsic) => intrinsic.to_string(), Value::Array { array, .. } => { let elements = vecmap(array, |element| value(function, *element)); - format!("{id}=[{}]", elements.join(", ")) + format!("[{}]", elements.join(", ")) } Value::Param { .. } | Value::Instruction { .. } | Value::ForeignFunction(_) => { id.to_string() From 46d31f34e358afb8d32d37194d93b0784b133093 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 18:41:46 +0000 Subject: [PATCH 03/11] add unit tests --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 140 +++++++++++++++++++- 1 file changed, 137 insertions(+), 3 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index aa5410172fc..e9c346eaaff 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -115,7 +115,9 @@ impl Context { // If we see an inc/dec RC pair within a block we can safely remove both instructions. let mut rcs_with_possible_pairs: HashMap> = HashMap::default(); let mut rc_pairs_to_remove = HashSet::default(); - + // We also separately track all IncrementRc instructions and all arrays which have been mutably borrowed. + // This is done to determine whether the array has ever been borrowed. + // If an array has not been mutably borrowed we can then safely remove all IncrementRc instructions on that array. let mut inc_rcs: HashMap> = HashMap::default(); let mut borrowed_arrays: HashSet = HashSet::default(); @@ -192,7 +194,7 @@ impl Context { function: &Function, rcs_with_possible_pairs: &mut HashMap>, inc_rcs_to_remove: &mut HashSet, - not_borrowed_rcs: &mut HashMap>, + inc_rcs: &mut HashMap>, borrowed_arrays: &mut HashSet, ) { let instruction = &function.dfg[instruction_id]; @@ -207,7 +209,7 @@ impl Context { } } - not_borrowed_rcs.entry(*value).or_default().insert(instruction_id); + inc_rcs.entry(*value).or_default().insert(instruction_id); } Instruction::DecrementRc { value } => { let typ = function.dfg.type_of_value(*value); @@ -228,6 +230,8 @@ impl Context { borrowed_arrays.insert(*array); } Instruction::Store { value, .. } => { + // We are very conservative and say that any store of an array value means it has the potential + // to be mutated. This is due to the tracking of mutable borrows still being per block. let typ = function.dfg.type_of_value(*value); if matches!(&typ, Type::Array(..) | Type::Slice(..)) { borrowed_arrays.insert(*value); @@ -593,6 +597,8 @@ fn apply_side_effects( mod test { use std::sync::Arc; + use im::vector; + use crate::ssa::{ function_builder::FunctionBuilder, ir::{ @@ -800,4 +806,132 @@ mod test { assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); } + + #[test] + fn keep_inc_rc_on_borrowed_array_store() { + // acir(inline) fn main f0 { + // b0(): + // inc_rc [u32 0, u32 0] + // v2 = allocate + // store [u32 0, u32 0] at v2 + // jmp b1() + // b1(): + // v3 = load v2 + // v5 = array_set v3, index u32 0, value u32 1 + // return v5 + // } + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + let zero = builder.numeric_constant(0u128, Type::unsigned(32)); + let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2); + let array = builder.array_constant(vector![zero, zero], array_type.clone()); + builder.increment_array_reference_count(array); + let v2 = builder.insert_allocate(array_type.clone()); + builder.insert_store(v2, array); + + let b1 = builder.insert_block(); + builder.terminate_with_jmp(b1, vec![]); + builder.switch_to_block(b1); + + let v3 = builder.insert_load(v2, array_type); + let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let v5 = builder.insert_array_set(v3, zero, one); + builder.terminate_with_return(vec![v5]); + + let ssa = builder.finish(); + let main = ssa.main(); + + // The instruction count never includes the terminator instruction + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + assert_eq!(main.dfg[b1].instructions().len(), 2); + + // We expect the output to be unchanged + let ssa = ssa.dead_instruction_elimination(); + let main = ssa.main(); + + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + assert_eq!(main.dfg[b1].instructions().len(), 2); + } + + #[test] + fn keep_inc_rc_on_borrowed_array_set() { + // acir(inline) fn main f0 { + // b0(v0: [u32; 2]): + // inc_rc v0 + // v3 = array_set v0, index u32 0, value u32 1 + // return v3 + // } + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2); + let v0 = builder.add_parameter(array_type.clone()); + builder.increment_array_reference_count(v0); + let zero = builder.numeric_constant(0u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let v3 = builder.insert_array_set(v0, zero, one); + builder.terminate_with_return(vec![v3]); + + let ssa = builder.finish(); + let main = ssa.main(); + + // The instruction count never includes the terminator instruction + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 2); + + // We expect the output to be unchanged + let ssa = ssa.dead_instruction_elimination(); + let main = ssa.main(); + + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 2); + } + + #[test] + fn remove_inc_rcs_that_are_never_mutably_borrowed() { + // acir(inline) fn main f0 { + // b0(v0: [Field; 2]): + // inc_rc v0 + // inc_rc v0 + // inc_rc v0 + // v2 = array_get v0, index u32 0 + // return v2 + // } + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + let v0 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); + builder.increment_array_reference_count(v0); + builder.increment_array_reference_count(v0); + builder.increment_array_reference_count(v0); + + let zero = builder.numeric_constant(0u128, Type::unsigned(32)); + let v1 = builder.insert_array_get(v0, zero, Type::field()); + // builder.decrement_array_reference_count(v0); + builder.terminate_with_return(vec![v1]); + + let ssa = builder.finish(); + println!("{}", ssa); + let main = ssa.main(); + + // The instruction count never includes the terminator instruction + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4); + + // Expected output: + // + // acir(inline) fn main f0 { + // b0(v0: [Field; 2]): + // v2 = array_get v0, index u32 0 + // return v2 + // } + let ssa = ssa.dead_instruction_elimination(); + println!("{}", ssa); + let main = ssa.main(); + + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 1); + assert!(matches!(&main.dfg[instructions[0]], Instruction::ArrayGet { .. })); + } } From 831c6f5e6c6487aad7b1e921c99101e1f6012d81 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 18:42:30 +0000 Subject: [PATCH 04/11] improve sentence --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index e9c346eaaff..b4f0061457e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -231,7 +231,7 @@ impl Context { } Instruction::Store { value, .. } => { // We are very conservative and say that any store of an array value means it has the potential - // to be mutated. This is due to the tracking of mutable borrows still being per block. + // to be mutated. This is done due to the tracking of mutable borrows still being per block. let typ = function.dfg.type_of_value(*value); if matches!(&typ, Type::Array(..) | Type::Slice(..)) { borrowed_arrays.insert(*value); From d5d051a7b21c987171f554a9b87c01a736053814 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 14:51:14 -0400 Subject: [PATCH 05/11] Update compiler/noirc_evaluator/src/ssa/opt/die.rs --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index b4f0061457e..0a45a310bfd 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -909,7 +909,6 @@ mod test { let zero = builder.numeric_constant(0u128, Type::unsigned(32)); let v1 = builder.insert_array_get(v0, zero, Type::field()); - // builder.decrement_array_reference_count(v0); builder.terminate_with_return(vec![v1]); let ssa = builder.finish(); From b3583f313344182a4a292b4b2ca90aafa35b6246 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 14:51:25 -0400 Subject: [PATCH 06/11] Update compiler/noirc_evaluator/src/ssa/opt/die.rs --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 0a45a310bfd..01dbdb9bfe0 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -926,7 +926,6 @@ mod test { // return v2 // } let ssa = ssa.dead_instruction_elimination(); - println!("{}", ssa); let main = ssa.main(); let instructions = main.dfg[main.entry_block()].instructions(); From ca63575d567d2e1ee9eb3238642efcb40a5fd8c3 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 14:51:34 -0400 Subject: [PATCH 07/11] Update compiler/noirc_evaluator/src/ssa/opt/die.rs --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 01dbdb9bfe0..1e408cab46b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -912,7 +912,6 @@ mod test { builder.terminate_with_return(vec![v1]); let ssa = builder.finish(); - println!("{}", ssa); let main = ssa.main(); // The instruction count never includes the terminator instruction From b6d236f4b9622b6b595c69161a2ded93bd2feb23 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 27 Sep 2024 14:52:32 -0400 Subject: [PATCH 08/11] Update compiler/noirc_evaluator/src/ssa/opt/die.rs --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 1e408cab46b..d59b2a82420 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -116,7 +116,6 @@ impl Context { let mut rcs_with_possible_pairs: HashMap> = HashMap::default(); let mut rc_pairs_to_remove = HashSet::default(); // We also separately track all IncrementRc instructions and all arrays which have been mutably borrowed. - // This is done to determine whether the array has ever been borrowed. // If an array has not been mutably borrowed we can then safely remove all IncrementRc instructions on that array. let mut inc_rcs: HashMap> = HashMap::default(); let mut borrowed_arrays: HashSet = HashSet::default(); From a8480989524212943f1af147130dc2c079cdab0b Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 30 Sep 2024 12:46:40 -0400 Subject: [PATCH 09/11] Update compiler/noirc_evaluator/src/ssa/opt/die.rs Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index d59b2a82420..26921a3630b 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -157,11 +157,8 @@ impl Context { ); } - for inc_rc_value in inc_rcs.keys() { - if !borrowed_arrays.contains(inc_rc_value) { - self.instructions_to_remove.extend(&inc_rcs[inc_rc_value]); - } - } + let unborrowed_arrays = inc_rcs.keys().filter(|id| !borrowed_arrays.contains(id)); + self.instructions_to_remove.extend(unborrowed_arrays); self.instructions_to_remove.extend(rc_pairs_to_remove); From bbf9b70caa42cb123ac3447e98621df857a4a814 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 30 Sep 2024 20:02:55 +0000 Subject: [PATCH 10/11] update keep_inc_rc_on_borrowed_array_set to also include inc_rc instructions after the array set --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 34 +++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 26921a3630b..3bfebf7ce84 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -157,9 +157,21 @@ impl Context { ); } - let unborrowed_arrays = inc_rcs.keys().filter(|id| !borrowed_arrays.contains(id)); - self.instructions_to_remove.extend(unborrowed_arrays); + let non_mutated_arrays = + inc_rcs + .keys() + .filter_map(|value| { + if !borrowed_arrays.contains(value) { + Some(&inc_rcs[value]) + } else { + None + } + }) + .flatten() + .copied() + .collect::>(); + self.instructions_to_remove.extend(non_mutated_arrays); self.instructions_to_remove.extend(rc_pairs_to_remove); // If there are some instructions that might trigger an out of bounds error, @@ -857,7 +869,11 @@ mod test { // b0(v0: [u32; 2]): // inc_rc v0 // v3 = array_set v0, index u32 0, value u32 1 - // return v3 + // inc_rc v0 + // inc_rc v0 + // inc_rc v0 + // v4 = array_get v3, index u32 1 + // return v4 // } let main_id = Id::test_new(0); @@ -869,19 +885,25 @@ mod test { let zero = builder.numeric_constant(0u128, Type::unsigned(32)); let one = builder.numeric_constant(1u128, Type::unsigned(32)); let v3 = builder.insert_array_set(v0, zero, one); - builder.terminate_with_return(vec![v3]); + builder.increment_array_reference_count(v0); + builder.increment_array_reference_count(v0); + builder.increment_array_reference_count(v0); + + let v4 = builder.insert_array_get(v3, one, Type::unsigned(32)); + + builder.terminate_with_return(vec![v4]); let ssa = builder.finish(); let main = ssa.main(); // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 2); + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 6); // We expect the output to be unchanged let ssa = ssa.dead_instruction_elimination(); let main = ssa.main(); - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 2); + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 6); } #[test] From 53bae09eb0385d86de3982adfb69e0d653bbd22c Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 30 Sep 2024 20:06:46 +0000 Subject: [PATCH 11/11] update other tests to have inc_rc after the mut instruction --- compiler/noirc_evaluator/src/ssa/opt/die.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 3bfebf7ce84..ae55f85d897 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -819,9 +819,10 @@ mod test { fn keep_inc_rc_on_borrowed_array_store() { // acir(inline) fn main f0 { // b0(): - // inc_rc [u32 0, u32 0] // v2 = allocate + // inc_rc [u32 0, u32 0] // store [u32 0, u32 0] at v2 + // inc_rc [u32 0, u32 0] // jmp b1() // b1(): // v3 = load v2 @@ -835,9 +836,10 @@ mod test { let zero = builder.numeric_constant(0u128, Type::unsigned(32)); let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2); let array = builder.array_constant(vector![zero, zero], array_type.clone()); - builder.increment_array_reference_count(array); let v2 = builder.insert_allocate(array_type.clone()); + builder.increment_array_reference_count(array); builder.insert_store(v2, array); + builder.increment_array_reference_count(array); let b1 = builder.insert_block(); builder.terminate_with_jmp(b1, vec![]); @@ -852,14 +854,14 @@ mod test { let main = ssa.main(); // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4); assert_eq!(main.dfg[b1].instructions().len(), 2); // We expect the output to be unchanged let ssa = ssa.dead_instruction_elimination(); let main = ssa.main(); - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 3); + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4); assert_eq!(main.dfg[b1].instructions().len(), 2); } @@ -914,6 +916,7 @@ mod test { // inc_rc v0 // inc_rc v0 // v2 = array_get v0, index u32 0 + // inc_rc v0 // return v2 // } let main_id = Id::test_new(0); @@ -926,14 +929,15 @@ mod test { builder.increment_array_reference_count(v0); let zero = builder.numeric_constant(0u128, Type::unsigned(32)); - let v1 = builder.insert_array_get(v0, zero, Type::field()); - builder.terminate_with_return(vec![v1]); + let v2 = builder.insert_array_get(v0, zero, Type::field()); + builder.increment_array_reference_count(v0); + builder.terminate_with_return(vec![v2]); let ssa = builder.finish(); let main = ssa.main(); // The instruction count never includes the terminator instruction - assert_eq!(main.dfg[main.entry_block()].instructions().len(), 4); + assert_eq!(main.dfg[main.entry_block()].instructions().len(), 5); // Expected output: //