131
131
//! v11 = mul v4, Field 12
132
132
//! v12 = add v10, v11
133
133
//! store v12 at v5 (new store)
134
- use fxhash:: FxHashMap as HashMap ;
134
+ use fxhash:: { FxHashMap as HashMap , FxHashSet as HashSet } ;
135
135
136
136
use acvm:: { acir:: AcirField , acir:: BlackBoxFunc , FieldElement } ;
137
137
use iter_extended:: vecmap;
@@ -201,6 +201,15 @@ struct Context<'f> {
201
201
/// When processing a block, we pop this stack to get its arguments
202
202
/// and at the end we push the arguments for his successor
203
203
arguments_stack : Vec < Vec < ValueId > > ,
204
+
205
+ /// Stores all allocations local to the current branch.
206
+ ///
207
+ /// Since these branches are local to the current branch (i.e. only defined within one branch of
208
+ /// an if expression), they should not be merged with their previous value or stored value in
209
+ /// the other branch since there is no such value.
210
+ ///
211
+ /// The `ValueId` here is that which is returned by the allocate instruction.
212
+ local_allocations : HashSet < ValueId > ,
204
213
}
205
214
206
215
#[ derive( Clone ) ]
@@ -211,6 +220,8 @@ struct ConditionalBranch {
211
220
old_condition : ValueId ,
212
221
// The condition of the branch
213
222
condition : ValueId ,
223
+ // The allocations accumulated when processing the branch
224
+ local_allocations : HashSet < ValueId > ,
214
225
}
215
226
216
227
struct ConditionalContext {
@@ -243,6 +254,7 @@ fn flatten_function_cfg(function: &mut Function, no_predicates: &HashMap<Functio
243
254
slice_sizes : HashMap :: default ( ) ,
244
255
condition_stack : Vec :: new ( ) ,
245
256
arguments_stack : Vec :: new ( ) ,
257
+ local_allocations : HashSet :: default ( ) ,
246
258
} ;
247
259
context. flatten ( no_predicates) ;
248
260
}
@@ -317,7 +329,6 @@ impl<'f> Context<'f> {
317
329
// If this is not a separate variable, clippy gets confused and says the to_vec is
318
330
// unnecessary, when removing it actually causes an aliasing/mutability error.
319
331
let instructions = self . inserter . function . dfg [ block] . instructions ( ) . to_vec ( ) ;
320
- let mut previous_allocate_result = None ;
321
332
322
333
for instruction in instructions. iter ( ) {
323
334
if self . is_no_predicate ( no_predicates, instruction) {
@@ -405,10 +416,12 @@ impl<'f> Context<'f> {
405
416
let old_condition = * condition;
406
417
let then_condition = self . inserter . resolve ( old_condition) ;
407
418
419
+ let old_allocations = std:: mem:: take ( & mut self . local_allocations ) ;
408
420
let branch = ConditionalBranch {
409
421
old_condition,
410
422
condition : self . link_condition ( then_condition) ,
411
423
last_block : * then_destination,
424
+ local_allocations : old_allocations,
412
425
} ;
413
426
let cond_context = ConditionalContext {
414
427
condition : then_condition,
@@ -435,10 +448,12 @@ impl<'f> Context<'f> {
435
448
) ;
436
449
let else_condition = self . link_condition ( else_condition) ;
437
450
451
+ let old_allocations = std:: mem:: take ( & mut self . local_allocations ) ;
438
452
let else_branch = ConditionalBranch {
439
453
old_condition : cond_context. then_branch . old_condition ,
440
454
condition : else_condition,
441
455
last_block : * block,
456
+ local_allocations : old_allocations,
442
457
} ;
443
458
cond_context. else_branch = Some ( else_branch) ;
444
459
self . condition_stack . push ( cond_context) ;
@@ -461,6 +476,7 @@ impl<'f> Context<'f> {
461
476
}
462
477
463
478
let mut else_branch = cond_context. else_branch . unwrap ( ) ;
479
+ self . local_allocations = std:: mem:: take ( & mut else_branch. local_allocations ) ;
464
480
else_branch. last_block = * block;
465
481
cond_context. else_branch = Some ( else_branch) ;
466
482
@@ -593,22 +609,19 @@ impl<'f> Context<'f> {
593
609
/// `previous_allocate_result` should only be set to the result of an allocate instruction
594
610
/// if that instruction was the instruction immediately previous to this one - if there are
595
611
/// any instructions in between it should be None.
596
- fn push_instruction (
597
- & mut self ,
598
- id : InstructionId ,
599
- previous_allocate_result : & mut Option < ValueId > ,
600
- ) {
612
+ fn push_instruction ( & mut self , id : InstructionId ) {
601
613
let ( instruction, call_stack) = self . inserter . map_instruction ( id) ;
602
- let instruction = self . handle_instruction_side_effects (
603
- instruction,
604
- call_stack. clone ( ) ,
605
- * previous_allocate_result,
606
- ) ;
614
+ let instruction = self . handle_instruction_side_effects ( instruction, call_stack. clone ( ) ) ;
607
615
608
616
let instruction_is_allocate = matches ! ( & instruction, Instruction :: Allocate ) ;
609
617
let entry = self . inserter . function . entry_block ( ) ;
610
618
let results = self . inserter . push_instruction_value ( instruction, id, entry, call_stack) ;
611
- * previous_allocate_result = instruction_is_allocate. then ( || results. first ( ) ) ;
619
+
620
+ // Remember an allocate was created local to this branch so that we do not try to merge store
621
+ // values across branches for it later.
622
+ if instruction_is_allocate {
623
+ self . local_allocations . insert ( results. first ( ) ) ;
624
+ }
612
625
}
613
626
614
627
/// If we are currently in a branch, we need to modify constrain instructions
@@ -652,7 +665,7 @@ impl<'f> Context<'f> {
652
665
Instruction :: Store { address, value } => {
653
666
// If this instruction immediately follows an allocate, and stores to that
654
667
// address there is no previous value to load and we don't need a merge anyway.
655
- if Some ( address) == previous_allocate_result {
668
+ if self . local_allocations . contains ( & address) {
656
669
Instruction :: Store { address, value }
657
670
} else {
658
671
// Instead of storing `value`, store `if condition { value } else { previous_value }`
0 commit comments