From 4210dd277680518e6d7073373ab6dfae394d452e Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 4 Jan 2025 01:03:01 -0800 Subject: [PATCH 1/2] Mutable variables in dynamic branches prevent full constant folding in partial evaluation This fixes the bug by having partial evaluation more explicitly track different variable mappings to literals across branches and recombining those mappings that match (ie: are constant) when all branches are done. This also includes partial eval and RIR SSA pass fixes to correctly support immutable and mutable copies of dynamic variables. New test cases for several combinations of constant folding at partial eval are included, as well as a new test case confirming RIR SSA fix. Fixes #2087 --- compiler/qsc/src/codegen/tests.rs | 4 +- .../src/evaluation_context.rs | 52 +- compiler/qsc_partial_eval/src/lib.rs | 85 ++- .../qsc_partial_eval/src/tests/assigns.rs | 234 ++++---- .../qsc_partial_eval/src/tests/bindings.rs | 49 +- compiler/qsc_partial_eval/src/tests/calls.rs | 3 +- .../src/tests/dynamic_vars.rs | 535 +++++++++++++++++- .../qsc_partial_eval/src/tests/intrinsics.rs | 1 + compiler/qsc_partial_eval/src/tests/misc.rs | 60 +- .../qsc_partial_eval/src/tests/operators.rs | 285 ++++++---- compiler/qsc_rir/src/passes/ssa_transform.rs | 7 +- .../qsc_rir/src/passes/ssa_transform/tests.rs | 153 +++++ .../output/Adaptive_RI/SuperdenseCoding.ll | 14 +- .../Adaptive_RI/ThreeQubitRepetitionCode.ll | 102 ++-- 14 files changed, 1192 insertions(+), 392 deletions(-) diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs index 6c82359c6a..2379b9262f 100644 --- a/compiler/qsc/src/codegen/tests.rs +++ b/compiler/qsc/src/codegen/tests.rs @@ -1230,8 +1230,8 @@ mod adaptive_ri_profile { block_2: br label %block_3 block_3: - %var_3 = phi i64 [0, %block_1], [1, %block_2] - call void @__quantum__rt__int_record_output(i64 %var_3, i8* null) + %var_4 = phi i64 [0, %block_1], [1, %block_2] + call void @__quantum__rt__int_record_output(i64 %var_4, i8* null) ret void } diff --git a/compiler/qsc_partial_eval/src/evaluation_context.rs b/compiler/qsc_partial_eval/src/evaluation_context.rs index a0468580da..b124634d8c 100644 --- a/compiler/qsc_partial_eval/src/evaluation_context.rs +++ b/compiler/qsc_partial_eval/src/evaluation_context.rs @@ -96,12 +96,10 @@ pub struct Scope { pub args_value_kind: Vec, /// The classical environment of the callable, which holds values corresponding to local variables. pub env: Env, - // Consider optimizing `hybrid_vars` and `mutable_vars` by removing them and enlightening the evaluator on how to - // properly handle `Value::Var`, which could be either `static` or `dynamic`. /// Map that holds the values of local variables. hybrid_vars: FxHashMap, - /// Maps variable IDs to mutable variables, which contain their current kind. - mutable_vars: FxHashMap, + /// Maps variable IDs to static literal values, if any. + static_vars: FxHashMap, /// Number of currently active blocks (starting from where this scope was created). active_block_count: usize, } @@ -161,18 +159,37 @@ impl Scope { env, active_block_count: 1, hybrid_vars, - mutable_vars: FxHashMap::default(), + static_vars: FxHashMap::default(), } } - /// Gets a mutable variable. - pub fn find_mutable_kind(&self, var_id: VariableId) -> Option<&MutableKind> { - self.mutable_vars.get(&var_id) + /// Gets the static literal value for the given variable. + pub fn get_static_value(&self, var_id: VariableId) -> Option<&Literal> { + self.static_vars.get(&var_id) } - /// Gets a mutable mutable variable. - pub fn find_mutable_var_mut(&mut self, var_id: VariableId) -> Option<&mut MutableKind> { - self.mutable_vars.get_mut(&var_id) + /// Removes the static literal value for the given variable. + pub fn remove_static_value(&mut self, var_id: VariableId) { + self.static_vars.remove(&var_id); + } + + /// Clones the static literal value mappings, which allows callers to cache the mappings across branches. + pub fn clone_static_var_mappings(&self) -> FxHashMap { + self.static_vars.clone() + } + + /// Sets the static literal value mappings to the given mapping, overwriting any existing mappings. + pub fn set_static_var_mappings(&mut self, static_vars: FxHashMap) { + self.static_vars = static_vars; + } + + /// Keeps only the static literal value mappings that are also present in the provided other mapping. + pub fn keep_matching_static_var_mappings( + &mut self, + other_mappings: &FxHashMap, + ) { + self.static_vars + .retain(|var_id, lit| Some(&*lit) == other_mappings.get(var_id)); } /// Gets the value of a hybrid local variable. @@ -197,11 +214,8 @@ impl Scope { } // Insert a variable into the mutable variables map. - pub fn insert_mutable_var(&mut self, var_id: VariableId, mutable_kind: MutableKind) { - let Entry::Vacant(vacant) = self.mutable_vars.entry(var_id) else { - panic!("mutable variable should not already exist"); - }; - vacant.insert(mutable_kind); + pub fn insert_static_var_mapping(&mut self, var_id: VariableId, literal: Literal) { + self.static_vars.insert(var_id, literal); } /// Determines whether we are currently evaluating a branch within the scope. @@ -317,9 +331,3 @@ fn map_eval_value_to_value_kind(value: &Value) -> ValueKind { | Value::String(_) => ValueKind::Element(RuntimeKind::Static), } } - -#[derive(Clone, Copy, Debug)] -pub enum MutableKind { - Static(Literal), - Dynamic, -} diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs index d6f5768810..741fb13038 100644 --- a/compiler/qsc_partial_eval/src/lib.rs +++ b/compiler/qsc_partial_eval/src/lib.rs @@ -13,7 +13,7 @@ mod management; use core::panic; use evaluation_context::{ - Arg, BlockNode, BranchControlFlow, EvalControlFlow, EvaluationContext, MutableKind, Scope, + Arg, BlockNode, BranchControlFlow, EvalControlFlow, EvaluationContext, Scope, }; use management::{QuantumIntrinsicsChecker, ResourceManager}; use miette::Diagnostic; @@ -204,8 +204,19 @@ impl<'a> PartialEvaluator<'a> { fn bind_value_to_ident(&mut self, mutability: Mutability, ident: &Ident, value: Value) { // We do slightly different things depending on the mutability of the identifier. match mutability { - Mutability::Immutable => self.bind_value_to_immutable_ident(ident, value), Mutability::Mutable => self.bind_value_to_mutable_ident(ident, value), + Mutability::Immutable => { + let current_scope = self.eval_context.get_current_scope(); + if matches!(value, Value::Var(var) if current_scope.get_static_value(var.id.into()).is_none()) + { + // An immutable identifier is being bound to a dynamic value, so treat the identifier as mutable. + // This allows it to represent a point-in-time copy of the mutable value during evaluation. + self.bind_value_to_mutable_ident(ident, value); + } else { + // The value is static, so bind it to the classical map. + self.bind_value_to_immutable_ident(ident, value); + } + } }; } @@ -226,11 +237,13 @@ impl<'a> PartialEvaluator<'a> { } // Always bind the value to the hybrid map but do it differently depending of the value type. - if let Some((var_id, mutable_kind)) = self.try_create_mutable_variable(ident.id, &value) { - // Keep track of whether the mutable variable is static or dynamic. - self.eval_context - .get_current_scope_mut() - .insert_mutable_var(var_id, mutable_kind); + if let Some((var_id, literal)) = self.try_create_mutable_variable(ident.id, &value) { + // If the variable maps to a know static literal, track that mapping. + if let Some(literal) = literal { + self.eval_context + .get_current_scope_mut() + .insert_static_var_mapping(var_id, literal); + } } else { self.bind_value_in_hybrid_map(ident, value); } @@ -1510,6 +1523,11 @@ impl<'a> PartialEvaluator<'a> { }; // Evaluate the body expression. + // First, we cache the current static variable mappings so that we can restore them later. + let cached_mappings = self + .eval_context + .get_current_scope() + .clone_static_var_mappings(); let if_true_branch_control_flow = self.eval_expr_if_branch(body_expr_id, continuation_block_node_id, maybe_if_expr_var)?; let if_true_block_id = match if_true_branch_control_flow { @@ -1519,16 +1537,37 @@ impl<'a> PartialEvaluator<'a> { // Evaluate the otherwise expression (if any), and determine the block to branch to if the condition is false. let if_false_block_id = if let Some(otherwise_expr_id) = otherwise_expr_id { + // Cache the mappings after the true block so we can compare afterwards. + let post_if_true_mappings = self + .eval_context + .get_current_scope() + .clone_static_var_mappings(); + // Restore the cached mappings from before evaluating the true block. + self.eval_context + .get_current_scope_mut() + .set_static_var_mappings(cached_mappings); let if_false_branch_control_flow = self.eval_expr_if_branch( otherwise_expr_id, continuation_block_node_id, maybe_if_expr_var, )?; + // Only keep the static mappings that are the same in both blocks; when they are different, + // the variable is no longer static across the if expression. + self.eval_context + .get_current_scope_mut() + .keep_matching_static_var_mappings(&post_if_true_mappings); match if_false_branch_control_flow { BranchControlFlow::Block(block_id) => block_id, BranchControlFlow::Return(value) => return Ok(EvalControlFlow::Return(value)), } } else { + // Only keep the static mappings that are the same after the true block as before; when they are different, + // the variable is no longer static across the if expression. + self.eval_context + .get_current_scope_mut() + .keep_matching_static_var_mappings(&cached_mappings); + + // Since there is no otherwise block, we branch to the continuation block. continuation_block_node_id }; @@ -1814,9 +1853,7 @@ impl<'a> PartialEvaluator<'a> { // the variable if it is static at this moment. if let Value::Var(var) = bound_value { let current_scope = self.eval_context.get_current_scope(); - if let Some(MutableKind::Static(literal)) = - current_scope.find_mutable_kind(var.id.into()) - { + if let Some(literal) = current_scope.get_static_value(var.id.into()) { map_rir_literal_to_eval_value(*literal) } else { bound_value.clone() @@ -2229,7 +2266,7 @@ impl<'a> PartialEvaluator<'a> { &mut self, local_var_id: LocalVarId, value: &Value, - ) -> Option<(rir::VariableId, MutableKind)> { + ) -> Option<(rir::VariableId, Option)> { // Check if we can create a mutable variable for this value. let var_ty = try_get_eval_var_type(value)?; @@ -2249,13 +2286,13 @@ impl<'a> PartialEvaluator<'a> { let store_ins = Instruction::Store(value_operand, rir_var); self.get_current_rir_block_mut().0.push(store_ins); - // Create a mutable variable. - let mutable_kind = match value_operand { - Operand::Literal(literal) => MutableKind::Static(literal), - Operand::Variable(_) => MutableKind::Dynamic, + // Create a mutable variable, mapping it to the static value if any. + let static_value = match value_operand { + Operand::Literal(literal) => Some(literal), + Operand::Variable(_) => None, }; - Some((var_id, mutable_kind)) + Some((var_id, static_value)) } fn get_or_insert_callable(&mut self, callable: Callable) -> CallableId { @@ -2625,14 +2662,16 @@ impl<'a> PartialEvaluator<'a> { // If this is a mutable variable, make sure to update whether it is static or dynamic. let current_scope = self.eval_context.get_current_scope_mut(); - if matches!(rhs_operand, Operand::Variable(_)) - || current_scope.is_currently_evaluating_branch() - { - if let Some(mutable_kind) = current_scope.find_mutable_var_mut(rir_var.variable_id) - { - *mutable_kind = MutableKind::Dynamic; + match rhs_operand { + Operand::Literal(literal) => { + // The variable maps to a static literal here, so track that literal value. + current_scope.insert_static_var_mapping(rir_var.variable_id, literal); } - } + Operand::Variable(_) => { + // The variable is not known to be some literal value, so remove the static mapping. + current_scope.remove_static_value(rir_var.variable_id); + } + }; } else { // Verify that we are not updating a value that does not have a backing variable from a dynamic branch // because it is unsupported. diff --git a/compiler/qsc_partial_eval/src/tests/assigns.rs b/compiler/qsc_partial_eval/src/tests/assigns.rs index 3a0e8e2ae3..321cb4da97 100644 --- a/compiler/qsc_partial_eval/src/tests/assigns.rs +++ b/compiler/qsc_partial_eval/src/tests/assigns.rs @@ -191,7 +191,8 @@ fn assigning_dynamic_bool_updates_value_and_adds_store_instructions() { Variable(1, Boolean) = Call id(2), args( Result(0), ) Variable(2, Boolean) = Store Variable(1, Boolean) Variable(0, Boolean) = Store Variable(2, Boolean) - Call id(3), args( Variable(0, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(0, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); } @@ -301,7 +302,8 @@ fn assigning_dynamic_int_updates_value_and_adds_store_instructions() { Branch Variable(2, Boolean), 2, 3 Block 1:Block: Variable(0, Integer) = Store Variable(3, Integer) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(4, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(4, Integer), Pointer, ) Return Block 2:Block: Variable(3, Integer) = Store Integer(1) @@ -379,7 +381,8 @@ fn assigning_classical_bool_within_dynamic_if_expression_adds_store_instruction( Variable(2, Boolean) = Store Variable(1, Boolean) Branch Variable(2, Boolean), 2, 1 Block 1:Block: - Call id(3), args( Variable(0, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(0, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return Block 2:Block: Variable(0, Boolean) = Store Bool(true) @@ -456,7 +459,8 @@ fn assigning_classical_int_within_dynamic_if_else_expression_adds_store_instruct Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) Branch Variable(2, Boolean), 2, 3 Block 1:Block: - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) Return Block 2:Block: Variable(0, Integer) = Store Integer(1) @@ -1210,9 +1214,11 @@ fn logical_and_assign_with_lhs_classical_true_is_optimized_as_store() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Store Bool(true) Variable(2, Boolean) = Store Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(3, Boolean) = Store Bool(true) + Variable(3, Boolean) = Store Variable(2, Boolean) + Variable(4, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(4, Boolean), Pointer, ) Return"#]], ); } @@ -1282,8 +1288,9 @@ fn logical_and_assign_with_lhs_classical_false_short_circuits_evaluation() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Store Bool(false) - Variable(2, Boolean) = Store Bool(false) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Store Bool(false) + Variable(3, Boolean) = Store Bool(false) Call id(3), args( Bool(false), Pointer, ) Return"#]], ); @@ -1358,7 +1365,8 @@ fn logical_and_assign_with_dynamic_lhs_and_dynamic_rhs_short_circuits_when_rhs_i Branch Variable(2, Boolean), 2, 1 Block 1:Block: Variable(2, Boolean) = Store Variable(3, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(6, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(6, Boolean), Pointer, ) Return Block 2:Block: Call id(1), args( Qubit(0), Result(1), ) @@ -1434,8 +1442,9 @@ fn logical_or_assign_with_lhs_classical_true_short_circuits_evaluation() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Store Bool(true) - Variable(2, Boolean) = Store Bool(true) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Store Bool(true) + Variable(3, Boolean) = Store Bool(true) Call id(3), args( Bool(true), Pointer, ) Return"#]], ); @@ -1506,9 +1515,11 @@ fn logical_or_assign_with_lhs_classical_false_is_optimized_as_store() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Store Bool(false) Variable(2, Boolean) = Store Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(3, Boolean) = Store Bool(false) + Variable(3, Boolean) = Store Variable(2, Boolean) + Variable(4, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(4, Boolean), Pointer, ) Return"#]], ); } @@ -1582,7 +1593,8 @@ fn logical_or_assign_with_dynamic_lhs_and_dynamic_rhs_short_circuits_when_rhs_is Branch Variable(2, Boolean), 1, 2 Block 1:Block: Variable(2, Boolean) = Store Variable(3, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(6, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(6, Boolean), Pointer, ) Return Block 2:Block: Call id(1), args( Qubit(0), Result(1), ) @@ -1662,7 +1674,8 @@ fn integer_assign_add_with_lhs_classical_integer_and_rhs_dynamic_integer() { Block 1:Block: Variable(4, Integer) = Add Integer(0), Variable(3, Integer) Variable(0, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(5, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(3, Integer) = Store Integer(0) @@ -1732,24 +1745,25 @@ fn integer_assign_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 - Block 1:Block: - Variable(3, Integer) = Store Variable(2, Integer) - Variable(4, Integer) = Sub Variable(3, Integer), Integer(1) - Variable(3, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) - Return - Block 2:Block: - Variable(2, Integer) = Store Integer(0) - Jump(1) - Block 3:Block: - Variable(2, Integer) = Store Integer(1) - Jump(1)"#]], + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Sub Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Variable(5, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], ); } @@ -1812,35 +1826,36 @@ fn integer_assign_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 - Block 1:Block: - Variable(3, Integer) = Store Variable(2, Integer) - Call id(1), args( Qubit(0), Result(1), ) - Variable(4, Boolean) = Call id(2), args( Result(1), ) - Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) - Branch Variable(5, Boolean), 5, 6 - Block 2:Block: - Variable(2, Integer) = Store Integer(0) - Jump(1) - Block 3:Block: - Variable(2, Integer) = Store Integer(1) - Jump(1) - Block 4:Block: - Variable(7, Integer) = Mul Variable(3, Integer), Variable(6, Integer) - Variable(3, Integer) = Store Variable(7, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) - Return - Block 5:Block: - Variable(6, Integer) = Store Integer(1) - Jump(4) - Block 6:Block: - Variable(6, Integer) = Store Integer(0) - Jump(4)"#]], + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(1), args( Qubit(0), Result(1), ) + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(7, Integer) = Mul Variable(3, Integer), Variable(6, Integer) + Variable(3, Integer) = Store Variable(7, Integer) + Variable(8, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(8, Integer), Pointer, ) + Return + Block 5:Block: + Variable(6, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(6, Integer) = Store Integer(0) + Jump(4)"#]], ); } @@ -1913,7 +1928,8 @@ fn integer_assign_div_with_lhs_classical_integer_and_rhs_dynamic_integer() { Block 1:Block: Variable(4, Integer) = Sdiv Integer(0), Variable(3, Integer) Variable(0, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(5, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(3, Integer) = Store Integer(0) @@ -1983,24 +1999,25 @@ fn integer_assign_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 - Block 1:Block: - Variable(3, Integer) = Store Variable(2, Integer) - Variable(4, Integer) = Srem Variable(3, Integer), Integer(1) - Variable(3, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) - Return - Block 2:Block: - Variable(2, Integer) = Store Integer(0) - Jump(1) - Block 3:Block: - Variable(2, Integer) = Store Integer(1) - Jump(1)"#]], + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Srem Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Variable(5, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], ); } @@ -2119,7 +2136,8 @@ fn integer_assign_exp_with_lhs_dynamic_integer_and_rhs_classical_zero_integer() Variable(3, Integer) = Store Variable(2, Integer) Variable(4, Integer) = Store Integer(1) Variable(3, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(5, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2202,7 +2220,8 @@ fn integer_assign_exp_with_lhs_dynamic_integer_and_rhs_classical_positive_intege Variable(6, Integer) = Mul Variable(5, Integer), Variable(3, Integer) Variable(7, Integer) = Mul Variable(6, Integer), Variable(3, Integer) Variable(3, Integer) = Store Variable(7, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(8, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(8, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2316,7 +2335,8 @@ fn integer_assign_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() Block 4:Block: Variable(7, Integer) = BitwiseAnd Variable(3, Integer), Variable(6, Integer) Variable(3, Integer) = Store Variable(7, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(8, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(8, Integer), Pointer, ) Return Block 5:Block: Variable(6, Integer) = Store Integer(1) @@ -2396,7 +2416,8 @@ fn integer_assign_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer( Block 1:Block: Variable(4, Integer) = BitwiseOr Integer(0), Variable(3, Integer) Variable(0, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(5, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(3, Integer) = Store Integer(0) @@ -2466,24 +2487,25 @@ fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 - Block 1:Block: - Variable(3, Integer) = Store Variable(2, Integer) - Variable(4, Integer) = BitwiseXor Variable(3, Integer), Integer(1) - Variable(3, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) - Return - Block 2:Block: - Variable(2, Integer) = Store Integer(0) - Jump(1) - Block 3:Block: - Variable(2, Integer) = Store Integer(1) - Jump(1)"#]], + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = BitwiseXor Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Variable(5, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], ); } @@ -2567,7 +2589,8 @@ fn integer_assign_bitwise_left_shift_with_lhs_dynamic_integer_and_rhs_dynamic_in Block 4:Block: Variable(7, Integer) = Shl Variable(3, Integer), Variable(6, Integer) Variable(3, Integer) = Store Variable(7, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(8, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(8, Integer), Pointer, ) Return Block 5:Block: Variable(6, Integer) = Store Integer(1) @@ -2647,7 +2670,8 @@ fn integer_assign_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic Block 1:Block: Variable(4, Integer) = Ashr Integer(0), Variable(3, Integer) Variable(0, Integer) = Store Variable(4, Integer) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(5, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(3, Integer) = Store Integer(0) diff --git a/compiler/qsc_partial_eval/src/tests/bindings.rs b/compiler/qsc_partial_eval/src/tests/bindings.rs index a669d879c8..8c25538491 100644 --- a/compiler/qsc_partial_eval/src/tests/bindings.rs +++ b/compiler/qsc_partial_eval/src/tests/bindings.rs @@ -175,7 +175,9 @@ fn immutable_bool_binding_does_not_generate_store_instruction() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); } @@ -242,7 +244,8 @@ fn mutable_bool_binding_generates_store_instruction() { Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) Variable(2, Boolean) = Store Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); } @@ -310,7 +313,9 @@ fn immutable_int_binding_does_not_generate_store_instruction() { Variable(1, Boolean) = Store Variable(0, Boolean) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Call id(3), args( Variable(2, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(4, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -385,7 +390,8 @@ fn mutable_int_binding_does_generate_store_instruction() { Branch Variable(1, Boolean), 2, 3 Block 1:Block: Variable(3, Integer) = Store Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(4, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(4, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -462,22 +468,23 @@ fn mutable_variable_in_outer_scope_set_to_mutable_from_inner_scope() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Variable(0, Integer) = Store Integer(0) - Call id(1), args( Qubit(0), Result(0), ) - Variable(1, Boolean) = Call id(2), args( Result(0), ) - Variable(2, Boolean) = Store Variable(1, Boolean) - Branch Variable(2, Boolean), 2, 3 - Block 1:Block: - Call id(3), args( Variable(0, Integer), Pointer, ) - Return - Block 2:Block: - Variable(3, Integer) = Store Integer(1) - Variable(0, Integer) = Store Integer(1) - Jump(1) - Block 3:Block: - Variable(0, Integer) = Store Integer(2) - Jump(1)"#]], + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Variable(4, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(4, Integer), Pointer, ) + Return + Block 2:Block: + Variable(3, Integer) = Store Integer(1) + Variable(0, Integer) = Store Integer(1) + Jump(1) + Block 3:Block: + Variable(0, Integer) = Store Integer(2) + Jump(1)"#]], ); } diff --git a/compiler/qsc_partial_eval/src/tests/calls.rs b/compiler/qsc_partial_eval/src/tests/calls.rs index f614c7120c..6607eaf575 100644 --- a/compiler/qsc_partial_eval/src/tests/calls.rs +++ b/compiler/qsc_partial_eval/src/tests/calls.rs @@ -553,7 +553,8 @@ fn call_to_operation_that_returns_dynamic_bool() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) Return"#]], ); } diff --git a/compiler/qsc_partial_eval/src/tests/dynamic_vars.rs b/compiler/qsc_partial_eval/src/tests/dynamic_vars.rs index d0b580f863..f278746bcb 100644 --- a/compiler/qsc_partial_eval/src/tests/dynamic_vars.rs +++ b/compiler/qsc_partial_eval/src/tests/dynamic_vars.rs @@ -55,21 +55,22 @@ fn dynamic_int_from_if_expression_with_single_measurement_comparison_and_classic assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 - Block 1:Block: - Call id(3), args( Integer(0), Pointer, ) - Return - Block 2:Block: - Variable(2, Integer) = Store Integer(0) - Jump(1) - Block 3:Block: - Variable(2, Integer) = Store Integer(1) - Jump(1)"#]], + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(3), args( Integer(0), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], ); } @@ -150,25 +151,513 @@ fn dynamic_int_from_if_expression_with_single_measurement_comparison_and_non_cla output_type: body: "#]], ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(5), args( Integer(0), Pointer, ) + Return + Block 2:Block: + Call id(3), args( Qubit(1), ) + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Call id(4), args( Qubit(1), ) + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn dynamic_var_across_if_else_static_in_both_branches_constant_folded() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + } else { + value += 1; + } + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Call id(3), args( Integer(0), Pointer, ) + Return + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Jump(1) + Block 3:Block: + Variable(0, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn dynamic_var_across_if_else_in_loop_constant_folded_in_first_iteration() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + for _ in 0..1 { + if cond == Zero { + value -= 1; + } else { + value += 1; + } + } + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Integer) = Store Integer(0) + Variable(2, Boolean) = Call id(2), args( Result(0), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) + Branch Variable(3, Boolean), 2, 3 + Block 1:Block: + Variable(1, Integer) = Store Integer(1) + Variable(4, Boolean) = Call id(2), args( Result(0), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Jump(1) + Block 3:Block: + Variable(0, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(1, Integer) = Store Integer(2) + Call id(3), args( Integer(0), Pointer, ) + Return + Block 5:Block: + Variable(6, Integer) = Sub Variable(0, Integer), Integer(1) + Variable(0, Integer) = Store Variable(6, Integer) + Jump(4) + Block 6:Block: + Variable(7, Integer) = Add Variable(0, Integer), Integer(1) + Variable(0, Integer) = Store Variable(7, Integer) + Jump(4)"#]], + ); +} + +#[test] +fn dynamic_var_within_if_else_in_loop_constant_folded_in_every_iteration() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + use q = Qubit(); + let cond = MResetZ(q); + for _ in 0..1 { + mutable value = 0; + if cond == Zero { + value -= 1; + } else { + value += 1; + } + } + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Integer) = Store Integer(0) + Variable(1, Integer) = Store Integer(0) + Variable(2, Boolean) = Call id(2), args( Result(0), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) + Branch Variable(3, Boolean), 2, 3 + Block 1:Block: + Variable(0, Integer) = Store Integer(1) + Variable(4, Integer) = Store Integer(0) + Variable(5, Boolean) = Call id(2), args( Result(0), ) + Variable(6, Boolean) = Icmp Eq, Variable(5, Boolean), Bool(false) + Branch Variable(6, Boolean), 5, 6 + Block 2:Block: + Variable(1, Integer) = Store Integer(-1) + Jump(1) + Block 3:Block: + Variable(1, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(0, Integer) = Store Integer(2) + Call id(3), args( Integer(0), Pointer, ) + Return + Block 5:Block: + Variable(4, Integer) = Store Integer(-1) + Jump(4) + Block 6:Block: + Variable(4, Integer) = Store Integer(1) + Jump(4)"#]], + ); +} + +#[test] +fn dynamic_var_updated_twice_in_same_branch_constant_folded() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + value += 3; + } else { + value += 1; + value -= 3; + } + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Call id(3), args( Integer(0), Pointer, ) + Return + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(2) + Jump(1) + Block 3:Block: + Variable(0, Integer) = Store Integer(1) + Variable(0, Integer) = Store Integer(-2) + Jump(1)"#]], + ); +} + +#[test] +fn dynamic_var_updated_to_same_value_in_different_branches_constant_folded_after_if() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + value += 2; + } else { + value += 1; + value /= 1; + } + value += 1; + } + } + "#, + }); + assert_blocks( &program, &expect![[r#" Blocks: Block 0:Block: + Variable(0, Integer) = Store Integer(0) Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Branch Variable(1, Boolean), 2, 3 + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 Block 1:Block: - Call id(5), args( Integer(0), Pointer, ) + Variable(0, Integer) = Store Integer(2) + Call id(3), args( Integer(0), Pointer, ) Return Block 2:Block: - Call id(3), args( Qubit(1), ) - Variable(2, Integer) = Store Integer(0) + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(1) Jump(1) Block 3:Block: - Call id(4), args( Qubit(1), ) - Variable(2, Integer) = Store Integer(1) + Variable(0, Integer) = Store Integer(1) + Variable(0, Integer) = Store Integer(1) Jump(1)"#]], ); } + +#[test] +fn dynamic_var_updated_in_nested_branches_constant_folded_when_value_matches_across_all_branches() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + if cond == Zero { + value += 2; + } else { + value -= 2; + value /= 3; + value *= -1; + } + } else { + value += 1; + if cond == Zero { + value += 2; + value /= 3; + } else { + value -= 2; + value *= -1; + } + } + return value; + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 6 + Block 1:Block: + Call id(3), args( Integer(1), Pointer, ) + Return + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Variable(3, Boolean) = Call id(2), args( Result(0), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 4, 5 + Block 3:Block: + Jump(1) + Block 4:Block: + Variable(0, Integer) = Store Integer(1) + Jump(3) + Block 5:Block: + Variable(0, Integer) = Store Integer(-3) + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(1) + Jump(3) + Block 6:Block: + Variable(0, Integer) = Store Integer(1) + Variable(5, Boolean) = Call id(2), args( Result(0), ) + Variable(6, Boolean) = Icmp Eq, Variable(5, Boolean), Bool(false) + Branch Variable(6, Boolean), 8, 9 + Block 7:Block: + Jump(1) + Block 8:Block: + Variable(0, Integer) = Store Integer(3) + Variable(0, Integer) = Store Integer(1) + Jump(7) + Block 9:Block: + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(1) + Jump(7)"#]], + ); +} + +#[test] +fn dynamic_var_set_to_static_after_dynamism_still_constant_folded() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Unit { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + } + value *= 2; + value = 3; + value += 1; + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 1 + Block 1:Block: + Variable(3, Integer) = Mul Variable(0, Integer), Integer(2) + Variable(0, Integer) = Store Variable(3, Integer) + Variable(0, Integer) = Store Integer(3) + Variable(0, Integer) = Store Integer(4) + Call id(3), args( Integer(0), Pointer, ) + Return + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Jump(1)"#]], + ); +} + +#[test] +fn dynamic_var_updated_in_loop_constant_folded_when_every_iteration_results_in_same_value() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + for _ in 0..1 { + if cond == Zero { + value -= 1; + value ^= 2; + } else { + value += 1; + } + value -= 1; + } + return value + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Integer) = Store Integer(0) + Variable(2, Boolean) = Call id(2), args( Result(0), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) + Branch Variable(3, Boolean), 2, 3 + Block 1:Block: + Variable(0, Integer) = Store Integer(0) + Variable(1, Integer) = Store Integer(1) + Variable(4, Boolean) = Call id(2), args( Result(0), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(1) + Jump(1) + Block 3:Block: + Variable(0, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(0, Integer) = Store Integer(0) + Variable(1, Integer) = Store Integer(2) + Call id(3), args( Integer(0), Pointer, ) + Return + Block 5:Block: + Variable(0, Integer) = Store Integer(-1) + Variable(0, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(0, Integer) = Store Integer(1) + Jump(4)"#]], + ); +} + +#[test] +fn immutable_bind_of_dynamic_var_should_be_point_in_time_copy() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : (Int, Int) { + mutable value = 0; + use q = Qubit(); + let cond = MResetZ(q); + if cond == Zero { + value -= 1; + } + let copy = value; + value += 1; + (copy, value) + } + } + "#, + }); + + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 1 + Block 1:Block: + Variable(3, Integer) = Store Variable(0, Integer) + Variable(4, Integer) = Add Variable(0, Integer), Integer(1) + Variable(0, Integer) = Store Variable(4, Integer) + Call id(3), args( Integer(2), Pointer, ) + Call id(4), args( Variable(3, Integer), Pointer, ) + Call id(4), args( Variable(0, Integer), Pointer, ) + Return + Block 2:Block: + Variable(0, Integer) = Store Integer(-1) + Jump(1)"#]], + ); +} diff --git a/compiler/qsc_partial_eval/src/tests/intrinsics.rs b/compiler/qsc_partial_eval/src/tests/intrinsics.rs index bd82f1421d..4a2379682f 100644 --- a/compiler/qsc_partial_eval/src/tests/intrinsics.rs +++ b/compiler/qsc_partial_eval/src/tests/intrinsics.rs @@ -767,6 +767,7 @@ fn call_to_intrinsic_begin_estimate_caching_with_dynamic_values_yields_true() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(3), args( Qubit(0), ) Call id(4), args( Integer(0), Pointer, ) Return diff --git a/compiler/qsc_partial_eval/src/tests/misc.rs b/compiler/qsc_partial_eval/src/tests/misc.rs index 30f9094766..a493abed6a 100644 --- a/compiler/qsc_partial_eval/src/tests/misc.rs +++ b/compiler/qsc_partial_eval/src/tests/misc.rs @@ -206,7 +206,8 @@ fn boolean_assign_and_update_with_classical_value_within_an_if_with_dynamic_cond Variable(2, Boolean) = Store Variable(1, Boolean) Branch Variable(2, Boolean), 2, 1 Block 1:Block: - Call id(3), args( Variable(0, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(0, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return Block 2:Block: Variable(0, Boolean) = Store Bool(false) @@ -242,7 +243,8 @@ fn integer_assign_and_update_with_classical_value_within_an_if_with_dynamic_cond Variable(2, Boolean) = Store Variable(1, Boolean) Branch Variable(2, Boolean), 2, 1 Block 1:Block: - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) Return Block 2:Block: Variable(0, Integer) = Store Integer(5) @@ -313,31 +315,32 @@ fn integer_assign_with_hybrid_value_within_an_if_with_dynamic_condition() { assert_blocks( &program, &expect![[r#" - Blocks: - Block 0:Block: - Variable(0, Integer) = Store Integer(0) - Variable(1, Integer) = Store Integer(0) - Call id(1), args( Qubit(0), Result(0), ) - Variable(2, Boolean) = Call id(2), args( Result(0), ) - Variable(3, Boolean) = Store Variable(2, Boolean) - Branch Variable(3, Boolean), 2, 1 - Block 1:Block: - Variable(1, Integer) = Store Integer(1) - Call id(1), args( Qubit(0), Result(1), ) - Variable(4, Boolean) = Call id(2), args( Result(1), ) - Variable(5, Boolean) = Store Variable(4, Boolean) - Branch Variable(5, Boolean), 4, 3 - Block 2:Block: - Variable(0, Integer) = Store Integer(1) - Jump(1) - Block 3:Block: - Variable(1, Integer) = Store Integer(2) - Call id(3), args( Variable(0, Integer), Pointer, ) - Return - Block 4:Block: - Variable(6, Integer) = BitwiseOr Variable(0, Integer), Integer(2) - Variable(0, Integer) = Store Variable(6, Integer) - Jump(3)"#]], + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Variable(1, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(2, Boolean) = Call id(2), args( Result(0), ) + Variable(3, Boolean) = Store Variable(2, Boolean) + Branch Variable(3, Boolean), 2, 1 + Block 1:Block: + Variable(1, Integer) = Store Integer(1) + Call id(1), args( Qubit(0), Result(1), ) + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Store Variable(4, Boolean) + Branch Variable(5, Boolean), 4, 3 + Block 2:Block: + Variable(0, Integer) = Store Integer(1) + Jump(1) + Block 3:Block: + Variable(1, Integer) = Store Integer(2) + Variable(7, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(7, Integer), Pointer, ) + Return + Block 4:Block: + Variable(6, Integer) = BitwiseOr Variable(0, Integer), Integer(2) + Variable(0, Integer) = Store Variable(6, Integer) + Jump(3)"#]], ); } @@ -372,7 +375,8 @@ fn large_loop_with_inner_if_completes_eval_and_transform() { &expect![[r#" Block: Variable(1, Integer) = Store Integer(100) - Call id(3), args( Variable(0, Integer), Pointer, ) + Variable(400, Integer) = Store Variable(0, Integer) + Call id(3), args( Variable(400, Integer), Pointer, ) Return"#]], ); } diff --git a/compiler/qsc_partial_eval/src/tests/operators.rs b/compiler/qsc_partial_eval/src/tests/operators.rs index f3f5e5e7f5..20eb1a3b48 100644 --- a/compiler/qsc_partial_eval/src/tests/operators.rs +++ b/compiler/qsc_partial_eval/src/tests/operators.rs @@ -72,7 +72,9 @@ fn leading_positive_unary_operator_does_not_generate_rir_instruction() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Call id(3), args( Variable(2, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Store Variable(3, Integer) + Call id(3), args( Variable(4, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -146,8 +148,10 @@ fn leading_negative_unary_operator_generates_rir_instruction() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Mul Integer(-1), Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Mul Integer(-1), Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -221,8 +225,10 @@ fn bitwise_not_unary_operator_generates_rir_instruction() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = BitwiseNot Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = BitwiseNot Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -294,8 +300,10 @@ fn logical_not_unary_operator_generates_logical_not_rir_instruction() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Variable(2, Boolean) = LogicalNot Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = LogicalNot Variable(2, Boolean) + Variable(4, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(4, Boolean), Pointer, ) Return"#]], ); } @@ -366,7 +374,8 @@ fn comparing_measurement_results_for_equality_adds_read_result_and_comparison_in Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Call id(2), args( Result(1), ) Variable(2, Boolean) = Icmp Eq, Variable(0, Boolean), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); assert_eq!(program.num_qubits, 2); @@ -439,7 +448,8 @@ fn comparing_measurement_results_for_inequality_adds_read_result_and_comparison_ Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Call id(2), args( Result(1), ) Variable(2, Boolean) = Icmp Ne, Variable(0, Boolean), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); assert_eq!(program.num_qubits, 2); @@ -510,7 +520,8 @@ fn comparing_measurement_result_against_result_literal_for_equality_adds_read_re Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) Return"#]], ); assert_eq!(program.num_qubits, 1); @@ -581,7 +592,8 @@ fn comparing_measurement_result_against_result_literal_for_inequality_adds_read_ Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Icmp Ne, Variable(0, Boolean), Bool(true) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) Return"#]], ); assert_eq!(program.num_qubits, 1); @@ -651,8 +663,10 @@ fn comparing_lhs_classical_boolean_against_rhs_dynamic_boolean_for_equality() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Icmp Eq, Bool(false), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Icmp Eq, Bool(false), Variable(2, Boolean) + Variable(4, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(4, Boolean), Pointer, ) Return"#]], ); } @@ -715,16 +729,17 @@ fn comparing_lhs_dynamic_boolean_against_rhs_dynamic_boolean_for_equality() { &program, BlockId(0), &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Call id(1), args( Qubit(0), Result(1), ) - Variable(2, Boolean) = Call id(2), args( Result(1), ) - Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) - Variable(4, Boolean) = Icmp Eq, Variable(1, Boolean), Variable(3, Boolean) - Call id(3), args( Variable(4, Boolean), Pointer, ) - Return"#]], + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Call id(1), args( Qubit(0), Result(1), ) + Variable(2, Boolean) = Call id(2), args( Result(1), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) + Variable(4, Boolean) = Icmp Eq, Variable(1, Boolean), Variable(3, Boolean) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) + Return"#]], ); } @@ -791,8 +806,10 @@ fn comparing_lhs_classical_boolean_against_rhs_dynamic_boolean_for_inequality() Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Variable(2, Boolean) = Icmp Ne, Bool(true), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Icmp Ne, Bool(true), Variable(2, Boolean) + Variable(4, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(4, Boolean), Pointer, ) Return"#]], ); } @@ -855,16 +872,17 @@ fn comparing_lhs_dynamic_boolean_against_rhs_dynamic_boolean_for_inequality() { &program, BlockId(0), &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Call id(1), args( Qubit(0), Result(1), ) - Variable(2, Boolean) = Call id(2), args( Result(1), ) - Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) - Variable(4, Boolean) = Icmp Ne, Variable(1, Boolean), Variable(3, Boolean) - Call id(3), args( Variable(4, Boolean), Pointer, ) - Return"#]], + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Call id(1), args( Qubit(0), Result(1), ) + Variable(2, Boolean) = Call id(2), args( Result(1), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(false) + Variable(4, Boolean) = Icmp Ne, Variable(1, Boolean), Variable(3, Boolean) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) + Return"#]], ); } @@ -931,7 +949,9 @@ fn logical_and_with_lhs_classical_true_is_optimized_as_store() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); } @@ -999,6 +1019,7 @@ fn logical_and_with_lhs_classical_false_short_circuits_evaluation() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) + Variable(2, Boolean) = Store Variable(1, Boolean) Call id(3), args( Bool(false), Pointer, ) Return"#]], ); @@ -1069,7 +1090,8 @@ fn logical_and_with_dynamic_lhs_and_dynamic_rhs_short_circuits_when_lhs_is_false Variable(2, Boolean) = Store Bool(false) Branch Variable(1, Boolean), 2, 1 Block 1:Block: - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(5, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Call id(1), args( Qubit(0), Result(1), ) @@ -1143,6 +1165,7 @@ fn logical_or_with_lhs_classical_true_short_circuits_evaluation() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) + Variable(2, Boolean) = Store Variable(1, Boolean) Call id(3), args( Bool(true), Pointer, ) Return"#]], ); @@ -1211,7 +1234,9 @@ fn logical_or_with_lhs_classical_false_is_optimized_as_store() { Call id(1), args( Qubit(0), Result(0), ) Variable(0, Boolean) = Call id(2), args( Result(0), ) Variable(1, Boolean) = Store Variable(0, Boolean) - Call id(3), args( Variable(1, Boolean), Pointer, ) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(3, Boolean), Pointer, ) Return"#]], ); } @@ -1281,7 +1306,8 @@ fn logical_or_with_dynamic_lhs_and_dynamic_rhs_short_circuits_when_rhs_is_true() Variable(2, Boolean) = Store Bool(true) Branch Variable(1, Boolean), 1, 2 Block 1:Block: - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(5, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Call id(1), args( Qubit(0), Result(1), ) @@ -1366,7 +1392,8 @@ fn logical_and_and_sequence_with_dynamic_operands() { Variable(2, Boolean) = Store Variable(4, Boolean) Jump(1) Block 3:Block: - Call id(3), args( Variable(5, Boolean), Pointer, ) + Variable(8, Boolean) = Store Variable(5, Boolean) + Call id(3), args( Variable(8, Boolean), Pointer, ) Return Block 4:Block: Call id(1), args( Qubit(2), Result(2), ) @@ -1451,7 +1478,8 @@ fn logical_and_or_sequence_with_dynamic_operands() { Variable(2, Boolean) = Store Variable(4, Boolean) Jump(1) Block 3:Block: - Call id(3), args( Variable(5, Boolean), Pointer, ) + Variable(8, Boolean) = Store Variable(5, Boolean) + Call id(3), args( Variable(8, Boolean), Pointer, ) Return Block 4:Block: Call id(1), args( Qubit(2), Result(2), ) @@ -1527,7 +1555,8 @@ fn logical_or_and_sequence_with_dynamic_operands() { Variable(2, Boolean) = Store Bool(true) Branch Variable(1, Boolean), 1, 2 Block 1:Block: - Call id(3), args( Variable(2, Boolean), Pointer, ) + Variable(8, Boolean) = Store Variable(2, Boolean) + Call id(3), args( Variable(8, Boolean), Pointer, ) Return Block 2:Block: Call id(1), args( Qubit(1), Result(1), ) @@ -1621,7 +1650,8 @@ fn logical_or_or_sequence_with_dynamic_operands() { Variable(2, Boolean) = Store Variable(4, Boolean) Jump(1) Block 3:Block: - Call id(3), args( Variable(5, Boolean), Pointer, ) + Variable(8, Boolean) = Store Variable(5, Boolean) + Call id(3), args( Variable(8, Boolean), Pointer, ) Return Block 4:Block: Call id(1), args( Qubit(2), Result(2), ) @@ -1697,8 +1727,10 @@ fn integer_add_with_lhs_classical_integer_and_rhs_dynamic_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Add Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Add Integer(1), Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -1774,8 +1806,10 @@ fn integer_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Sub Variable(2, Integer), Integer(1) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Sub Variable(3, Integer), Integer(1) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -1852,10 +1886,11 @@ fn integer_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(1), args( Qubit(0), Result(1), ) - Variable(3, Boolean) = Call id(2), args( Result(1), ) - Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) - Branch Variable(4, Boolean), 5, 6 + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 Block 2:Block: Variable(2, Integer) = Store Integer(0) Jump(1) @@ -1863,14 +1898,16 @@ fn integer_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { Variable(2, Integer) = Store Integer(1) Jump(1) Block 4:Block: - Variable(6, Integer) = Mul Variable(2, Integer), Variable(5, Integer) - Call id(3), args( Variable(6, Integer), Pointer, ) + Variable(7, Integer) = Store Variable(6, Integer) + Variable(8, Integer) = Mul Variable(3, Integer), Variable(7, Integer) + Variable(9, Integer) = Store Variable(8, Integer) + Call id(3), args( Variable(9, Integer), Pointer, ) Return Block 5:Block: - Variable(5, Integer) = Store Integer(1) + Variable(6, Integer) = Store Integer(1) Jump(4) Block 6:Block: - Variable(5, Integer) = Store Integer(0) + Variable(6, Integer) = Store Integer(0) Jump(4)"#]], ); } @@ -1940,8 +1977,10 @@ fn integer_div_with_lhs_classical_integer_and_rhs_dynamic_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Sdiv Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Sdiv Integer(1), Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2039,8 +2078,10 @@ fn integer_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Srem Variable(2, Integer), Integer(1) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Srem Variable(3, Integer), Integer(1) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2161,8 +2202,10 @@ fn integer_exponentiation_with_lhs_dynamic_integer_and_rhs_classical_zero_intege Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Store Integer(1) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Store Integer(1) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2238,11 +2281,13 @@ fn integer_exponentiation_with_lhs_dynamic_integer_and_rhs_classical_positive_in Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Store Integer(1) - Variable(4, Integer) = Mul Variable(3, Integer), Variable(2, Integer) - Variable(5, Integer) = Mul Variable(4, Integer), Variable(2, Integer) - Variable(6, Integer) = Mul Variable(5, Integer), Variable(2, Integer) - Call id(3), args( Variable(6, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Store Integer(1) + Variable(5, Integer) = Mul Variable(4, Integer), Variable(3, Integer) + Variable(6, Integer) = Mul Variable(5, Integer), Variable(3, Integer) + Variable(7, Integer) = Mul Variable(6, Integer), Variable(3, Integer) + Variable(8, Integer) = Store Variable(7, Integer) + Call id(3), args( Variable(8, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2342,10 +2387,11 @@ fn integer_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(1), args( Qubit(0), Result(1), ) - Variable(3, Boolean) = Call id(2), args( Result(1), ) - Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) - Branch Variable(4, Boolean), 5, 6 + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 Block 2:Block: Variable(2, Integer) = Store Integer(0) Jump(1) @@ -2353,14 +2399,16 @@ fn integer_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { Variable(2, Integer) = Store Integer(1) Jump(1) Block 4:Block: - Variable(6, Integer) = BitwiseAnd Variable(2, Integer), Variable(5, Integer) - Call id(3), args( Variable(6, Integer), Pointer, ) + Variable(7, Integer) = Store Variable(6, Integer) + Variable(8, Integer) = BitwiseAnd Variable(3, Integer), Variable(7, Integer) + Variable(9, Integer) = Store Variable(8, Integer) + Call id(3), args( Variable(9, Integer), Pointer, ) Return Block 5:Block: - Variable(5, Integer) = Store Integer(1) + Variable(6, Integer) = Store Integer(1) Jump(4) Block 6:Block: - Variable(5, Integer) = Store Integer(0) + Variable(6, Integer) = Store Integer(0) Jump(4)"#]], ); } @@ -2430,8 +2478,10 @@ fn integer_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = BitwiseOr Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = BitwiseOr Integer(1), Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2507,8 +2557,10 @@ fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() { Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = BitwiseXor Variable(2, Integer), Integer(1) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = BitwiseXor Variable(3, Integer), Integer(1) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2585,10 +2637,11 @@ fn integer_bitwise_left_shif_with_lhs_dynamic_integer_and_rhs_dynamic_integer() Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(1), args( Qubit(0), Result(1), ) - Variable(3, Boolean) = Call id(2), args( Result(1), ) - Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) - Branch Variable(4, Boolean), 5, 6 + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 Block 2:Block: Variable(2, Integer) = Store Integer(0) Jump(1) @@ -2596,14 +2649,16 @@ fn integer_bitwise_left_shif_with_lhs_dynamic_integer_and_rhs_dynamic_integer() Variable(2, Integer) = Store Integer(1) Jump(1) Block 4:Block: - Variable(6, Integer) = Shl Variable(2, Integer), Variable(5, Integer) - Call id(3), args( Variable(6, Integer), Pointer, ) + Variable(7, Integer) = Store Variable(6, Integer) + Variable(8, Integer) = Shl Variable(3, Integer), Variable(7, Integer) + Variable(9, Integer) = Store Variable(8, Integer) + Call id(3), args( Variable(9, Integer), Pointer, ) Return Block 5:Block: - Variable(5, Integer) = Store Integer(1) + Variable(6, Integer) = Store Integer(1) Jump(4) Block 6:Block: - Variable(5, Integer) = Store Integer(0) + Variable(6, Integer) = Store Integer(0) Jump(4)"#]], ); } @@ -2673,8 +2728,10 @@ fn integer_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic_intege Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Integer) = Ashr Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Integer), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Ashr Integer(1), Variable(3, Integer) + Variable(5, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(5, Integer), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2750,8 +2807,10 @@ fn integer_equality_comparison_with_lhs_dynamic_integer_and_rhs_classical_intege Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Boolean) = Icmp Eq, Variable(2, Integer), Integer(1) - Call id(3), args( Variable(3, Boolean), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Boolean) = Icmp Eq, Variable(3, Integer), Integer(1) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2828,10 +2887,11 @@ fn integer_inequality_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_intege Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(1), args( Qubit(0), Result(1), ) - Variable(3, Boolean) = Call id(2), args( Result(1), ) - Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) - Branch Variable(4, Boolean), 5, 6 + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 Block 2:Block: Variable(2, Integer) = Store Integer(0) Jump(1) @@ -2839,14 +2899,16 @@ fn integer_inequality_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_intege Variable(2, Integer) = Store Integer(1) Jump(1) Block 4:Block: - Variable(6, Boolean) = Icmp Ne, Variable(2, Integer), Variable(5, Integer) - Call id(3), args( Variable(6, Boolean), Pointer, ) + Variable(7, Integer) = Store Variable(6, Integer) + Variable(8, Boolean) = Icmp Ne, Variable(3, Integer), Variable(7, Integer) + Variable(9, Boolean) = Store Variable(8, Boolean) + Call id(3), args( Variable(9, Boolean), Pointer, ) Return Block 5:Block: - Variable(5, Integer) = Store Integer(1) + Variable(6, Integer) = Store Integer(1) Jump(4) Block 6:Block: - Variable(5, Integer) = Store Integer(0) + Variable(6, Integer) = Store Integer(0) Jump(4)"#]], ); } @@ -2916,8 +2978,10 @@ fn integer_greater_than_comparison_with_lhs_classical_integer_and_rhs_dynamic_in Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Boolean) = Icmp Sgt, Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Boolean), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Boolean) = Icmp Sgt, Integer(1), Variable(3, Integer) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -2993,8 +3057,10 @@ fn integer_greater_or_equal_than_comparison_with_lhs_dynamic_integer_and_rhs_cla Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Boolean) = Icmp Sge, Variable(2, Integer), Integer(1) - Call id(3), args( Variable(3, Boolean), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Boolean) = Icmp Sge, Variable(3, Integer), Integer(1) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) @@ -3071,10 +3137,11 @@ fn integer_less_than_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_integer Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) Call id(1), args( Qubit(0), Result(1), ) - Variable(3, Boolean) = Call id(2), args( Result(1), ) - Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) - Branch Variable(4, Boolean), 5, 6 + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 Block 2:Block: Variable(2, Integer) = Store Integer(0) Jump(1) @@ -3082,14 +3149,16 @@ fn integer_less_than_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_integer Variable(2, Integer) = Store Integer(1) Jump(1) Block 4:Block: - Variable(6, Boolean) = Icmp Slt, Variable(2, Integer), Variable(5, Integer) - Call id(3), args( Variable(6, Boolean), Pointer, ) + Variable(7, Integer) = Store Variable(6, Integer) + Variable(8, Boolean) = Icmp Slt, Variable(3, Integer), Variable(7, Integer) + Variable(9, Boolean) = Store Variable(8, Boolean) + Call id(3), args( Variable(9, Boolean), Pointer, ) Return Block 5:Block: - Variable(5, Integer) = Store Integer(1) + Variable(6, Integer) = Store Integer(1) Jump(4) Block 6:Block: - Variable(5, Integer) = Store Integer(0) + Variable(6, Integer) = Store Integer(0) Jump(4)"#]], ); } @@ -3159,8 +3228,10 @@ fn integer_less_or_equal_than_comparison_with_lhs_classical_integer_and_rhs_dyna Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) Branch Variable(1, Boolean), 2, 3 Block 1:Block: - Variable(3, Boolean) = Icmp Sle, Integer(1), Variable(2, Integer) - Call id(3), args( Variable(3, Boolean), Pointer, ) + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Boolean) = Icmp Sle, Integer(1), Variable(3, Integer) + Variable(5, Boolean) = Store Variable(4, Boolean) + Call id(3), args( Variable(5, Boolean), Pointer, ) Return Block 2:Block: Variable(2, Integer) = Store Integer(0) diff --git a/compiler/qsc_rir/src/passes/ssa_transform.rs b/compiler/qsc_rir/src/passes/ssa_transform.rs index 040adad607..1dffc3b1ac 100644 --- a/compiler/qsc_rir/src/passes/ssa_transform.rs +++ b/compiler/qsc_rir/src/passes/ssa_transform.rs @@ -172,7 +172,10 @@ fn map_variable_use_in_block(block: &mut Block, var_map: &mut FxHashMap { - var_map.insert(var.variable_id, *operand); + // Note this uses the mapped operand to make sure this variable points to whatever root literal or variable + // this operand corresponds to at this point in the block. This makes the new variable respect a point-in-time + // copy of the operand. + var_map.insert(var.variable_id, operand.mapped(var_map)); continue; } @@ -181,7 +184,7 @@ fn map_variable_use_in_block(block: &mut Block, var_map: &mut FxHashMap var.map_to_operand(var_map), + Operand::Variable(var) => *var_map.get(&var.variable_id).unwrap_or(arg), Operand::Literal(_) => *arg, }) .collect(); diff --git a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs index f6e9c158a9..46fd6ba2fb 100644 --- a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs +++ b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs @@ -2493,3 +2493,156 @@ fn ssa_transform_maps_store_with_variable_from_store_in_conditional_to_phi_node( num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } + +#[test] +fn ssa_transform_allows_point_in_time_copy_of_dynamic_variable() { + let mut program = new_program(); + program.callables.insert( + CallableId(1), + Callable { + name: "dynamic_bool".to_string(), + input_type: Vec::new(), + output_type: Some(Ty::Boolean), + body: None, + call_type: CallableType::Regular, + }, + ); + + program.blocks.insert( + BlockId(0), + Block(vec![ + Instruction::Call( + CallableId(1), + Vec::new(), + Some(Variable { + variable_id: VariableId(0), + ty: Ty::Boolean, + }), + ), + Instruction::Store( + Operand::Variable(Variable { + variable_id: VariableId(0), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(1), + ty: Ty::Boolean, + }, + ), + Instruction::Store( + Operand::Variable(Variable { + variable_id: VariableId(1), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(2), + ty: Ty::Boolean, + }, + ), + Instruction::LogicalNot( + Operand::Variable(Variable { + variable_id: VariableId(1), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(3), + ty: Ty::Boolean, + }, + ), + Instruction::Store( + Operand::Variable(Variable { + variable_id: VariableId(3), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(1), + ty: Ty::Boolean, + }, + ), + Instruction::LogicalNot( + Operand::Variable(Variable { + variable_id: VariableId(2), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(4), + ty: Ty::Boolean, + }, + ), + Instruction::LogicalNot( + Operand::Variable(Variable { + variable_id: VariableId(1), + ty: Ty::Boolean, + }), + Variable { + variable_id: VariableId(5), + ty: Ty::Boolean, + }, + ), + Instruction::Return, + ]), + ); + + // Before + expect![[r#" + Program: + entry: 0 + callables: + Callable 0: Callable: + name: main + call_type: Regular + input_type: + output_type: + body: 0 + Callable 1: Callable: + name: dynamic_bool + call_type: Regular + input_type: + output_type: Boolean + body: + blocks: + Block 0: Block: + Variable(0, Boolean) = Call id(1), args( ) + Variable(1, Boolean) = Store Variable(0, Boolean) + Variable(2, Boolean) = Store Variable(1, Boolean) + Variable(3, Boolean) = LogicalNot Variable(1, Boolean) + Variable(1, Boolean) = Store Variable(3, Boolean) + Variable(4, Boolean) = LogicalNot Variable(2, Boolean) + Variable(5, Boolean) = LogicalNot Variable(1, Boolean) + Return + config: Config: + capabilities: Base + num_qubits: 0 + num_results: 0"#]] + .assert_eq(&program.to_string()); + + // After + transform_program(&mut program); + expect![[r#" + Program: + entry: 0 + callables: + Callable 0: Callable: + name: main + call_type: Regular + input_type: + output_type: + body: 0 + Callable 1: Callable: + name: dynamic_bool + call_type: Regular + input_type: + output_type: Boolean + body: + blocks: + Block 0: Block: + Variable(0, Boolean) = Call id(1), args( ) + Variable(3, Boolean) = LogicalNot Variable(0, Boolean) + Variable(4, Boolean) = LogicalNot Variable(0, Boolean) + Variable(5, Boolean) = LogicalNot Variable(3, Boolean) + Return + config: Config: + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) + num_qubits: 0 + num_results: 0"#]].assert_eq(&program.to_string()); +} diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll index 03607bf23f..8af7bfa295 100644 --- a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll +++ b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll @@ -10,13 +10,13 @@ block_0: %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) - %var_2 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 1 to %Result*)) + %var_4 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 1 to %Result*)) br i1 %var_0, label %block_1, label %block_2 block_1: call void @__quantum__qis__z__body(%Qubit* inttoptr (i64 0 to %Qubit*)) br label %block_2 block_2: - br i1 %var_2, label %block_3, label %block_4 + br i1 %var_4, label %block_3, label %block_4 block_3: call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*)) br label %block_4 @@ -26,22 +26,22 @@ block_4: call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) - %var_5 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 2 to %Result*)) + %var_9 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 2 to %Result*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) - %var_8 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) + %var_13 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__bool_record_output(i1 %var_0, i8* null) - call void @__quantum__rt__bool_record_output(i1 %var_2, i8* null) + call void @__quantum__rt__bool_record_output(i1 %var_4, i8* null) call void @__quantum__rt__tuple_record_output(i64 2, i8* null) - call void @__quantum__rt__bool_record_output(i1 %var_5, i8* null) - call void @__quantum__rt__bool_record_output(i1 %var_8, i8* null) + call void @__quantum__rt__bool_record_output(i1 %var_9, i8* null) + call void @__quantum__rt__bool_record_output(i1 %var_13, i8* null) ret void } diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll index 2d136271f9..79aea49eb5 100644 --- a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll +++ b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll @@ -47,15 +47,15 @@ block_6: block_7: br label %block_9 block_8: - %var_81 = phi i1 [true, %block_5], [false, %block_6] + %var_87 = phi i1 [true, %block_5], [false, %block_6] br label %block_9 block_9: - %var_82 = phi i1 [true, %block_7], [%var_81, %block_8] - br i1 %var_82, label %block_10, label %block_11 + %var_88 = phi i1 [true, %block_7], [%var_87, %block_8] + br i1 %var_88, label %block_10, label %block_11 block_10: br label %block_11 block_11: - %var_83 = phi i64 [0, %block_9], [1, %block_10] + %var_89 = phi i64 [0, %block_9], [1, %block_10] call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 2 to %Qubit*)) @@ -74,14 +74,14 @@ block_11: call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) - %var_25 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 2 to %Result*)) - br i1 %var_25, label %block_12, label %block_13 + %var_26 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 2 to %Result*)) + br i1 %var_26, label %block_12, label %block_13 block_12: - %var_27 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) - br i1 %var_27, label %block_14, label %block_15 + %var_28 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) + br i1 %var_28, label %block_14, label %block_15 block_13: - %var_29 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) - br i1 %var_29, label %block_16, label %block_17 + %var_30 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 3 to %Result*)) + br i1 %var_30, label %block_16, label %block_17 block_14: call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) br label %block_18 @@ -96,16 +96,16 @@ block_17: block_18: br label %block_20 block_19: - %var_84 = phi i1 [true, %block_16], [false, %block_17] + %var_90 = phi i1 [true, %block_16], [false, %block_17] br label %block_20 block_20: - %var_85 = phi i1 [true, %block_18], [%var_84, %block_19] - br i1 %var_85, label %block_21, label %block_22 + %var_91 = phi i1 [true, %block_18], [%var_90, %block_19] + br i1 %var_91, label %block_21, label %block_22 block_21: - %var_31 = add i64 %var_83, 1 + %var_33 = add i64 %var_89, 1 br label %block_22 block_22: - %var_86 = phi i64 [%var_83, %block_20], [%var_31, %block_21] + %var_92 = phi i64 [%var_89, %block_20], [%var_33, %block_21] call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 2 to %Qubit*)) @@ -124,14 +124,14 @@ block_22: call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 4 to %Result*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 5 to %Result*)) - %var_40 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 4 to %Result*)) - br i1 %var_40, label %block_23, label %block_24 + %var_42 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 4 to %Result*)) + br i1 %var_42, label %block_23, label %block_24 block_23: - %var_42 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 5 to %Result*)) - br i1 %var_42, label %block_25, label %block_26 -block_24: %var_44 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 5 to %Result*)) - br i1 %var_44, label %block_27, label %block_28 + br i1 %var_44, label %block_25, label %block_26 +block_24: + %var_46 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 5 to %Result*)) + br i1 %var_46, label %block_27, label %block_28 block_25: call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) br label %block_29 @@ -146,16 +146,16 @@ block_28: block_29: br label %block_31 block_30: - %var_87 = phi i1 [true, %block_27], [false, %block_28] + %var_93 = phi i1 [true, %block_27], [false, %block_28] br label %block_31 block_31: - %var_88 = phi i1 [true, %block_29], [%var_87, %block_30] - br i1 %var_88, label %block_32, label %block_33 + %var_94 = phi i1 [true, %block_29], [%var_93, %block_30] + br i1 %var_94, label %block_32, label %block_33 block_32: - %var_46 = add i64 %var_86, 1 + %var_49 = add i64 %var_92, 1 br label %block_33 block_33: - %var_89 = phi i64 [%var_86, %block_31], [%var_46, %block_32] + %var_95 = phi i64 [%var_92, %block_31], [%var_49, %block_32] call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 2 to %Qubit*)) @@ -174,14 +174,14 @@ block_33: call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 6 to %Result*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 7 to %Result*)) - %var_55 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 6 to %Result*)) - br i1 %var_55, label %block_34, label %block_35 + %var_58 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 6 to %Result*)) + br i1 %var_58, label %block_34, label %block_35 block_34: - %var_57 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 7 to %Result*)) - br i1 %var_57, label %block_36, label %block_37 + %var_60 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 7 to %Result*)) + br i1 %var_60, label %block_36, label %block_37 block_35: - %var_59 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 7 to %Result*)) - br i1 %var_59, label %block_38, label %block_39 + %var_62 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 7 to %Result*)) + br i1 %var_62, label %block_38, label %block_39 block_36: call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) br label %block_40 @@ -196,16 +196,16 @@ block_39: block_40: br label %block_42 block_41: - %var_90 = phi i1 [true, %block_38], [false, %block_39] + %var_96 = phi i1 [true, %block_38], [false, %block_39] br label %block_42 block_42: - %var_91 = phi i1 [true, %block_40], [%var_90, %block_41] - br i1 %var_91, label %block_43, label %block_44 + %var_97 = phi i1 [true, %block_40], [%var_96, %block_41] + br i1 %var_97, label %block_43, label %block_44 block_43: - %var_61 = add i64 %var_89, 1 + %var_65 = add i64 %var_95, 1 br label %block_44 block_44: - %var_92 = phi i64 [%var_89, %block_42], [%var_61, %block_43] + %var_98 = phi i64 [%var_95, %block_42], [%var_65, %block_43] call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__rx__body(double 1.5707963267948966, %Qubit* inttoptr (i64 2 to %Qubit*)) @@ -224,14 +224,14 @@ block_44: call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 8 to %Result*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 9 to %Result*)) - %var_70 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 8 to %Result*)) - br i1 %var_70, label %block_45, label %block_46 + %var_74 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 8 to %Result*)) + br i1 %var_74, label %block_45, label %block_46 block_45: - %var_72 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 9 to %Result*)) - br i1 %var_72, label %block_47, label %block_48 + %var_76 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 9 to %Result*)) + br i1 %var_76, label %block_47, label %block_48 block_46: - %var_74 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 9 to %Result*)) - br i1 %var_74, label %block_49, label %block_50 + %var_78 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 9 to %Result*)) + br i1 %var_78, label %block_49, label %block_50 block_47: call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) br label %block_51 @@ -246,26 +246,26 @@ block_50: block_51: br label %block_53 block_52: - %var_93 = phi i1 [true, %block_49], [false, %block_50] + %var_99 = phi i1 [true, %block_49], [false, %block_50] br label %block_53 block_53: - %var_94 = phi i1 [true, %block_51], [%var_93, %block_52] - br i1 %var_94, label %block_54, label %block_55 + %var_100 = phi i1 [true, %block_51], [%var_99, %block_52] + br i1 %var_100, label %block_54, label %block_55 block_54: - %var_76 = add i64 %var_92, 1 + %var_81 = add i64 %var_98, 1 br label %block_55 block_55: - %var_95 = phi i64 [%var_92, %block_53], [%var_76, %block_54] + %var_101 = phi i64 [%var_98, %block_53], [%var_81, %block_54] call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 10 to %Result*)) - %var_77 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 10 to %Result*)) + %var_82 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 10 to %Result*)) call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__rt__tuple_record_output(i64 2, i8* null) - call void @__quantum__rt__bool_record_output(i1 %var_77, i8* null) - call void @__quantum__rt__int_record_output(i64 %var_95, i8* null) + call void @__quantum__rt__bool_record_output(i1 %var_82, i8* null) + call void @__quantum__rt__int_record_output(i64 %var_101, i8* null) ret void } From f57da20b8cde1ccd17f311b4d1de52902f70f413 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 7 Jan 2025 13:18:44 -0800 Subject: [PATCH 2/2] PR Feedback --- compiler/qsc_partial_eval/src/lib.rs | 45 ++++++++++++-------- compiler/qsc_rir/src/passes/ssa_transform.rs | 6 ++- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs index 741fb13038..3fa2fd7339 100644 --- a/compiler/qsc_partial_eval/src/lib.rs +++ b/compiler/qsc_partial_eval/src/lib.rs @@ -51,7 +51,7 @@ use qsc_rir::{ builder, rir::{ self, Callable, CallableId, CallableType, ConditionCode, Instruction, Literal, Operand, - Program, + Program, VariableId, }, }; use rustc_hash::FxHashMap; @@ -1524,10 +1524,7 @@ impl<'a> PartialEvaluator<'a> { // Evaluate the body expression. // First, we cache the current static variable mappings so that we can restore them later. - let cached_mappings = self - .eval_context - .get_current_scope() - .clone_static_var_mappings(); + let cached_mappings = self.clone_current_static_var_map(); let if_true_branch_control_flow = self.eval_expr_if_branch(body_expr_id, continuation_block_node_id, maybe_if_expr_var)?; let if_true_block_id = match if_true_branch_control_flow { @@ -1538,14 +1535,9 @@ impl<'a> PartialEvaluator<'a> { // Evaluate the otherwise expression (if any), and determine the block to branch to if the condition is false. let if_false_block_id = if let Some(otherwise_expr_id) = otherwise_expr_id { // Cache the mappings after the true block so we can compare afterwards. - let post_if_true_mappings = self - .eval_context - .get_current_scope() - .clone_static_var_mappings(); + let post_if_true_mappings = self.clone_current_static_var_map(); // Restore the cached mappings from before evaluating the true block. - self.eval_context - .get_current_scope_mut() - .set_static_var_mappings(cached_mappings); + self.overwrite_current_static_var_map(cached_mappings); let if_false_branch_control_flow = self.eval_expr_if_branch( otherwise_expr_id, continuation_block_node_id, @@ -1553,9 +1545,7 @@ impl<'a> PartialEvaluator<'a> { )?; // Only keep the static mappings that are the same in both blocks; when they are different, // the variable is no longer static across the if expression. - self.eval_context - .get_current_scope_mut() - .keep_matching_static_var_mappings(&post_if_true_mappings); + self.keep_matching_static_var_mappings(&post_if_true_mappings); match if_false_branch_control_flow { BranchControlFlow::Block(block_id) => block_id, BranchControlFlow::Return(value) => return Ok(EvalControlFlow::Return(value)), @@ -1563,9 +1553,7 @@ impl<'a> PartialEvaluator<'a> { } else { // Only keep the static mappings that are the same after the true block as before; when they are different, // the variable is no longer static across the if expression. - self.eval_context - .get_current_scope_mut() - .keep_matching_static_var_mappings(&cached_mappings); + self.keep_matching_static_var_mappings(&cached_mappings); // Since there is no otherwise block, we branch to the continuation block. continuation_block_node_id @@ -2957,6 +2945,27 @@ impl<'a> PartialEvaluator<'a> { _ => panic!("{value} cannot be mapped to a RIR operand"), } } + + fn clone_current_static_var_map(&self) -> FxHashMap { + self.eval_context + .get_current_scope() + .clone_static_var_mappings() + } + + fn overwrite_current_static_var_map(&mut self, static_vars: FxHashMap) { + self.eval_context + .get_current_scope_mut() + .set_static_var_mappings(static_vars); + } + + fn keep_matching_static_var_mappings( + &mut self, + other_mappings: &FxHashMap, + ) { + self.eval_context + .get_current_scope_mut() + .keep_matching_static_var_mappings(other_mappings); + } } fn eval_un_op_with_literals(un_op: UnOp, value: Value) -> Value { diff --git a/compiler/qsc_rir/src/passes/ssa_transform.rs b/compiler/qsc_rir/src/passes/ssa_transform.rs index 1dffc3b1ac..463943e946 100644 --- a/compiler/qsc_rir/src/passes/ssa_transform.rs +++ b/compiler/qsc_rir/src/passes/ssa_transform.rs @@ -184,7 +184,11 @@ fn map_variable_use_in_block(block: &mut Block, var_map: &mut FxHashMap *var_map.get(&var.variable_id).unwrap_or(arg), + Operand::Variable(var) => { + // If the variable is not in the map, it is not something whose value has been updated via store in this block, + // so just fallback to use the `arg` value directly. + *var_map.get(&var.variable_id).unwrap_or(arg) + } Operand::Literal(_) => *arg, }) .collect();