diff --git a/crates/noirc_evaluator/src/ssa/acir_gen.rs b/crates/noirc_evaluator/src/ssa/acir_gen.rs index 6ac54ef7afa..90deaed8354 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen.rs @@ -68,8 +68,8 @@ impl Acir { Operation::Load { array_id, index } => { load::evaluate(*array_id, *index, acir_mem, var_cache, evaluator, ctx) } - Operation::Store { array_id, index, value } => { - store::evaluate(*array_id, *index, *value, acir_mem, var_cache, evaluator, ctx) + Operation::Store { .. } => { + store::evaluate(&ins.operation, acir_mem, var_cache, evaluator, ctx) } Operation::Nop => None, i @ Operation::Jne(..) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/condition.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/condition.rs index ed117a870fd..55230e7b1b5 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/condition.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/condition.rs @@ -6,7 +6,7 @@ use crate::{ }, Evaluator, }; -use acvm::FieldElement; +use acvm::{acir::native_types::Expression, FieldElement}; pub(crate) fn evaluate( condition: NodeId, @@ -19,11 +19,21 @@ pub(crate) fn evaluate( let cond = var_cache.get_or_compute_internal_var_unwrap(condition, evaluator, ctx); let l_c = var_cache.get_or_compute_internal_var_unwrap(lhs, evaluator, ctx); let r_c = var_cache.get_or_compute_internal_var_unwrap(rhs, evaluator, ctx); - let sub = constraints::subtract(l_c.expression(), FieldElement::one(), r_c.expression()); - let result = constraints::add( - &constraints::mul_with_witness(evaluator, cond.expression(), &sub), - FieldElement::one(), - r_c.expression(), - ); + let result = + evaluate_expression(cond.expression(), l_c.expression(), r_c.expression(), evaluator); Some(result.into()) } + +pub fn evaluate_expression( + condition: &Expression, + lhs: &Expression, + rhs: &Expression, + evaluator: &mut Evaluator, +) -> Expression { + let sub = constraints::subtract(lhs, FieldElement::one(), rhs); + constraints::add( + &constraints::mul_with_witness(evaluator, condition, &sub), + FieldElement::one(), + rhs, + ) +} diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/store.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/store.rs index ead9429f642..250a8a64bed 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/store.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/store.rs @@ -2,32 +2,58 @@ use crate::{ ssa::{ acir_gen::{acir_mem::AcirMem, internal_var_cache::InternalVarCache, InternalVar}, context::SsaContext, - mem::{self, ArrayId}, - node::NodeId, + mem::{self}, + node::Operation, }, Evaluator, }; +use super::condition; + pub(crate) fn evaluate( - array_id: ArrayId, - index: NodeId, - value: NodeId, + store: &Operation, acir_mem: &mut AcirMem, var_cache: &mut InternalVarCache, evaluator: &mut Evaluator, ctx: &SsaContext, ) -> Option { - //maps the address to the rhs if address is known at compile time - let index = var_cache.get_or_compute_internal_var_unwrap(index, evaluator, ctx); - let value = var_cache.get_or_compute_internal_var_unwrap(value, evaluator, ctx); + if let Operation::Store { array_id, index, value, predicate } = *store { + //maps the address to the rhs if address is known at compile time + let index = var_cache.get_or_compute_internal_var_unwrap(index, evaluator, ctx); + let value = var_cache.get_or_compute_internal_var_unwrap(value, evaluator, ctx); - match index.to_const() { - Some(index) => { - let idx = mem::Memory::as_u32(index); - acir_mem.insert(array_id, idx, value); - //we do not generate constraint, so no output. - None + match index.to_const() { + Some(index) => { + let idx = mem::Memory::as_u32(index); + let value_with_predicate = if let Some(predicate) = predicate { + if predicate.is_dummy() || ctx.is_one(predicate) { + value + } else if ctx.is_zero(predicate) { + return None; + } else { + let pred = + var_cache.get_or_compute_internal_var_unwrap(predicate, evaluator, ctx); + let dummy_load = acir_mem + .load_array_element_constant_index(&ctx.mem[array_id], idx) + .unwrap(); + let result = condition::evaluate_expression( + pred.expression(), + value.expression(), + dummy_load.expression(), + evaluator, + ); + result.into() + } + } else { + value + }; + acir_mem.insert(array_id, idx, value_with_predicate); + //we do not generate constraint, so no output. + None + } + None => todo!("dynamic arrays are not implemented yet"), } - None => todo!("dynamic arrays are not implemented yet"), + } else { + unreachable!("Expected store, got {:?}", store.opcode()); } } diff --git a/crates/noirc_evaluator/src/ssa/anchor.rs b/crates/noirc_evaluator/src/ssa/anchor.rs index 3de6348fd1d..725f35f26fa 100644 --- a/crates/noirc_evaluator/src/ssa/anchor.rs +++ b/crates/noirc_evaluator/src/ssa/anchor.rs @@ -1,5 +1,6 @@ use crate::errors::{RuntimeError, RuntimeErrorKind}; use crate::ssa::{ + conditional::DecisionTree, context::SsaContext, mem::ArrayId, node::{NodeId, ObjectType, Opcode, Operation}, @@ -250,9 +251,13 @@ impl Anchor { return Some(CseAction::ReplaceWith(a)); } } - Operation::Store { index, value, .. } => { + Operation::Store { index, value, predicate, .. } => { if !ctx.maybe_distinct(*index, b_idx) { - return Some(CseAction::ReplaceWith(*value)); + if ctx.is_one(DecisionTree::unwrap_predicate(ctx, predicate)) { + return Some(CseAction::ReplaceWith(*value)); + } else { + return Some(CseAction::Keep); + } } if ctx.maybe_equal(*index, b_idx) { return Some(CseAction::Keep); diff --git a/crates/noirc_evaluator/src/ssa/conditional.rs b/crates/noirc_evaluator/src/ssa/conditional.rs index 0befa5a4928..ef9490f2622 100644 --- a/crates/noirc_evaluator/src/ssa/conditional.rs +++ b/crates/noirc_evaluator/src/ssa/conditional.rs @@ -374,11 +374,15 @@ impl DecisionTree { merged_ins = self.synchronize(ctx, &left_ins, &right_ins, left); } let mut modified = false; + // write the merged instructions to the block super::optimizations::cse_block(ctx, left, &mut merged_ins, &mut modified)?; if modified { // A second round is necessary when the synchronization optimizes function calls between the two branches. // In that case, the first cse updates the result instructions to the same call and then // the second cse can (and must) then simplify identical result instructions. + // We clear the list because we want to perform the cse on the block instructions, if we don't it will use the list instead. + // We should refactor cse_block so that its behavior is consistent and does not rely on the list being empty. + merged_ins.clear(); super::optimizations::cse_block(ctx, left, &mut merged_ins, &mut modified)?; } //housekeeping... @@ -605,7 +609,7 @@ impl DecisionTree { _ => (), } } - Operation::Store { array_id, index, value } => { + Operation::Store { array_id, index, value, predicate } => { if !ins.operation.is_dummy_store() { if let Some(idx) = ctx.get_as_constant(*index) { if (idx.to_u128() as u32) >= ctx.mem[*array_id].len { @@ -619,32 +623,14 @@ impl DecisionTree { } } if !stack.is_new_array(ctx, array_id) && ctx.under_assumption(ass_value) { - let load = Operation::Load { array_id: *array_id, index: *index }; - let e_type = ctx.mem[*array_id].element_type; - let dummy = ctx.add_instruction(Instruction::new( - load, - e_type, - Some(stack.block), - )); - let operation = Operation::Cond { - condition: ass_value, - val_true: *value, - val_false: dummy, - }; - let cond = ctx.add_instruction(Instruction::new( - operation, - e_type, - Some(stack.block), - )); - - stack.push(dummy); - stack.push(cond); - //store the conditional value + let pred = + Self::and_conditions(Some(ass_value), *predicate, stack, ctx); let ins2 = ctx.instruction_mut(ins_id); ins2.operation = Operation::Store { array_id: *array_id, index: *index, - value: cond, + value: *value, + predicate: pred, }; } } @@ -739,6 +725,82 @@ impl DecisionTree { } } + // returns condition1 AND condition2 + fn and_conditions( + condition1: Option, + condition2: Option, + stack_frame: &mut StackFrame, + ctx: &mut SsaContext, + ) -> Option { + match (condition1, condition2) { + (None, None) => None, + (Some(cond), other) | (other, Some(cond)) if cond.is_dummy() => { + Self::and_conditions(None, other, stack_frame, ctx) + } + (Some(cond), None) | (None, Some(cond)) => Some(cond), + (Some(cond1), Some(cond2)) if cond1 == cond2 => condition1, + (Some(cond1), Some(cond2)) => { + let op = Operation::Binary(node::Binary { + lhs: cond1, + rhs: cond2, + operator: BinaryOp::Mul, + predicate: None, + }); + let cond = ctx.add_instruction(Instruction::new( + op, + ObjectType::Boolean, + Some(stack_frame.block), + )); + optimizations::simplify_id(ctx, cond).unwrap(); + stack_frame.push(cond); + Some(cond) + } + } + } + + // returns condition1 OR condition2 + fn or_conditions( + condition1: Option, + condition2: Option, + stack_frame: &mut StackFrame, + ctx: &mut SsaContext, + ) -> Option { + match (condition1, condition2) { + (_condition, None) | (None, _condition) => None, + (Some(cond1), Some(cond2)) => { + if cond1.is_dummy() || cond2.is_dummy() { + None + } else if cond1 == cond2 { + condition1 + } else { + let op = Operation::Binary(node::Binary { + lhs: cond1, + rhs: cond2, + operator: BinaryOp::Or, + predicate: None, + }); + let cond = ctx.add_instruction(Instruction::new( + op, + ObjectType::Boolean, + Some(stack_frame.block), + )); + optimizations::simplify_id(ctx, cond).unwrap(); + stack_frame.push(cond); + Some(cond) + } + } + } + } + + pub fn unwrap_predicate(ctx: &SsaContext, predicate: &Option) -> NodeId { + let predicate = predicate.unwrap_or(NodeId::dummy()); + if predicate.is_dummy() { + ctx.one() + } else { + predicate + } + } + fn synchronize( &self, ctx: &mut SsaContext, @@ -791,19 +853,18 @@ impl DecisionTree { // 3. Merge the blocks using the solution let mut left_pos = 0; let mut right_pos = 0; - let mut result = Vec::new(); + let mut stack_frame = StackFrame::new(block_id); for i in solution { - result.extend_from_slice(&left[left_pos..i.left.0]); + stack_frame.stack.extend_from_slice(&left[left_pos..i.left.0]); left_pos = i.left.0; - result.extend_from_slice(&right[right_pos..i.right.0]); + stack_frame.stack.extend_from_slice(&right[right_pos..i.right.0]); right_pos = i.right.0; //merge i: - let left_ins = ctx.instruction(left[left_pos]); - let right_ins = ctx.instruction(right[right_pos]); + let left_ins = ctx.instruction(left[left_pos]).clone(); + let right_ins = ctx.instruction(right[right_pos]).clone(); let assumption = &self[ctx[block_id].assumption]; - let mut to_merge = Vec::new(); - let mut merged_op = match (&left_ins.operation, &right_ins.operation) { + let merged_op = match (&left_ins.operation, &right_ins.operation) { ( Operation::Call { func: left_func, @@ -815,6 +876,7 @@ impl DecisionTree { Operation::Call { func: right_func, arguments: right_arg, .. }, ) => { debug_assert_eq!(left_func, right_func); + let mut args = Vec::new(); for a in left_arg.iter().enumerate() { let op = Operation::Cond { condition: self[assumption.parent].condition, @@ -822,20 +884,28 @@ impl DecisionTree { val_false: right_arg[a.0], }; let typ = ctx.object_type(*a.1); - to_merge.push(Instruction::new(op, typ, Some(block_id))); + let arg_id = ctx.add_instruction(Instruction::new(op, typ, Some(block_id))); + stack_frame.stack.push(arg_id); + args.push(arg_id); } Operation::Call { func: *left_func, - arguments: Vec::new(), + arguments: args, returned_arrays: left_arrays.clone(), predicate: self.root, location: *left_location, } } ( - Operation::Store { array_id: left_array, index: left_index, value: left_val }, - Operation::Store { value: right_val, .. }, + Operation::Store { + array_id: left_array, + index: left_index, + value: left_val, + predicate: left_pred, + }, + Operation::Store { value: right_val, predicate: right_pred, .. }, ) => { + let pred = Self::or_conditions(*left_pred, *right_pred, &mut stack_frame, ctx); let op = Operation::Cond { condition: self[assumption.parent].condition, val_true: *left_val, @@ -843,40 +913,30 @@ impl DecisionTree { }; let merge = Instruction::new(op, ctx.mem[*left_array].element_type, Some(block_id)); - to_merge.push(merge); + let merge_id = ctx.add_instruction(merge); + stack_frame.stack.push(merge_id); Operation::Store { array_id: *left_array, index: *left_index, - value: NodeId::dummy(), + value: merge_id, + predicate: pred, } } _ => unreachable!(), }; - - let mut merge_ids = Vec::new(); - for merge in to_merge { - let merge_id = ctx.add_instruction(merge); - result.push(merge_id); - merge_ids.push(merge_id); - } - if let Operation::Store { value, .. } = &mut merged_op { - *value = *merge_ids.last().unwrap(); - } else { - if let Operation::Call { arguments, .. } = &mut merged_op { - *arguments = merge_ids; - } + if let Opcode::Call(_) = merged_op.opcode() { let left_ins = ctx.instruction_mut(left[left_pos]); left_ins.mark = node::Mark::ReplaceWith(right[right_pos]); } let ins1 = ctx.instruction_mut(right[right_pos]); ins1.operation = merged_op; - result.push(ins1.id); + stack_frame.stack.push(ins1.id); left_pos += 1; right_pos += 1; } - result.extend_from_slice(&left[left_pos..left.len()]); - result.extend_from_slice(&right[right_pos..right.len()]); - result + stack_frame.stack.extend_from_slice(&left[left_pos..left.len()]); + stack_frame.stack.extend_from_slice(&right[right_pos..right.len()]); + stack_frame.stack } } diff --git a/crates/noirc_evaluator/src/ssa/context.rs b/crates/noirc_evaluator/src/ssa/context.rs index c01429ae735..7c815b73b81 100644 --- a/crates/noirc_evaluator/src/ssa/context.rs +++ b/crates/noirc_evaluator/src/ssa/context.rs @@ -123,8 +123,12 @@ impl SsaContext { #[allow(clippy::map_entry)] pub fn add_dummy_store(&mut self, a: ArrayId) { if !self.dummy_store.contains_key(&a) { - let op_a = - Operation::Store { array_id: a, index: NodeId::dummy(), value: NodeId::dummy() }; + let op_a = Operation::Store { + array_id: a, + index: NodeId::dummy(), + value: NodeId::dummy(), + predicate: None, + }; let dummy_store = node::Instruction::new(op_a, node::ObjectType::NotAnObject, None); let id = self.add_instruction(dummy_store); self.dummy_store.insert(a, id); @@ -197,11 +201,17 @@ impl SsaContext { Operation::Load { array_id, index } => { format!("load {array_id:?}, index {}", self.id_to_string(*index)) } - Operation::Store { array_id, index, value } => { + Operation::Store { array_id, index, value, predicate } => { + let pred_str = if let Some(predicate) = predicate { + format!(", predicate {}", self.id_to_string(*predicate)) + } else { + String::new() + }; format!( - "store {array_id:?}, index {}, value {}", + "store {array_id:?}, index {}, value {}{}", self.id_to_string(*index), - self.id_to_string(*value) + self.id_to_string(*value), + pred_str ) } Operation::Intrinsic(opcode, args) => format!("intrinsic {opcode}({})", join(args)), @@ -771,7 +781,8 @@ impl SsaContext { .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); let op_b = Operation::Load { array_id: b, index: idx_b }; let load = self.new_instruction(op_b, e_type)?; - let op_a = Operation::Store { array_id: a, index: idx_a, value: load }; + let op_a = + Operation::Store { array_id: a, index: idx_a, value: load, predicate: None }; self.new_instruction(op_a, l_type)?; } } else { @@ -851,7 +862,8 @@ impl SsaContext { if let Some(idx) = index { if let ObjectType::Pointer(a) = lhs_type { //Store - let op_a = Operation::Store { array_id: a, index: idx, value: rhs }; + let op_a = + Operation::Store { array_id: a, index: idx, value: rhs, predicate: None }; return self.new_instruction(op_a, self.mem[a].element_type); } else { unreachable!("Index expression must be for an array"); @@ -925,7 +937,7 @@ impl SsaContext { for (i, v) in values.iter().enumerate() { let index = self.get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); - let op_a = Operation::Store { array_id, index, value: *v }; + let op_a = Operation::Store { array_id, index, value: *v, predicate: None }; self.new_instruction_inline(op_a, e_type, stack_frame); } } @@ -950,7 +962,8 @@ impl SsaContext { .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); let op_b = Operation::Load { array_id: b, index: idx_b }; let load = self.new_instruction_inline(op_b, e_type, stack_frame); - let op_a = Operation::Store { array_id: a, index: idx_a, value: load }; + let op_a = + Operation::Store { array_id: a, index: idx_a, value: load, predicate: None }; self.new_instruction_inline(op_a, l_type, stack_frame); } } else { @@ -1046,7 +1059,7 @@ impl SsaContext { let v2 = self.new_instruction(op, el_type).unwrap(); self.current_block = exit_block; let v = self.new_phi(v1, v2, c); - let op = Operation::Store { array_id, index, value: v }; + let op = Operation::Store { array_id, index, value: v, predicate: None }; self.new_instruction(op, el_type).unwrap(); } id diff --git a/crates/noirc_evaluator/src/ssa/inline.rs b/crates/noirc_evaluator/src/ssa/inline.rs index 70e1e67e3e2..09b5bf0f937 100644 --- a/crates/noirc_evaluator/src/ssa/inline.rs +++ b/crates/noirc_evaluator/src/ssa/inline.rs @@ -313,10 +313,15 @@ pub fn inline_in_block( new_ins.id = clone.id; push_instruction(ctx, new_ins, stack_frame, inline_map); } - Operation::Store { array_id, index, value } => { + Operation::Store { array_id, index, value, predicate } => { let b = stack_frame.get_or_default(*array_id); let mut new_ins = Instruction::new( - Operation::Store { array_id: b, index: *index, value: *value }, + Operation::Store { + array_id: b, + index: *index, + value: *value, + predicate: *predicate, + }, clone.res_type, Some(stack_frame.block), ); diff --git a/crates/noirc_evaluator/src/ssa/integer.rs b/crates/noirc_evaluator/src/ssa/integer.rs index 20e3b5a26fa..340aacd171f 100644 --- a/crates/noirc_evaluator/src/ssa/integer.rs +++ b/crates/noirc_evaluator/src/ssa/integer.rs @@ -54,7 +54,7 @@ fn get_instruction_max_operand( Operation::Binary(node::Binary { operator, lhs, rhs, .. }) => { if let BinaryOp::Sub { .. } = operator { //TODO uses interval analysis instead - if matches!(ins.res_type, ObjectType::Unsigned(_)) { + if matches!(ins.res_type, ObjectType::Unsigned(_) | ObjectType::Boolean) { if let Some(lhs_const) = ctx.get_as_constant(*lhs) { let lhs_big = BigUint::from_bytes_be(&lhs_const.to_be_bytes()); if max_map[rhs] <= lhs_big { @@ -287,11 +287,15 @@ fn block_overflow( ins.mark = Mark::ReplaceWith(*val); } } - Operation::Store { array_id, index, value } => { + Operation::Store { array_id, index, value, predicate } => { if let Some(idx) = Memory::to_u32(ctx, index) { - let absolute_adr = ctx.mem[array_id].absolute_adr(idx); - //optimize static store - memory_map.insert(absolute_adr, value); + if ctx.is_one(crate::ssa::conditional::DecisionTree::unwrap_predicate( + ctx, &predicate, + )) { + let absolute_adr = ctx.mem[array_id].absolute_adr(idx); + //optimize static store + memory_map.insert(absolute_adr, value); + } } } Operation::Binary(node::Binary { operator: BinaryOp::Shl, lhs, rhs, .. }) => { diff --git a/crates/noirc_evaluator/src/ssa/node.rs b/crates/noirc_evaluator/src/ssa/node.rs index 47b4f75fc1a..db212a39b3e 100644 --- a/crates/noirc_evaluator/src/ssa/node.rs +++ b/crates/noirc_evaluator/src/ssa/node.rs @@ -107,6 +107,9 @@ impl NodeId { pub fn dummy() -> NodeId { NodeId(SsaContext::dummy_id()) } + pub fn is_dummy(&self) -> bool { + self.0 == SsaContext::dummy_id() + } } #[derive(Debug)] @@ -518,6 +521,7 @@ pub enum Operation { array_id: ArrayId, index: NodeId, value: NodeId, + predicate: Option, }, Intrinsic(builtin::Opcode, Vec), //Custom implementation of useful primitives which are more performant with Aztec backend @@ -1117,9 +1121,12 @@ impl Operation { Cond { condition: f(*condition), val_true: f(*lhs), val_false: f(*rhs) } } Load { array_id: array, index } => Load { array_id: *array, index: f(*index) }, - Store { array_id: array, index, value } => { - Store { array_id: *array, index: f(*index), value: f(*value) } - } + Store { array_id: array, index, value, predicate } => Store { + array_id: *array, + index: f(*index), + value: f(*value), + predicate: predicate.as_ref().map(|pred| f(*pred)), + }, Intrinsic(i, args) => Intrinsic(*i, vecmap(args.iter().copied(), f)), Nop => Nop, Call { func: func_id, arguments, returned_arrays, predicate, location } => Call { @@ -1164,9 +1171,10 @@ impl Operation { *rhs = f(*rhs) } Load { index, .. } => *index = f(*index), - Store { index, value, .. } => { + Store { index, value, predicate, .. } => { *index = f(*index); *value = f(*value); + *predicate = predicate.as_mut().map(|pred| f(*pred)); } Intrinsic(_, args) => { for arg in args { diff --git a/crates/noirc_evaluator/src/ssa/optimizations.rs b/crates/noirc_evaluator/src/ssa/optimizations.rs index f66ce3e817f..343a2f16aba 100644 --- a/crates/noirc_evaluator/src/ssa/optimizations.rs +++ b/crates/noirc_evaluator/src/ssa/optimizations.rs @@ -87,9 +87,9 @@ fn evaluate_intrinsic( ObjectType::NativeField, ); let op = if args[0] & (1 << i) != 0 { - Operation::Store { array_id: *a, index, value: ctx.one() } + Operation::Store { array_id: *a, index, value: ctx.one(), predicate: None } } else { - Operation::Store { array_id: *a, index, value: ctx.zero() } + Operation::Store { array_id: *a, index, value: ctx.zero(), predicate: None } }; let i = Instruction::new(op, ObjectType::NotAnObject, Some(block_id)); result.push(ctx.add_instruction(i)); @@ -210,8 +210,7 @@ fn cse_block_with_anchor( if ins.is_deleted() { continue; } - - let operator = ins.operation.map_id(|id| propagate(ctx, id, modified)); + let mut operator = ins.operation.map_id(|id| propagate(ctx, id, modified)); let mut new_mark = Mark::None; @@ -268,12 +267,72 @@ fn cse_block_with_anchor( } CseAction::Remove(id_to_remove) => { anchor.push_mem_instruction(ctx, *ins_id)?; - new_list.push(*ins_id); + // TODO if not found, it should be removed from other blocks; we could keep a list of instructions to remove if let Some(id) = new_list.iter().position(|x| *x == id_to_remove) { *modified = true; new_list.remove(id); } + // Store with predicate must be merged with the previous store + if let Operation::Store { + index: idx, + value: value2, + predicate: Some(predicate2), + .. + } = operator + { + if let Operation::Store { + value: value1, + predicate: predicate1, + .. + } = ctx.instruction(id_to_remove).operation + { + let (merge, pred) = if let Some(predicate1) = predicate1 { + if predicate1 != predicate2 { + let or_op = Operation::Binary(Binary { + lhs: predicate1, + rhs: predicate2, + operator: BinaryOp::Or, + predicate: None, + }); + let pred_id = ctx.add_instruction(Instruction::new( + or_op, + ObjectType::Boolean, + Some(block_id), + )); + new_list.push(pred_id); + (true, Some(pred_id)) + } else { + (false, None) + } + } else { + (true, None) + }; + if merge { + *modified = true; + let cond_op = Operation::Cond { + condition: predicate2, + val_true: value2, + val_false: value1, + }; + let cond_id = ctx.add_instruction(Instruction::new( + cond_op, + ctx.object_type(value2), + Some(block_id), + )); + new_list.push(cond_id); + operator = Operation::Store { + array_id: *x, + index: idx, + value: cond_id, + predicate: pred, + }; + } + } else { + unreachable!("ICE: expected store instruction") + } + } + new_list.push(*ins_id); } } } diff --git a/crates/noirc_evaluator/src/ssa/ssa_form.rs b/crates/noirc_evaluator/src/ssa/ssa_form.rs index 530158c6557..ebe2f2b4bfb 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_form.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_form.rs @@ -66,8 +66,12 @@ fn add_dummy_store(ctx: &mut SsaContext, entry: BlockId, join: BlockId) { //add dummy store for a in modified { - let store = - node::Operation::Store { array_id: a, index: NodeId::dummy(), value: NodeId::dummy() }; + let store = node::Operation::Store { + array_id: a, + index: NodeId::dummy(), + value: NodeId::dummy(), + predicate: None, + }; let i = node::Instruction::new(store, node::ObjectType::NotAnObject, Some(join)); ctx.insert_instruction_after_phi(i, join); } diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen.rs b/crates/noirc_evaluator/src/ssa/ssa_gen.rs index d5efe4cf4b4..eb5cfd8733c 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen.rs @@ -501,7 +501,12 @@ impl IrGenerator { FieldElement::from((pos as u32) as u128), ObjectType::NativeField, ); - let store = Operation::Store { array_id, index: lhs_adr, value: object }; + let store = Operation::Store { + array_id, + index: lhs_adr, + value: object, + predicate: None, + }; self.context.new_instruction(store, element_type)?; } Ok(Value::Node(new_var))