Skip to content

Commit

Permalink
RCA check for use of dynamic rhs in exponent binop (#1539)
Browse files Browse the repository at this point in the history
This adds a special case to RCA for the rhs of exponentiation that adds
a `UseOfDynamicExponent` feature flag if appropriate. This can then be
used to tie dynamic exponentiation to backward branching (aka looping)
which is needed to support such exponentiation.
  • Loading branch information
swernli authored May 17, 2024
1 parent 83fe7cb commit b65a78f
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 16 deletions.
62 changes: 57 additions & 5 deletions compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use super::tests_common::{
LOOP_WITH_DYNAMIC_CONDITION, MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL,
RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION, USE_DYNAMICALLY_SIZED_ARRAY,
USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE, USE_DYNAMIC_FUNCTION,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_OPERATION, USE_DYNAMIC_PAULI,
USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT,
USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE, USE_ENTRY_POINT_STATIC_BIG_INT,
USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE, USE_ENTRY_POINT_STATIC_INT,
USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP, USE_DYNAMIC_OPERATION,
USE_DYNAMIC_PAULI, USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_RHS_EXP_BINOP,
USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE,
USE_ENTRY_POINT_STATIC_BIG_INT, USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE,
USE_ENTRY_POINT_STATIC_INT, USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_ENTRY_POINT_STATIC_RANGE, USE_ENTRY_POINT_STATIC_STRING,
};
use expect_test::{expect, Expect};
Expand Down Expand Up @@ -476,6 +476,58 @@ fn use_of_dynamic_index_yields_errors() {
);
}

#[test]
fn use_of_dynamic_lhs_exp_binop_yields_errors() {
check_profile(
USE_DYNAMIC_LHS_EXP_BINOP,
&expect![[r#"
[
UseOfDynamicInt(
Span {
lo: 104,
hi: 124,
},
),
UseOfDynamicInt(
Span {
lo: 138,
hi: 143,
},
),
]
"#]],
);
}

#[test]
fn use_of_dynamic_rhs_exp_binop_yields_errors() {
check_profile(
USE_DYNAMIC_RHS_EXP_BINOP,
&expect![[r#"
[
UseOfDynamicInt(
Span {
lo: 104,
hi: 124,
},
),
UseOfDynamicInt(
Span {
lo: 138,
hi: 143,
},
),
UseOfDynamicExponent(
Span {
lo: 138,
hi: 143,
},
),
]
"#]],
);
}

#[test]
fn return_within_dynamic_scope_yields_no_errors() {
check_profile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use super::tests_common::{
LOOP_WITH_DYNAMIC_CONDITION, MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL,
RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION, USE_DYNAMICALLY_SIZED_ARRAY,
USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE, USE_DYNAMIC_FUNCTION,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_OPERATION, USE_DYNAMIC_PAULI,
USE_DYNAMIC_QUBIT, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE,
USE_ENTRY_POINT_STATIC_BIG_INT, USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE,
USE_ENTRY_POINT_STATIC_INT, USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP, USE_DYNAMIC_OPERATION,
USE_DYNAMIC_PAULI, USE_DYNAMIC_QUBIT, USE_DYNAMIC_RHS_EXP_BINOP, USE_DYNAMIC_STRING,
USE_DYNAMIC_UDT, USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE, USE_ENTRY_POINT_STATIC_BIG_INT,
USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE, USE_ENTRY_POINT_STATIC_INT,
USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_ENTRY_POINT_STATIC_RANGE, USE_ENTRY_POINT_STATIC_STRING,
};
use expect_test::{expect, Expect};
Expand Down Expand Up @@ -404,6 +405,33 @@ fn use_of_dynamic_index_yields_errors() {
);
}

#[test]
fn use_of_dynamic_lhs_exp_binop_allowed() {
check_profile(
USE_DYNAMIC_LHS_EXP_BINOP,
&expect![[r#"
[]
"#]],
);
}

#[test]
fn use_of_dynamic_rhs_exp_binop_yields_errors() {
check_profile(
USE_DYNAMIC_RHS_EXP_BINOP,
&expect![[r#"
[
UseOfDynamicExponent(
Span {
lo: 138,
hi: 143,
},
),
]
"#]],
);
}

#[test]
fn return_within_dynamic_scope_yields_no_errors() {
check_profile(
Expand Down
74 changes: 69 additions & 5 deletions compiler/qsc_passes/src/capabilitiesck/tests_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use super::tests_common::{
LOOP_WITH_DYNAMIC_CONDITION, MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL,
RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION, USE_DYNAMICALLY_SIZED_ARRAY,
USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE, USE_DYNAMIC_FUNCTION,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_OPERATION, USE_DYNAMIC_PAULI,
USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT,
USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE, USE_ENTRY_POINT_STATIC_BIG_INT,
USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE, USE_ENTRY_POINT_STATIC_INT,
USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP, USE_DYNAMIC_OPERATION,
USE_DYNAMIC_PAULI, USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_RHS_EXP_BINOP,
USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, USE_ENTRY_POINT_INT_ARRAY_IN_TUPLE,
USE_ENTRY_POINT_STATIC_BIG_INT, USE_ENTRY_POINT_STATIC_BOOL, USE_ENTRY_POINT_STATIC_DOUBLE,
USE_ENTRY_POINT_STATIC_INT, USE_ENTRY_POINT_STATIC_INT_IN_TUPLE, USE_ENTRY_POINT_STATIC_PAULI,
USE_ENTRY_POINT_STATIC_RANGE, USE_ENTRY_POINT_STATIC_STRING,
};
use expect_test::{expect, Expect};
Expand Down Expand Up @@ -589,6 +589,70 @@ fn use_of_dynamic_index_yields_errors() {
);
}

#[test]
fn use_of_dynamic_lhs_exp_binop_yields_errors() {
check_profile(
USE_DYNAMIC_LHS_EXP_BINOP,
&expect![[r#"
[
UseOfDynamicBool(
Span {
lo: 104,
hi: 116,
},
),
UseOfDynamicBool(
Span {
lo: 138,
hi: 143,
},
),
UseOfDynamicInt(
Span {
lo: 138,
hi: 143,
},
),
]
"#]],
);
}

#[test]
fn use_of_dynamic_rhs_exp_binop_yields_errors() {
check_profile(
USE_DYNAMIC_RHS_EXP_BINOP,
&expect![[r#"
[
UseOfDynamicBool(
Span {
lo: 104,
hi: 116,
},
),
UseOfDynamicBool(
Span {
lo: 138,
hi: 143,
},
),
UseOfDynamicInt(
Span {
lo: 138,
hi: 143,
},
),
UseOfDynamicExponent(
Span {
lo: 138,
hi: 143,
},
),
]
"#]],
);
}

#[test]
fn return_within_dynamic_scope_yields_errors() {
check_profile(
Expand Down
18 changes: 18 additions & 0 deletions compiler/qsc_passes/src/capabilitiesck/tests_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,24 @@ pub const USE_DYNAMIC_INDEX: &str = r#"
}
}"#;

pub const USE_DYNAMIC_LHS_EXP_BINOP: &str = r#"
namespace Test {
operation Foo() : Unit {
use q = Qubit();
let i = M(q) == Zero ? 0 | 1;
i ^ 1;
}
}"#;

pub const USE_DYNAMIC_RHS_EXP_BINOP: &str = r#"
namespace Test {
operation Foo() : Unit {
use q = Qubit();
let i = M(q) == Zero ? 0 | 1;
1 ^ i;
}
}"#;

pub const RETURN_WITHIN_DYNAMIC_SCOPE: &str = r#"
namespace Test {
operation Foo() : Int {
Expand Down
36 changes: 34 additions & 2 deletions compiler/qsc_rca/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use qsc_data_structures::{functors::FunctorApp, index_map::IndexMap};
use qsc_fir::{
extensions::InputParam,
fir::{
Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId, ExprKind, Global,
Ident, Item, ItemKind, LocalVarId, Mutability, Package, PackageId, PackageLookup,
BinOp, Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId, ExprKind,
Global, Ident, Item, ItemKind, LocalVarId, Mutability, Package, PackageId, PackageLookup,
PackageStore, PackageStoreLookup, Pat, PatId, PatKind, Res, SpecDecl, SpecImpl, Stmt,
StmtId, StmtKind, StoreExprId, StoreItemId, StorePatId, StringComponent,
},
Expand Down Expand Up @@ -265,6 +265,35 @@ impl<'a> Analyzer<'a> {
compute_kind
}

fn analyze_expr_bin_op_exp(&mut self, lhs_expr_id: ExprId, rhs_expr_id: ExprId) -> ComputeKind {
// Visit the LHS and RHS expressions to determine their compute kind.
self.visit_expr(lhs_expr_id);
self.visit_expr(rhs_expr_id);

// The compute kind of a binary operator expression is the aggregation of its LHS and RHS expressions.
let application_instance = self.get_current_application_instance();
let lhs_compute_kind = *application_instance.get_expr_compute_kind(lhs_expr_id);
let rhs_compute_kind = *application_instance.get_expr_compute_kind(rhs_expr_id);
let mut compute_kind = ComputeKind::Classical;
compute_kind = compute_kind.aggregate(lhs_compute_kind);
compute_kind = compute_kind.aggregate(rhs_compute_kind);

// As a special case for exp, if the rhs is dynamic the expression gets an extra runtime feature.
// This is because we don't emit a native exponential binary operator but unroll it into a sequence
// of multiplications, which requires the rhs to be known at compile time.
if rhs_compute_kind.is_dynamic() {
compute_kind = compute_kind.aggregate_runtime_features(
ComputeKind::new_with_runtime_features(
RuntimeFeatureFlags::UseOfDynamicExponent,
ValueKind::Element(RuntimeKind::Static),
),
ValueKind::Element(RuntimeKind::Static),
);
}

compute_kind
}

fn analyze_expr_block(&mut self, block_id: BlockId) -> ComputeKind {
// Visit the block to determine its compute kind.
self.visit_block(block_id);
Expand Down Expand Up @@ -1639,6 +1668,9 @@ impl<'a> Visitor<'a> for Analyzer<'a> {
*index_expr_id,
*replacement_value_expr_id,
),
ExprKind::BinOp(BinOp::Exp, lhs_expr_id, rhs_expr_id) => {
self.analyze_expr_bin_op_exp(*lhs_expr_id, *rhs_expr_id)
}
ExprKind::BinOp(_, lhs_expr_id, rhs_expr_id) => {
self.analyze_expr_bin_op(*lhs_expr_id, *rhs_expr_id, &expr.ty)
}
Expand Down
10 changes: 10 additions & 0 deletions compiler/qsc_rca/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ pub enum Error {
#[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicString"))]
UseOfDynamicString(#[label] Span),

#[error("cannot use a dynamic exponent")]
#[diagnostic(help(
"using an exponent that depends on a measurement result is not supported by the current target"
))]
#[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicExponent"))]
UseOfDynamicExponent(#[label] Span),

#[error("cannot use a dynamically-sized array")]
#[diagnostic(help(
"using an array whose size depends on a measurement result is not supported by the current target"
Expand Down Expand Up @@ -191,6 +198,9 @@ pub fn generate_errors_from_runtime_features(
if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicString) {
errors.push(Error::UseOfDynamicString(span));
}
if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicExponent) {
errors.push(Error::UseOfDynamicExponent(span));
}
if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicallySizedArray) {
errors.push(Error::UseOfDynamicallySizedArray(span));
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/qsc_rca/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,8 @@ bitflags! {
const UseOfDoubleOutput = 1 << 23;
// Use of an `Int` as output of a computation.
const UseOfIntOutput = 1 << 24;
// Use of a dynamic exponent in a computation.
const UseOfDynamicExponent = 1 << 25;
}
}

Expand Down Expand Up @@ -871,6 +873,9 @@ impl RuntimeFeatureFlags {
if self.contains(RuntimeFeatureFlags::UseOfAdvancedOutput) {
capabilities |= TargetCapabilityFlags::HigherLevelConstructs;
}
if self.contains(RuntimeFeatureFlags::UseOfDynamicExponent) {
capabilities |= TargetCapabilityFlags::BackwardsBranching;
}
capabilities
}
}
Loading

0 comments on commit b65a78f

Please sign in to comment.