diff --git a/Cargo.lock b/Cargo.lock index 32e5a33635..b2864bcb43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,6 +1015,7 @@ version = "0.0.0" dependencies = [ "expect-test", "qsc_ast", + "qsc_data_structures", "qsc_frontend", "qsc_hir", "regex-lite", @@ -1068,7 +1069,6 @@ dependencies = [ name = "qsc_frontend" version = "0.0.0" dependencies = [ - "bitflags 2.4.2", "expect-test", "indoc", "library", diff --git a/compiler/qsc/benches/eval.rs b/compiler/qsc/benches/eval.rs index df4b54926d..bc4dceb3f2 100644 --- a/compiler/qsc/benches/eval.rs +++ b/compiler/qsc/benches/eval.rs @@ -5,10 +5,10 @@ allocator::assign_global!(); use criterion::{criterion_group, criterion_main, Criterion}; use indoc::indoc; -use qsc::{interpret::Interpreter, PackageType}; +use qsc::{interpret::Interpreter, PackageType, TargetCapabilityFlags}; use qsc_data_structures::language_features::LanguageFeatures; use qsc_eval::output::GenericReceiver; -use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::SourceMap; const TELEPORT: &str = include_str!("../../../samples/algorithms/Teleportation.qs"); const DEUTSCHJOZSA: &str = include_str!("../../../samples/algorithms/DeutschJozsa.qs"); diff --git a/compiler/qsc/benches/large.rs b/compiler/qsc/benches/large.rs index 5025108d5a..b89a20ee93 100644 --- a/compiler/qsc/benches/large.rs +++ b/compiler/qsc/benches/large.rs @@ -4,9 +4,12 @@ allocator::assign_global!(); use criterion::{criterion_group, criterion_main, Criterion}; -use qsc::compile::{self, compile}; +use qsc::{ + compile::{self, compile}, + TargetCapabilityFlags, +}; use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore, SourceMap}; use qsc_passes::PackageType; const INPUT: &str = include_str!("./large.qs"); diff --git a/compiler/qsc/benches/library.rs b/compiler/qsc/benches/library.rs index 3decd16bf8..e1b80b0058 100644 --- a/compiler/qsc/benches/library.rs +++ b/compiler/qsc/benches/library.rs @@ -4,8 +4,8 @@ allocator::assign_global!(); use criterion::{criterion_group, criterion_main, Criterion}; -use qsc::compile; -use qsc_frontend::compile::{PackageStore, TargetCapabilityFlags}; +use qsc::{compile, TargetCapabilityFlags}; +use qsc_frontend::compile::PackageStore; pub fn library(c: &mut Criterion) { c.bench_function("Core + Standard library compilation", |b| { diff --git a/compiler/qsc/benches/rca.rs b/compiler/qsc/benches/rca.rs index c9005f9aa5..0cbbfd7078 100644 --- a/compiler/qsc/benches/rca.rs +++ b/compiler/qsc/benches/rca.rs @@ -3,9 +3,9 @@ use criterion::{criterion_group, criterion_main, Criterion}; use qsc::incremental::Compiler; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_fir::fir::PackageStore; -use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap}; use qsc_lowerer::{map_hir_package_to_fir, Lowerer}; use qsc_passes::PackageType; use qsc_rca::{Analyzer, PackageStoreComputeProperties}; diff --git a/compiler/qsc/src/bin/memtest.rs b/compiler/qsc/src/bin/memtest.rs index 05cb61bf04..d7471b0771 100644 --- a/compiler/qsc/src/bin/memtest.rs +++ b/compiler/qsc/src/bin/memtest.rs @@ -4,7 +4,8 @@ //! Records the memory usage of the compiler. use qsc::{compile, CompileUnit}; -use qsc_frontend::compile::{PackageStore, TargetCapabilityFlags}; +use qsc_data_structures::target::TargetCapabilityFlags; +use qsc_frontend::compile::PackageStore; use std::{ alloc::{GlobalAlloc, Layout, System}, sync::atomic::{AtomicU64, Ordering}, diff --git a/compiler/qsc/src/bin/qsc.rs b/compiler/qsc/src/bin/qsc.rs index 91f3f8b139..83bd128a04 100644 --- a/compiler/qsc/src/bin/qsc.rs +++ b/compiler/qsc/src/bin/qsc.rs @@ -8,9 +8,9 @@ use log::info; use miette::{Context, IntoDiagnostic, Report}; use qsc::compile::compile; use qsc_codegen::qir_base; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_frontend::{ - compile::{PackageStore, SourceContents, SourceMap, SourceName, TargetCapabilityFlags}, + compile::{PackageStore, SourceContents, SourceMap, SourceName}, error::WithSource, }; use qsc_hir::hir::{Package, PackageId}; diff --git a/compiler/qsc/src/bin/qsi.rs b/compiler/qsc/src/bin/qsi.rs index c987012e49..1876787ca4 100644 --- a/compiler/qsc/src/bin/qsi.rs +++ b/compiler/qsc/src/bin/qsi.rs @@ -8,13 +8,13 @@ use miette::{Context, IntoDiagnostic, Report, Result}; use num_bigint::BigUint; use num_complex::Complex64; use qsc::interpret::{self, InterpretResult, Interpreter}; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_eval::{ output::{self, Receiver}, state::format_state_id, val::Value, }; -use qsc_frontend::compile::{SourceContents, SourceMap, SourceName, TargetCapabilityFlags}; +use qsc_frontend::compile::{SourceContents, SourceMap, SourceName}; use qsc_passes::PackageType; use qsc_project::{FileSystem, Manifest, StdFs}; use std::{ diff --git a/compiler/qsc/src/codegen.rs b/compiler/qsc/src/codegen.rs index d0d8b692e6..80b893b2cf 100644 --- a/compiler/qsc/src/codegen.rs +++ b/compiler/qsc/src/codegen.rs @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#[cfg(test)] +mod tests; + use qsc_codegen::qir::fir_to_qir; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore, SourceMap}; use qsc_partial_eval::ProgramEntry; use qsc_passes::{PackageType, PassContext}; +use qsc_rca::Analyzer; use crate::compile; @@ -30,14 +34,19 @@ pub fn get_qir( // Ensure it compiles before trying to add it to the store. if !errors.is_empty() { - // This should never happen, as the program should be checked for errors before trying to - // generate code for it. But just in case, simply report the failure. - return Err("Failed to generate QIR".to_string()); + // This will happen when QIR generation is attempted on a program that has errors. + // This can happen in the playground. + let mut error_message = + String::from("Failed to generate QIR. Could not compile sources.:\n"); + for error in errors { + error_message.push_str(&format!("{error}\n")); + } + + return Err(error_message); } let package_id = package_store.insert(unit); let (fir_store, fir_package_id) = qsc_passes::lower_hir_to_fir(&package_store, package_id); - let caps_results = PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities); let package = fir_store.get(fir_package_id); let entry = ProgramEntry { exec_graph: package.entry_exec_graph.clone(), @@ -49,16 +58,21 @@ pub fn get_qir( ) .into(), }; - // Ensure it compiles before trying to add it to the store. - match caps_results { - Ok(compute_properties) => { - fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry) - .map_err(|e| e.to_string()) - } - Err(_) => { - // This should never happen, as the program should be checked for errors before trying to - // generate code for it. But just in case, simply report the failure. - Err("Failed to generate QIR".to_string()) - } - } + + let compute_properties = if capabilities == TargetCapabilityFlags::empty() { + // baseprofchk already handled compliance, run the analyzer to get the compute properties. + let analyzer = Analyzer::init(&fir_store); + Ok(analyzer.analyze_all()) + } else { + PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities) + }; + + let Ok(compute_properties) = compute_properties else { + // This should never happen, as the program should be checked for errors before trying to + // generate code for it. But just in case, simply report the failure. + return Err("Failed to generate QIR. Could not generate compute properties.".to_string()); + }; + + fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry) + .map_err(|e| e.to_string()) } diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs new file mode 100644 index 0000000000..1c23e2f0c4 --- /dev/null +++ b/compiler/qsc/src/codegen/tests.rs @@ -0,0 +1,661 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use expect_test::expect; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::SourceMap; + +use crate::codegen::get_qir; + +#[test] +fn code_with_errors_returns_errors() { + let source = "namespace Test { + @EntryPoint() + operation Main() : Unit { + use q = Qubit() + let pi_over_two = 4.0 / 2.0; + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::empty(); + + expect![[r#" + Err( + "Failed to generate QIR. Could not compile sources.:\nsyntax error\n", + ) + "#]] + .assert_debug_eq(&get_qir(sources, language_features, capabilities)); +} + +mod base_profile { + use expect_test::expect; + use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; + use qsc_frontend::compile::SourceMap; + + use crate::codegen::get_qir; + + #[test] + fn simple() { + let source = "namespace Test { + open Microsoft.Quantum.Math; + open QIR.Intrinsic; + @EntryPoint() + operation Main() : Result { + use q = Qubit(); + let pi_over_two = 4.0 / 2.0; + __quantum__qis__rz__body(pi_over_two, q); + mutable some_angle = ArcSin(0.0); + __quantum__qis__rz__body(some_angle, q); + set some_angle = ArcCos(-1.0) / PI(); + __quantum__qis__rz__body(some_angle, q); + __quantum__qis__mresetz__body(q) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::empty(); + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__rz__body(double, %Qubit*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]] + .assert_eq(&qir); + } + + #[test] + fn qubit_reuse_triggers_reindexing() { + let source = "namespace Test { + @EntryPoint() + operation Main() : (Result, Result) { + use q = Qubit(); + (MResetZ(q), MResetZ(q)) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::empty(); + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]].assert_eq(&qir); + } + + #[test] + fn qubit_measurements_get_deferred() { + let source = "namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + X(q0); + let r0 = MResetZ(q0); + X(q1); + let r1 = MResetZ(q1); + [r0, r1] + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::empty(); + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__array_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__x__body(%Qubit*) + + declare void @__quantum__rt__array_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + "#]].assert_eq(&qir); + } +} + +mod adaptive_profile { + use expect_test::expect; + use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; + use qsc_frontend::compile::SourceMap; + + use crate::codegen::get_qir; + + #[test] + fn simple() { + let source = "namespace Test { + open Microsoft.Quantum.Math; + open QIR.Intrinsic; + @EntryPoint() + operation Main() : Result { + use q = Qubit(); + let pi_over_two = 4.0 / 2.0; + __quantum__qis__rz__body(pi_over_two, q); + mutable some_angle = ArcSin(0.0); + __quantum__qis__rz__body(some_angle, q); + set some_angle = ArcCos(-1.0) / PI(); + __quantum__qis__rz__body(some_angle, q); + __quantum__qis__mresetz__body(q) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__rz__body(double, %Qubit*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 false} + !5 = !{i32 1, !"classical_floats", i1 false} + !6 = !{i32 1, !"backwards_branching", i1 false} + !7 = !{i32 1, !"qubit_resetting", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]] + .assert_eq(&qir); + } + + #[test] + fn qubit_reuse_triggers_reindexing() { + let source = "namespace Test { + @EntryPoint() + operation Main() : (Result, Result) { + use q = Qubit(); + (MResetZ(q), MResetZ(q)) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 false} + !5 = !{i32 1, !"classical_floats", i1 false} + !6 = !{i32 1, !"backwards_branching", i1 false} + !7 = !{i32 1, !"qubit_resetting", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]].assert_eq(&qir); + } + + #[test] + fn qubit_measurements_not_deferred() { + let source = "namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + X(q0); + let r0 = MResetZ(q0); + X(q1); + let r1 = MResetZ(q1); + [r0, r1] + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__array_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__x__body(%Qubit*) + + declare void @__quantum__rt__array_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1 + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 false} + !5 = !{i32 1, !"classical_floats", i1 false} + !6 = !{i32 1, !"backwards_branching", i1 false} + !7 = !{i32 1, !"qubit_resetting", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]].assert_eq(&qir); + } +} + +mod quantinuum_profile { + use expect_test::expect; + use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; + use qsc_frontend::compile::SourceMap; + + use crate::codegen::get_qir; + + #[test] + fn simple() { + let source = "namespace Test { + open Microsoft.Quantum.Math; + open QIR.Intrinsic; + @EntryPoint() + operation Main() : Result { + use q = Qubit(); + let pi_over_two = 4.0 / 2.0; + __quantum__qis__rz__body(pi_over_two, q); + mutable some_angle = ArcSin(0.0); + __quantum__qis__rz__body(some_angle, q); + set some_angle = ArcCos(-1.0) / PI(); + __quantum__qis__rz__body(some_angle, q); + __quantum__qis__mresetz__body(q) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive + | TargetCapabilityFlags::QubitReset + | TargetCapabilityFlags::IntegerComputations; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__rz__body(double, %Qubit*) + + declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1 + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]] + .assert_eq(&qir); + } + + #[test] + fn qubit_reuse_allowed() { + let source = "namespace Test { + @EntryPoint() + operation Main() : (Result, Result) { + use q = Qubit(); + (MResetZ(q), MResetZ(q)) + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive + | TargetCapabilityFlags::QubitReset + | TargetCapabilityFlags::IntegerComputations; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__tuple_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1 + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]].assert_eq(&qir); + } + + #[test] + fn qubit_measurements_not_deferred() { + let source = "namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + X(q0); + let r0 = MResetZ(q0); + X(q1); + let r1 = MResetZ(q1); + [r0, r1] + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive + | TargetCapabilityFlags::QubitReset + | TargetCapabilityFlags::IntegerComputations; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__rt__array_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__x__body(%Qubit*) + + declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1 + + declare void @__quantum__rt__array_record_output(i64, i8*) + + declare void @__quantum__rt__result_record_output(%Result*, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]].assert_eq(&qir); + } + + #[test] + fn dynamic_integer_with_branch_and_phi_supported() { + let source = "namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + H(q); + MResetZ(q) == Zero ? 0 | 1 + } + }"; + let sources = SourceMap::new([("test.qs".into(), source.into())], None); + let language_features = LanguageFeatures::default(); + let capabilities = TargetCapabilityFlags::Adaptive + | TargetCapabilityFlags::QubitReset + | TargetCapabilityFlags::IntegerComputations; + + let qir = + get_qir(sources, language_features, capabilities).expect("Failed to generate QIR"); + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + 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 0 to %Result*)) + %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*)) + %var_1 = icmp eq i1 %var_0, false + br i1 %var_1, label %block_1, label %block_2 + block_1: + br label %block_3 + block_2: + br label %block_3 + block_3: + %var_2 = phi i64 [0, %block_1], [1, %block_2] + call void @__quantum__rt__integer_record_output(i64 %var_2, i8* null) + ret void + } + + declare void @__quantum__qis__h__body(%Qubit*) + + declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1 + + declare i1 @__quantum__qis__read_result__body(%Result*) + + declare void @__quantum__rt__integer_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + "#]].assert_eq(&qir); + } +} diff --git a/compiler/qsc/src/compile.rs b/compiler/qsc/src/compile.rs index 593ce37590..85d9958e89 100644 --- a/compiler/qsc/src/compile.rs +++ b/compiler/qsc/src/compile.rs @@ -2,9 +2,9 @@ // Licensed under the MIT License. use miette::{Diagnostic, Report}; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_frontend::{ - compile::{CompileUnit, PackageStore, SourceMap, TargetCapabilityFlags}, + compile::{CompileUnit, PackageStore, SourceMap}, error::WithSource, }; use qsc_hir::hir::PackageId; diff --git a/compiler/qsc/src/incremental.rs b/compiler/qsc/src/incremental.rs index a4473ce612..536affb1a4 100644 --- a/compiler/qsc/src/incremental.rs +++ b/compiler/qsc/src/incremental.rs @@ -3,10 +3,12 @@ use crate::compile::{self, compile, core, std}; use miette::Diagnostic; + use qsc_ast::ast; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; + use qsc_frontend::{ - compile::{OpenPackageStore, PackageStore, SourceMap, TargetCapabilityFlags}, + compile::{OpenPackageStore, PackageStore, SourceMap}, error::WithSource, incremental::Increment, }; diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs index b1f9dbceaf..ffbca883c7 100644 --- a/compiler/qsc/src/interpret.rs +++ b/compiler/qsc/src/interpret.rs @@ -46,6 +46,7 @@ use qsc_data_structures::{ language_features::LanguageFeatures, line_column::{Encoding, Range}, span::Span, + target::TargetCapabilityFlags, }; use qsc_eval::{ backend::{Backend, Chain as BackendChain, SparseSim}, @@ -58,7 +59,7 @@ use qsc_fir::{ visit::{self, Visitor}, }; use qsc_frontend::{ - compile::{CompileUnit, PackageStore, Source, SourceMap, TargetCapabilityFlags}, + compile::{CompileUnit, PackageStore, Source, SourceMap}, error::WithSource, incremental::Increment, }; diff --git a/compiler/qsc/src/interpret/debug/tests.rs b/compiler/qsc/src/interpret/debug/tests.rs index 3de86b7296..c8bba1efc0 100644 --- a/compiler/qsc/src/interpret/debug/tests.rs +++ b/compiler/qsc/src/interpret/debug/tests.rs @@ -5,9 +5,9 @@ use indoc::indoc; use miette::Result; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_eval::{output::CursorReceiver, val::Value}; -use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::SourceMap; use qsc_passes::PackageType; use std::io::Cursor; diff --git a/compiler/qsc/src/interpret/debugger_tests.rs b/compiler/qsc/src/interpret/debugger_tests.rs index d3e73953ae..93f236a3f8 100644 --- a/compiler/qsc/src/interpret/debugger_tests.rs +++ b/compiler/qsc/src/interpret/debugger_tests.rs @@ -8,7 +8,7 @@ use crate::line_column::Encoding; use qsc_data_structures::language_features::LanguageFeatures; use qsc_eval::{output::CursorReceiver, StepAction, StepResult}; use qsc_fir::fir::StmtId; -use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::SourceMap; use std::io::Cursor; fn get_breakpoint_ids(debugger: &Debugger, path: &str) -> Vec { @@ -123,6 +123,8 @@ mod given_debugger { }"#; #[cfg(test)] mod step { + use qsc_data_structures::target::TargetCapabilityFlags; + use super::*; #[test] diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs index 85cf65e778..796083a8a3 100644 --- a/compiler/qsc/src/interpret/tests.rs +++ b/compiler/qsc/src/interpret/tests.rs @@ -7,9 +7,9 @@ mod given_interpreter { use crate::interpret::{Error, InterpretResult, Interpreter}; use expect_test::Expect; use miette::Diagnostic; - use qsc_data_structures::language_features::LanguageFeatures; + use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_eval::{output::CursorReceiver, val::Value}; - use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags}; + use qsc_frontend::compile::SourceMap; use qsc_passes::PackageType; use std::{fmt::Write, io::Cursor, iter, str::from_utf8}; @@ -53,7 +53,6 @@ mod given_interpreter { mod without_sources { use expect_test::expect; use indoc::indoc; - use qsc_frontend::compile::TargetCapabilityFlags; use super::*; @@ -727,7 +726,9 @@ mod given_interpreter { true, SourceMap::default(), PackageType::Lib, - TargetCapabilityFlags::Adaptive, + TargetCapabilityFlags::Adaptive + | TargetCapabilityFlags::QubitReset + | TargetCapabilityFlags::IntegerComputations, LanguageFeatures::default(), ) .expect("interpreter should be created"); @@ -778,12 +779,19 @@ mod given_interpreter { ; module flags - !llvm.module.flags = !{!0, !1, !2, !3} + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} "#]] .assert_eq(&res); } @@ -794,7 +802,7 @@ mod given_interpreter { true, SourceMap::default(), PackageType::Lib, - TargetCapabilityFlags::Adaptive, + TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::QubitReset, LanguageFeatures::default(), ) .expect("interpreter should be created"); @@ -848,12 +856,19 @@ mod given_interpreter { ; module flags - !llvm.module.flags = !{!0, !1, !2, !3} + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"qubit_resetting", i1 true} + !5 = !{i32 1, !"classical_ints", i1 false} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} "#]] .assert_eq(&res); } @@ -1442,9 +1457,10 @@ mod given_interpreter { use crate::line_column::Encoding; use expect_test::expect; use indoc::indoc; + use qsc_ast::ast::{Expr, ExprKind, NodeId, Package, Path, Stmt, StmtKind, TopLevelNode}; use qsc_data_structures::span::Span; - use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags}; + use qsc_frontend::compile::SourceMap; use qsc_passes::PackageType; #[test] diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs index 7d48b480ac..4712f8159c 100644 --- a/compiler/qsc/src/lib.rs +++ b/compiler/qsc/src/lib.rs @@ -11,9 +11,7 @@ pub mod target; pub use qsc_formatter::formatter; -pub use qsc_frontend::compile::{ - CompileUnit, PackageStore, SourceContents, SourceMap, SourceName, TargetCapabilityFlags, -}; +pub use qsc_frontend::compile::{CompileUnit, PackageStore, SourceContents, SourceMap, SourceName}; pub mod resolve { pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res}; @@ -35,7 +33,9 @@ pub mod project { pub use qsc_project::{DirEntry, EntryType, FileSystem, Manifest, ManifestDescriptor}; } -pub use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; +pub use qsc_data_structures::{ + language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags, +}; pub use qsc_passes::{lower_hir_to_fir, PackageType, PassContext}; diff --git a/compiler/qsc/src/location.rs b/compiler/qsc/src/location.rs index 851c679fbd..dd3bde2b81 100644 --- a/compiler/qsc/src/location.rs +++ b/compiler/qsc/src/location.rs @@ -62,8 +62,9 @@ mod tests { use expect_test::expect; use qsc_data_structures::{ language_features::LanguageFeatures, line_column::Encoding, span::Span, + target::TargetCapabilityFlags, }; - use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags}; + use qsc_frontend::compile::{PackageStore, SourceMap}; use qsc_hir::hir::PackageId; use qsc_passes::PackageType; diff --git a/compiler/qsc/src/target.rs b/compiler/qsc/src/target.rs index f34de2557b..b1cd27c01d 100644 --- a/compiler/qsc/src/target.rs +++ b/compiler/qsc/src/target.rs @@ -3,7 +3,7 @@ use std::str::FromStr; -use qsc_frontend::compile::TargetCapabilityFlags; +use qsc_data_structures::target::TargetCapabilityFlags; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Profile { diff --git a/compiler/qsc_circuit/src/operations/tests.rs b/compiler/qsc_circuit/src/operations/tests.rs index 94254aba26..dcbd4604f3 100644 --- a/compiler/qsc_circuit/src/operations/tests.rs +++ b/compiler/qsc_circuit/src/operations/tests.rs @@ -3,8 +3,10 @@ use super::*; use expect_test::expect; -use qsc_data_structures::{functors::FunctorApp, language_features::LanguageFeatures}; -use qsc_frontend::compile::{compile, core, std, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{ + functors::FunctorApp, language_features::LanguageFeatures, target::TargetCapabilityFlags, +}; +use qsc_frontend::compile::{compile, core, std, PackageStore, SourceMap}; use qsc_hir::hir::{Item, ItemKind}; fn compile_one_operation(code: &str) -> (Item, String) { diff --git a/compiler/qsc_codegen/src/qir.rs b/compiler/qsc_codegen/src/qir.rs index 03085b0b81..568e7e733c 100644 --- a/compiler/qsc_codegen/src/qir.rs +++ b/compiler/qsc_codegen/src/qir.rs @@ -7,12 +7,12 @@ mod instruction_tests; #[cfg(test)] mod tests; -use qsc_frontend::compile::TargetCapabilityFlags; +use qsc_data_structures::target::TargetCapabilityFlags; use qsc_lowerer::map_hir_package_to_fir; use qsc_partial_eval::{partially_evaluate, ProgramEntry}; use qsc_rca::PackageStoreComputeProperties; use qsc_rir::{ - passes::{check_and_transform, defer_quantum_measurements}, + passes::check_and_transform, rir::{self, ConditionCode}, utils::get_all_block_successors, }; @@ -43,11 +43,8 @@ pub fn fir_to_qir( compute_properties: Option, entry: &ProgramEntry, ) -> Result { - let mut program = get_rir_from_compilation(fir_store, compute_properties, entry)?; + let mut program = get_rir_from_compilation(fir_store, compute_properties, entry, capabilities)?; check_and_transform(&mut program); - if capabilities.is_empty() { - defer_quantum_measurements(&mut program); - } Ok(ToQir::::to_qir(&program, &program)) } @@ -55,13 +52,14 @@ fn get_rir_from_compilation( fir_store: &qsc_fir::fir::PackageStore, compute_properties: Option, entry: &ProgramEntry, + capabilities: TargetCapabilityFlags, ) -> Result { let compute_properties = compute_properties.unwrap_or_else(|| { let analyzer = qsc_rca::Analyzer::init(fir_store); analyzer.analyze_all() }); - partially_evaluate(fir_store, &compute_properties, entry) + partially_evaluate(fir_store, &compute_properties, entry, capabilities) } /// A trait for converting a type into QIR of type `T`. @@ -546,9 +544,92 @@ impl ToQir for rir::Program { } else { "adaptive_profile" }; - format!( + let body = format!( include_str!("./qir/template.ll"), callables, profile, self.num_qubits, self.num_results - ) + ); + let flags = get_module_metadata(self); + body + "\n" + &flags + } +} + +/// Create the module metadata for the given program. +/// creating the `llvm.module.flags` and its associated values. +fn get_module_metadata(program: &rir::Program) -> String { + let mut flags = String::new(); + + // push the default attrs, we don't have any config values + // for now that would change any of them. + flags.push_str( + r#" +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +"#, + ); + + let mut index = 4; + + // If we are not in the base profile, we need to add the capabilities + // associated with the adaptive profile. + if !program.config.is_base() { + // loop through the capabilities and add them to the metadata + // for values that we can generate. + for cap in program.config.capabilities.iter() { + let name = match cap { + TargetCapabilityFlags::QubitReset => "qubit_resetting", + TargetCapabilityFlags::IntegerComputations => "classical_ints", + TargetCapabilityFlags::FloatingPointComputations => "classical_floats", + TargetCapabilityFlags::BackwardsBranching => "backwards_branching", + _ => continue, + }; + flags.push_str(&format!( + "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n", + index, 1, name, true + )); + index += 1; + } + + // loop through the capabilities that are missing and add them to the metadata + // as not supported. + let missing = TargetCapabilityFlags::all().difference(program.config.capabilities); + for cap in missing.iter() { + let name = match cap { + TargetCapabilityFlags::QubitReset => "qubit_resetting", + TargetCapabilityFlags::IntegerComputations => "classical_ints", + TargetCapabilityFlags::FloatingPointComputations => "classical_floats", + TargetCapabilityFlags::BackwardsBranching => "backwards_branching", + _ => continue, + }; + flags.push_str(&format!( + "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n", + index, 1, name, false + )); + index += 1; + } + + // Add the remaining extension capabilities as not supported. + // We can't generate these values yet so we just add them as false. + let unmapped_capabilities = [ + "classical_fixed_points", + "user_functions", + "multiple_target_branching", + ]; + for capability in unmapped_capabilities { + flags.push_str(&format!( + "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n", + index, 1, capability, false + )); + index += 1; + } + } + + let mut metadata_def = String::new(); + metadata_def.push_str("!llvm.module.flags = !{"); + for i in 0..index - 1 { + metadata_def.push_str(&format!("!{i}, ")); } + metadata_def.push_str(&format!("!{}}}\n", index - 1)); + metadata_def + &flags } diff --git a/compiler/qsc_codegen/src/qir/template.ll b/compiler/qsc_codegen/src/qir/template.ll index fae49bd70f..b58560a733 100644 --- a/compiler/qsc_codegen/src/qir/template.ll +++ b/compiler/qsc_codegen/src/qir/template.ll @@ -7,10 +7,3 @@ attributes #0 = {{ "entry_point" "output_labeling_schema" "qir_profiles"="{}" "r attributes #1 = {{ "irreversible" }} ; module flags - -!llvm.module.flags = !{{!0, !1, !2, !3}} - -!0 = !{{i32 1, !"qir_major_version", i32 1}} -!1 = !{{i32 7, !"qir_minor_version", i32 0}} -!2 = !{{i32 1, !"dynamic_qubit_management", i1 false}} -!3 = !{{i32 1, !"dynamic_result_management", i1 false}} diff --git a/compiler/qsc_codegen/src/qir/tests.rs b/compiler/qsc_codegen/src/qir/tests.rs index ddc887b916..80c8a0f50b 100644 --- a/compiler/qsc_codegen/src/qir/tests.rs +++ b/compiler/qsc_codegen/src/qir/tests.rs @@ -219,11 +219,18 @@ fn teleport_program() { ; module flags - !llvm.module.flags = !{!0, !1, !2, !3} + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 false} + !5 = !{i32 1, !"classical_floats", i1 false} + !6 = !{i32 1, !"backwards_branching", i1 false} + !7 = !{i32 1, !"qubit_resetting", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} "#]].assert_eq(&program.to_qir(&program)); } diff --git a/compiler/qsc_codegen/src/qir_base/tests.rs b/compiler/qsc_codegen/src/qir_base/tests.rs index 4002d546eb..9d3b2ef978 100644 --- a/compiler/qsc_codegen/src/qir_base/tests.rs +++ b/compiler/qsc_codegen/src/qir_base/tests.rs @@ -8,8 +8,8 @@ use std::sync::Arc; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_passes::{run_core_passes, run_default_passes, PackageType}; use crate::qir_base::generate_qir; diff --git a/compiler/qsc_codegen/src/qsharp/test_utils.rs b/compiler/qsc_codegen/src/qsharp/test_utils.rs index 2a8f8f2953..e13d728e7a 100644 --- a/compiler/qsc_codegen/src/qsharp/test_utils.rs +++ b/compiler/qsc_codegen/src/qsharp/test_utils.rs @@ -8,8 +8,10 @@ use std::sync::Arc; use expect_test::Expect; use qsc_ast::{ast::Package, mut_visit::MutVisitor}; -use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{ + language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags, +}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::hir::PackageId; use qsc_passes::{run_core_passes, run_default_passes, PackageType}; diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs index 53a850c6e0..68fc178cca 100644 --- a/compiler/qsc_data_structures/src/lib.rs +++ b/compiler/qsc_data_structures/src/lib.rs @@ -7,3 +7,4 @@ pub mod index_map; pub mod language_features; pub mod line_column; pub mod span; +pub mod target; diff --git a/compiler/qsc_data_structures/src/target.rs b/compiler/qsc_data_structures/src/target.rs new file mode 100644 index 0000000000..f9030ba282 --- /dev/null +++ b/compiler/qsc_data_structures/src/target.rs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use bitflags::bitflags; + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct TargetCapabilityFlags: u32 { + const Adaptive = 0b0000_0001; + const IntegerComputations = 0b0000_0010; + const FloatingPointComputations = 0b0000_0100; + const BackwardsBranching = 0b0000_1000; + const HigherLevelConstructs = 0b0001_0000; + const QubitReset = 0b0010_0000; + } +} + +impl std::str::FromStr for TargetCapabilityFlags { + type Err = (); + + fn from_str(value: &str) -> Result { + match value { + "Base" => Ok(TargetCapabilityFlags::empty()), + "Adaptive" => Ok(TargetCapabilityFlags::Adaptive), + "IntegerComputations" => Ok(TargetCapabilityFlags::IntegerComputations), + "FloatingPointComputations" => Ok(TargetCapabilityFlags::FloatingPointComputations), + "BackwardsBranching" => Ok(TargetCapabilityFlags::BackwardsBranching), + "HigherLevelConstructs" => Ok(TargetCapabilityFlags::HigherLevelConstructs), + "QubitReset" => Ok(TargetCapabilityFlags::QubitReset), + "Unrestricted" => Ok(TargetCapabilityFlags::all()), + _ => Err(()), + } + } +} + +impl Default for TargetCapabilityFlags { + fn default() -> Self { + TargetCapabilityFlags::empty() + } +} diff --git a/compiler/qsc_doc_gen/Cargo.toml b/compiler/qsc_doc_gen/Cargo.toml index 7249c2e6fa..ddc9e37ee9 100644 --- a/compiler/qsc_doc_gen/Cargo.toml +++ b/compiler/qsc_doc_gen/Cargo.toml @@ -13,6 +13,7 @@ expect-test = { workspace = true } [dependencies] regex-lite = { workspace = true } +qsc_data_structures = { path = "../qsc_data_structures" } qsc_frontend = { path = "../qsc_frontend" } qsc_ast = { path = "../qsc_ast" } qsc_hir = { path = "../qsc_hir" } diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index e03b76b46b..311c2c7071 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -7,7 +7,8 @@ mod tests; use crate::display::{increase_header_level, parse_doc_for_summary}; use crate::display::{CodeDisplay, Lookup}; use qsc_ast::ast; -use qsc_frontend::compile::{self, PackageStore, TargetCapabilityFlags}; +use qsc_data_structures::target::TargetCapabilityFlags; +use qsc_frontend::compile::{self, PackageStore}; use qsc_frontend::resolve; use qsc_hir::hir::{CallableKind, Item, ItemKind, Package, PackageId, Visibility}; use qsc_hir::{hir, ty}; diff --git a/compiler/qsc_eval/src/intrinsic/tests.rs b/compiler/qsc_eval/src/intrinsic/tests.rs index 30a3184ad5..7925858da0 100644 --- a/compiler/qsc_eval/src/intrinsic/tests.rs +++ b/compiler/qsc_eval/src/intrinsic/tests.rs @@ -17,8 +17,9 @@ use expect_test::{expect, Expect}; use indoc::indoc; use num_bigint::BigInt; use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::target::TargetCapabilityFlags; use qsc_fir::fir; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_lowerer::map_hir_package_to_fir; use qsc_passes::{run_core_passes, run_default_passes, PackageType}; diff --git a/compiler/qsc_eval/src/tests.rs b/compiler/qsc_eval/src/tests.rs index 1d74c7ba37..5263f05b96 100644 --- a/compiler/qsc_eval/src/tests.rs +++ b/compiler/qsc_eval/src/tests.rs @@ -14,10 +14,10 @@ use crate::{ }; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_fir::fir::{self, ExecGraphNode, StmtId}; use qsc_fir::fir::{PackageId, PackageStoreLookup}; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_lowerer::map_hir_package_to_fir; use qsc_passes::{run_core_passes, run_default_passes, PackageType}; diff --git a/compiler/qsc_frontend/Cargo.toml b/compiler/qsc_frontend/Cargo.toml index 0ee1895df3..a59b466366 100644 --- a/compiler/qsc_frontend/Cargo.toml +++ b/compiler/qsc_frontend/Cargo.toml @@ -9,7 +9,6 @@ edition.workspace = true license.workspace = true [dependencies] -bitflags = { workspace = true } miette = { workspace = true } qsc_data_structures = { path = "../qsc_data_structures" } qsc_ast = { path = "../qsc_ast" } diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs index 0f89a4f547..86e816a405 100644 --- a/compiler/qsc_frontend/src/compile.rs +++ b/compiler/qsc_frontend/src/compile.rs @@ -12,7 +12,7 @@ use crate::{ resolve::{self, Locals, Names, Resolver}, typeck::{self, Checker, Table}, }; -use bitflags::bitflags; + use miette::{Diagnostic, Report}; use preprocess::TrackedName; use qsc_ast::{ @@ -26,6 +26,7 @@ use qsc_data_structures::{ index_map::{self, IndexMap}, language_features::LanguageFeatures, span::Span, + target::TargetCapabilityFlags, }; use qsc_hir::{ assigner::Assigner as HirAssigner, @@ -34,39 +35,9 @@ use qsc_hir::{ validate::Validator as HirValidator, visit::Visitor as _, }; -use std::{fmt::Debug, str::FromStr, sync::Arc}; +use std::{fmt::Debug, sync::Arc}; use thiserror::Error; -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct TargetCapabilityFlags: u32 { - const Adaptive = 0b0000_0001; - const IntegerComputations = 0b0000_0010; - const FloatingPointComputations = 0b0000_0100; - const BackwardsBranching = 0b0000_1000; - const HigherLevelConstructs = 0b0001_0000; - const QubitReset = 0b0010_0000; - } -} - -impl FromStr for TargetCapabilityFlags { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "Base" => Ok(TargetCapabilityFlags::empty()), - "Adaptive" => Ok(TargetCapabilityFlags::Adaptive), - "IntegerComputations" => Ok(TargetCapabilityFlags::IntegerComputations), - "FloatingPointComputations" => Ok(TargetCapabilityFlags::FloatingPointComputations), - "BackwardsBranching" => Ok(TargetCapabilityFlags::BackwardsBranching), - "HigherLevelConstructs" => Ok(TargetCapabilityFlags::HigherLevelConstructs), - "QubitReset" => Ok(TargetCapabilityFlags::QubitReset), - "Unrestricted" => Ok(TargetCapabilityFlags::all()), - _ => Err(()), - } - } -} - #[derive(Debug, Default)] pub struct CompileUnit { pub package: hir::Package, diff --git a/compiler/qsc_frontend/src/incremental.rs b/compiler/qsc_frontend/src/incremental.rs index 7cd4eb6282..75a3d8f406 100644 --- a/compiler/qsc_frontend/src/incremental.rs +++ b/compiler/qsc_frontend/src/incremental.rs @@ -5,10 +5,7 @@ mod tests; use crate::{ - compile::{ - self, preprocess, AstPackage, CompileUnit, Offsetter, PackageStore, SourceMap, - TargetCapabilityFlags, - }, + compile::{self, preprocess, AstPackage, CompileUnit, Offsetter, PackageStore, SourceMap}, error::WithSource, lower::Lowerer, resolve::{self, Resolver}, @@ -21,7 +18,7 @@ use qsc_ast::{ validate::Validator as AstValidator, visit::Visitor as AstVisitor, }; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_hir::{ assigner::Assigner as HirAssigner, hir::{self, PackageId}, diff --git a/compiler/qsc_frontend/src/incremental/tests.rs b/compiler/qsc_frontend/src/incremental/tests.rs index a055b2a377..ac077e6044 100644 --- a/compiler/qsc_frontend/src/incremental/tests.rs +++ b/compiler/qsc_frontend/src/incremental/tests.rs @@ -3,13 +3,13 @@ use super::{Compiler, Increment}; use crate::{ - compile::{self, CompileUnit, PackageStore, TargetCapabilityFlags}, + compile::{self, CompileUnit, PackageStore}, incremental::Error, }; use expect_test::{expect, Expect}; use indoc::indoc; use miette::Diagnostic; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use std::fmt::Write; #[allow(clippy::too_many_lines)] diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs index 9d58f804d2..a5ecfa45ad 100644 --- a/compiler/qsc_frontend/src/lower.rs +++ b/compiler/qsc_frontend/src/lower.rs @@ -6,13 +6,12 @@ mod tests; use crate::{ closure::{self, Lambda, PartialApp}, - compile::TargetCapabilityFlags, resolve::{self, Names}, typeck::{self, convert}, }; use miette::Diagnostic; use qsc_ast::ast; -use qsc_data_structures::{index_map::IndexMap, span::Span}; +use qsc_data_structures::{index_map::IndexMap, span::Span, target::TargetCapabilityFlags}; use qsc_hir::{ assigner::Assigner, hir::{self, LocalItemId}, diff --git a/compiler/qsc_frontend/src/lower/tests.rs b/compiler/qsc_frontend/src/lower/tests.rs index 0f63a88b40..8f0a9b6545 100644 --- a/compiler/qsc_frontend/src/lower/tests.rs +++ b/compiler/qsc_frontend/src/lower/tests.rs @@ -3,10 +3,10 @@ #![allow(clippy::needless_raw_string_hashes)] -use crate::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use crate::compile::{self, compile, PackageStore, SourceMap}; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; fn check_hir(input: &str, expect: &Expect) { let sources = SourceMap::new([("test".into(), input.into())], None); diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs index 0969ddd54e..34222770a0 100644 --- a/compiler/qsc_frontend/src/resolve/tests.rs +++ b/compiler/qsc_frontend/src/resolve/tests.rs @@ -6,7 +6,6 @@ use super::{Error, Locals, Names, Res}; use crate::{ compile, - compile::TargetCapabilityFlags, resolve::{LocalKind, Resolver}, }; use expect_test::{expect, Expect}; @@ -17,7 +16,9 @@ use qsc_ast::{ mut_visit::MutVisitor, visit::{self, Visitor}, }; -use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; +use qsc_data_structures::{ + language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags, +}; use qsc_hir::assigner::Assigner as HirAssigner; use std::fmt::Write; diff --git a/compiler/qsc_linter/src/tests.rs b/compiler/qsc_linter/src/tests.rs index 70c0c6ce1b..fe436d92a1 100644 --- a/compiler/qsc_linter/src/tests.rs +++ b/compiler/qsc_linter/src/tests.rs @@ -6,8 +6,8 @@ use crate::{ Lint, LintConfig, LintLevel, }; use expect_test::{expect, Expect}; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, CompileUnit, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, CompileUnit, PackageStore, SourceMap}; use qsc_passes::PackageType; #[test] diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs index 5c0ec74492..1cc325d57a 100644 --- a/compiler/qsc_partial_eval/src/lib.rs +++ b/compiler/qsc_partial_eval/src/lib.rs @@ -7,8 +7,8 @@ mod management; use evaluation_context::{Arg, BlockNode, EvaluationContext, Scope}; use management::{QuantumIntrinsicsChecker, ResourceManager}; use miette::Diagnostic; -use qsc_data_structures::functors::FunctorApp; use qsc_data_structures::span::Span; +use qsc_data_structures::{functors::FunctorApp, target::TargetCapabilityFlags}; use qsc_eval::{ self, exec_graph_section, output::GenericReceiver, @@ -46,8 +46,10 @@ pub fn partially_evaluate( package_store: &PackageStore, compute_properties: &PackageStoreComputeProperties, entry: &ProgramEntry, + capabilities: TargetCapabilityFlags, ) -> Result { - let partial_evaluator = PartialEvaluator::new(package_store, compute_properties, entry); + let partial_evaluator = + PartialEvaluator::new(package_store, compute_properties, entry, capabilities); partial_evaluator.eval() } @@ -123,10 +125,12 @@ impl<'a> PartialEvaluator<'a> { package_store: &'a PackageStore, compute_properties: &'a PackageStoreComputeProperties, entry: &'a ProgramEntry, + capabilities: TargetCapabilityFlags, ) -> Self { // Create the entry-point callable. let mut resource_manager = ResourceManager::default(); let mut program = Program::new(); + program.config.capabilities = capabilities; let entry_block_id = resource_manager.next_block(); program.blocks.insert(entry_block_id, rir::Block::default()); let entry_point_id = resource_manager.next_callable(); diff --git a/compiler/qsc_partial_eval/tests/output_recording.rs b/compiler/qsc_partial_eval/tests/output_recording.rs index 3c9cd3c6f9..15677e2647 100644 --- a/compiler/qsc_partial_eval/tests/output_recording.rs +++ b/compiler/qsc_partial_eval/tests/output_recording.rs @@ -83,8 +83,7 @@ fn output_recording_for_tuple_of_different_types() { Call id(5), args( Variable(1, Boolean), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -170,8 +169,7 @@ fn output_recording_for_nested_tuples() { Call id(5), args( Variable(3, Boolean), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -265,8 +263,7 @@ fn output_recording_for_tuple_of_arrays() { Call id(6), args( Variable(3, Boolean), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -360,8 +357,7 @@ fn output_recording_for_array_of_tuples() { Call id(6), args( Variable(3, Boolean), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -403,8 +399,7 @@ fn output_recording_for_literal_bool() { Call id(1), args( Bool(true), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -446,8 +441,7 @@ fn output_recording_for_literal_int() { Call id(1), args( Integer(42), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -518,8 +512,7 @@ fn output_recording_for_mix_of_literal_and_variable() { Call id(4), args( Bool(true), Pointer, ) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); diff --git a/compiler/qsc_partial_eval/tests/test_utils.rs b/compiler/qsc_partial_eval/tests/test_utils.rs index 2552d8083d..8d558be4cc 100644 --- a/compiler/qsc_partial_eval/tests/test_utils.rs +++ b/compiler/qsc_partial_eval/tests/test_utils.rs @@ -3,9 +3,9 @@ use expect_test::Expect; use qsc::{incremental::Compiler, PackageType}; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_fir::fir::PackageStore; -use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap}; use qsc_lowerer::{map_hir_package_to_fir, Lowerer}; use qsc_partial_eval::{partially_evaluate, ProgramEntry}; use qsc_rca::{Analyzer, PackageStoreComputeProperties}; @@ -38,6 +38,7 @@ pub fn compile_and_partially_evaluate(source: &str) -> Program { &compilation_context.fir_store, &compilation_context.compute_properties, &compilation_context.entry, + TargetCapabilityFlags::empty(), ); match maybe_program { Ok(program) => program, diff --git a/compiler/qsc_passes/src/baseprofck/tests.rs b/compiler/qsc_passes/src/baseprofck/tests.rs index 9f87f98327..2e6f202aea 100644 --- a/compiler/qsc_passes/src/baseprofck/tests.rs +++ b/compiler/qsc_passes/src/baseprofck/tests.rs @@ -5,8 +5,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use crate::baseprofck::check_base_profile_compliance; diff --git a/compiler/qsc_passes/src/borrowck/tests.rs b/compiler/qsc_passes/src/borrowck/tests.rs index c6c146f7b0..f09c5d5eaa 100644 --- a/compiler/qsc_passes/src/borrowck/tests.rs +++ b/compiler/qsc_passes/src/borrowck/tests.rs @@ -5,8 +5,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::visit::Visitor; use crate::borrowck::Checker; diff --git a/compiler/qsc_passes/src/callable_limits/tests.rs b/compiler/qsc_passes/src/callable_limits/tests.rs index 5bd2a0cdba..7c09837d0f 100644 --- a/compiler/qsc_passes/src/callable_limits/tests.rs +++ b/compiler/qsc_passes/src/callable_limits/tests.rs @@ -5,8 +5,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::visit::Visitor; use crate::callable_limits::CallableLimits; diff --git a/compiler/qsc_passes/src/capabilitiesck.rs b/compiler/qsc_passes/src/capabilitiesck.rs index a7e5d64874..406501b694 100644 --- a/compiler/qsc_passes/src/capabilitiesck.rs +++ b/compiler/qsc_passes/src/capabilitiesck.rs @@ -14,7 +14,7 @@ mod tests_adaptive_plus_integers; pub mod tests_common; use miette::Diagnostic; -use qsc_data_structures::span::Span; +use qsc_data_structures::{span::Span, target::TargetCapabilityFlags}; use qsc_fir::{ fir::{ @@ -25,7 +25,7 @@ use qsc_fir::{ ty::FunctorSetValue, visit::Visitor, }; -use qsc_frontend::compile::TargetCapabilityFlags; + use qsc_lowerer::map_hir_package_to_fir; use qsc_rca::{ Analyzer, ComputeKind, ItemComputeProperties, PackageComputeProperties, diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs index 1f16269328..69193cc946 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs @@ -15,7 +15,7 @@ use super::tests_common::{ USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, }; use expect_test::{expect, Expect}; -use qsc_frontend::compile::TargetCapabilityFlags; +use qsc_data_structures::target::TargetCapabilityFlags; fn check_profile(source: &str, expect: &Expect) { check(source, expect, TargetCapabilityFlags::Adaptive); diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs index c38f8d39dd..0ee2903d7f 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs @@ -17,7 +17,7 @@ use super::tests_common::{ USE_DYNAMIC_QUBIT, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, }; use expect_test::{expect, Expect}; -use qsc_frontend::compile::TargetCapabilityFlags; +use qsc_data_structures::target::TargetCapabilityFlags; fn check_profile(source: &str, expect: &Expect) { check( diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs index 8410b7e4f9..56457eefbc 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs @@ -15,7 +15,7 @@ use super::tests_common::{ USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT, }; use expect_test::{expect, Expect}; -use qsc_frontend::compile::TargetCapabilityFlags; +use qsc_data_structures::target::TargetCapabilityFlags; fn check_profile(source: &str, expect: &Expect) { check(source, expect, TargetCapabilityFlags::empty()); diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs index b0b6fd9d0e..48f0ac34a7 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs @@ -7,9 +7,9 @@ use expect_test::Expect; use crate::capabilitiesck::check_supported_capabilities; use qsc::{incremental::Compiler, PackageType}; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_fir::fir::{Package, PackageId, PackageStore}; -use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap}; use qsc_lowerer::{map_hir_package_to_fir, Lowerer}; use qsc_rca::{Analyzer, PackageComputeProperties, PackageStoreComputeProperties}; diff --git a/compiler/qsc_passes/src/conjugate_invert/tests.rs b/compiler/qsc_passes/src/conjugate_invert/tests.rs index 34c8898bf9..b94e7eb529 100644 --- a/compiler/qsc_passes/src/conjugate_invert/tests.rs +++ b/compiler/qsc_passes/src/conjugate_invert/tests.rs @@ -6,8 +6,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::{validate::Validator, visit::Visitor}; use crate::conjugate_invert::invert_conjugate_exprs; diff --git a/compiler/qsc_passes/src/entry_point/tests.rs b/compiler/qsc_passes/src/entry_point/tests.rs index 4639d431dd..b28a8eacf3 100644 --- a/compiler/qsc_passes/src/entry_point/tests.rs +++ b/compiler/qsc_passes/src/entry_point/tests.rs @@ -6,8 +6,8 @@ use crate::entry_point::generate_entry_expr; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; fn check(file: &str, expr: &str, expect: &Expect) { let sources = SourceMap::new([("test".into(), file.into())], Some(expr.into())); diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs index 9116ee6081..45ff8988d8 100644 --- a/compiler/qsc_passes/src/lib.rs +++ b/compiler/qsc_passes/src/lib.rs @@ -20,8 +20,9 @@ use capabilitiesck::{check_supported_capabilities, lower_store, run_rca_pass}; use entry_point::generate_entry_expr; use loop_unification::LoopUni; use miette::Diagnostic; +use qsc_data_structures::target::TargetCapabilityFlags; use qsc_fir::fir; -use qsc_frontend::compile::{CompileUnit, TargetCapabilityFlags}; +use qsc_frontend::compile::CompileUnit; use qsc_hir::{ assigner::Assigner, global::{self, Table}, diff --git a/compiler/qsc_passes/src/logic_sep/tests.rs b/compiler/qsc_passes/src/logic_sep/tests.rs index 5a99cd528f..58077d7207 100644 --- a/compiler/qsc_passes/src/logic_sep/tests.rs +++ b/compiler/qsc_passes/src/logic_sep/tests.rs @@ -5,8 +5,10 @@ #![allow(clippy::needless_raw_string_hashes)] use expect_test::{expect, Expect}; -use qsc_data_structures::{language_features::LanguageFeatures, span::Span}; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{ + language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags, +}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::{ hir::{ExprKind, NodeId, Stmt}, visit::{walk_stmt, Visitor}, diff --git a/compiler/qsc_passes/src/loop_unification/tests.rs b/compiler/qsc_passes/src/loop_unification/tests.rs index 9ccf984ce4..f38145f8bd 100644 --- a/compiler/qsc_passes/src/loop_unification/tests.rs +++ b/compiler/qsc_passes/src/loop_unification/tests.rs @@ -5,8 +5,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::{mut_visit::MutVisitor, validate::Validator, visit::Visitor}; use crate::loop_unification::LoopUni; diff --git a/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs b/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs index 42fa1be385..b9c4d14021 100644 --- a/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs +++ b/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs @@ -4,8 +4,8 @@ use crate::replace_qubit_allocation::ReplaceQubitAllocation; use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::{mut_visit::MutVisitor, validate::Validator, visit::Visitor}; fn check(file: &str, expect: &Expect) { diff --git a/compiler/qsc_passes/src/spec_gen/tests.rs b/compiler/qsc_passes/src/spec_gen/tests.rs index 3743801e48..a791a8da2f 100644 --- a/compiler/qsc_passes/src/spec_gen/tests.rs +++ b/compiler/qsc_passes/src/spec_gen/tests.rs @@ -6,8 +6,8 @@ use expect_test::{expect, Expect}; use indoc::indoc; -use qsc_data_structures::language_features::LanguageFeatures; -use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; +use qsc_frontend::compile::{self, compile, PackageStore, SourceMap}; use qsc_hir::{validate::Validator, visit::Visitor}; use crate::spec_gen::generate_specs; diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs index 7a7544ea32..5ee665a896 100644 --- a/compiler/qsc_rca/src/lib.rs +++ b/compiler/qsc_rca/src/lib.rs @@ -18,7 +18,10 @@ mod scaffolding; use crate::common::set_indentation; use bitflags::bitflags; use indenter::indented; -use qsc_data_structures::index_map::{IndexMap, Iter}; +use qsc_data_structures::{ + index_map::{IndexMap, Iter}, + target::TargetCapabilityFlags, +}; use qsc_fir::{ fir::{ BlockId, ExprId, LocalItemId, PackageId, StmtId, StoreBlockId, StoreExprId, StoreItemId, @@ -26,7 +29,7 @@ use qsc_fir::{ }, ty::Ty, }; -use qsc_frontend::compile::TargetCapabilityFlags; + use std::{ cmp::Ord, fmt::{self, Debug, Display, Formatter, Write}, diff --git a/compiler/qsc_rca/tests/test_utils.rs b/compiler/qsc_rca/tests/test_utils.rs index a0b2db0b0b..b129af0eb2 100644 --- a/compiler/qsc_rca/tests/test_utils.rs +++ b/compiler/qsc_rca/tests/test_utils.rs @@ -3,9 +3,9 @@ use expect_test::Expect; use qsc::incremental::Compiler; -use qsc_data_structures::language_features::LanguageFeatures; +use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_fir::fir::{ItemKind, LocalItemId, Package, PackageStore, StoreItemId}; -use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags}; +use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap}; use qsc_lowerer::{map_hir_package_to_fir, Lowerer}; use qsc_passes::PackageType; use qsc_rca::{Analyzer, ComputePropertiesLookup, PackageStoreComputeProperties}; @@ -19,12 +19,12 @@ pub struct CompilationContext { impl CompilationContext { #[must_use] - pub fn new(runtime_capabilities: TargetCapabilityFlags) -> Self { + pub fn new(capabilities: TargetCapabilityFlags) -> Self { let compiler = Compiler::new( true, SourceMap::default(), PackageType::Lib, - runtime_capabilities, + capabilities, LanguageFeatures::default(), ) .expect("should be able to create a new compiler"); diff --git a/compiler/qsc_rir/src/builder.rs b/compiler/qsc_rir/src/builder.rs index 135f74c79a..aa67cc07b0 100644 --- a/compiler/qsc_rir/src/builder.rs +++ b/compiler/qsc_rir/src/builder.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use qsc_data_structures::target::TargetCapabilityFlags; + use crate::rir::{ Block, BlockId, Callable, CallableId, CallableType, Instruction, Literal, Operand, Program, Ty, Variable, VariableId, @@ -260,8 +262,6 @@ pub fn bell_program() -> Program { ); program.num_qubits = 2; program.num_results = 2; - program.config.defer_measurements = true; - program.config.remap_qubits_on_reuse = true; program } @@ -269,6 +269,7 @@ pub fn bell_program() -> Program { #[must_use] pub fn teleport_program() -> Program { let mut program = Program::default(); + program.config.capabilities = TargetCapabilityFlags::Adaptive; program.callables.insert(CallableId(0), h_decl()); program.callables.insert(CallableId(1), z_decl()); program.callables.insert(CallableId(2), x_decl()); diff --git a/compiler/qsc_rir/src/passes.rs b/compiler/qsc_rir/src/passes.rs index d6cd307665..6a730733ef 100644 --- a/compiler/qsc_rir/src/passes.rs +++ b/compiler/qsc_rir/src/passes.rs @@ -12,6 +12,7 @@ mod unreachable_code_check; use build_dominator_graph::build_dominator_graph; use defer_meas::defer_measurements; +use qsc_data_structures::target::TargetCapabilityFlags; use reindex_qubits::reindex_qubits; use remap_block_ids::remap_block_ids; use ssa_check::check_ssa_form; @@ -37,12 +38,18 @@ pub fn check_and_transform(program: &mut Program) { check_ssa_form(program, &preds, &doms); check_unreachable_code(program); check_types(program); -} -/// Run the RIR passes that are necessary for targets with no mid-program measurement. -/// This requires that qubits are not reused after measurement or reset, so qubit ids must be reindexed. -/// This also requires that the program is a single block and will panic otherwise. -pub fn defer_quantum_measurements(program: &mut Program) { - reindex_qubits(program); - defer_measurements(program); + // Run the RIR passes that are necessary for targets with no mid-program measurement. + // This requires that qubits are not reused after measurement or reset, so qubit ids must be reindexed. + // This also requires that the program is a single block and will panic otherwise. + if !program + .config + .capabilities + .contains(TargetCapabilityFlags::QubitReset) + { + reindex_qubits(program); + } + if program.config.capabilities == TargetCapabilityFlags::empty() { + defer_measurements(program); + } } diff --git a/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs b/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs index 7e16ca1f9e..2345e2cc53 100644 --- a/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs +++ b/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs @@ -54,8 +54,7 @@ fn remap_block_ids_no_changes() { Block 2: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -79,8 +78,7 @@ fn remap_block_ids_no_changes() { Block 2: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -128,8 +126,7 @@ fn remap_block_ids_out_of_order_no_branches() { Block 7: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -153,8 +150,7 @@ fn remap_block_ids_out_of_order_no_branches() { Block 2: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -215,8 +211,7 @@ fn remap_block_ids_out_of_order_with_one_branch() { Block 3: Block: Jump(1) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -243,8 +238,7 @@ fn remap_block_ids_out_of_order_with_one_branch() { Block 3: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -300,8 +294,7 @@ fn remap_block_ids_simple_loop() { Block 6: Block: Jump(4) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -326,8 +319,7 @@ fn remap_block_ids_simple_loop() { Block 2: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -370,8 +362,7 @@ fn remap_block_ids_infinite_loop() { Block 4: Block: Jump(0) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -394,8 +385,7 @@ fn remap_block_ids_infinite_loop() { Block 1: Block: Jump(0) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -459,8 +449,7 @@ fn remap_block_ids_nested_branching_loops() { Block 6: Block: Branch Variable(1, Boolean), 4, 2 config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -485,8 +474,7 @@ fn remap_block_ids_nested_branching_loops() { Block 2: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -589,8 +577,7 @@ fn remap_block_ids_ensures_acyclic_program_gets_topological_ordering() { Block 8: Block: Jump(7) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -627,8 +614,7 @@ fn remap_block_ids_ensures_acyclic_program_gets_topological_ordering() { Block 8: Block: Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); diff --git a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs index da9d64064d..2a343ec22b 100644 --- a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs +++ b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs @@ -4,6 +4,7 @@ #![allow(clippy::too_many_lines, clippy::needless_raw_string_hashes)] use expect_test::expect; +use qsc_data_structures::target::TargetCapabilityFlags; use crate::{ builder::{bell_program, new_program, teleport_program}, @@ -14,19 +15,15 @@ use crate::{ }, }; fn transform_program(program: &mut Program) { - // When this configuration is replaced by target capabilities, set them to "all" here. - program.config.defer_measurements = false; - program.config.remap_qubits_on_reuse = false; + program.config.capabilities = TargetCapabilityFlags::all(); check_and_transform(program); } #[test] fn ssa_transform_leaves_program_without_store_instruction_unchanged() { let mut program = bell_program(); - program.config.defer_measurements = false; - program.config.remap_qubits_on_reuse = false; + program.config.capabilities = TargetCapabilityFlags::all(); let program_string_orignal = program.to_string(); - transform_program(&mut program); assert_eq!(program_string_orignal, program.to_string()); @@ -35,8 +32,8 @@ fn ssa_transform_leaves_program_without_store_instruction_unchanged() { #[test] fn ssa_transform_leaves_branching_program_without_store_instruction_unchanged() { let mut program = teleport_program(); + program.config.capabilities = TargetCapabilityFlags::all(); let program_string_orignal = program.to_string(); - transform_program(&mut program); assert_eq!(program_string_orignal, program.to_string()); @@ -115,8 +112,7 @@ fn ssa_transform_removes_store_in_single_block_program() { Variable(2, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -145,8 +141,7 @@ fn ssa_transform_removes_store_in_single_block_program() { Variable(2, Boolean) = LogicalNot Variable(0, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -269,8 +264,7 @@ fn ssa_transform_removes_multiple_stores_in_single_block_program() { Variable(4, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -301,8 +295,7 @@ fn ssa_transform_removes_multiple_stores_in_single_block_program() { Variable(4, Boolean) = LogicalNot Variable(3, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -434,8 +427,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks() { Variable(4, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -472,8 +464,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks() { Variable(4, Boolean) = LogicalNot Variable(0, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -578,8 +569,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks_without_i Variable(4, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -614,8 +604,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks_without_i Variable(4, Boolean) = LogicalNot Variable(0, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -769,8 +758,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage() { Variable(4, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -808,8 +796,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage() { Variable(4, Boolean) = LogicalNot Variable(5, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } @@ -937,8 +924,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage_in_one_branch() { Variable(4, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -975,8 +961,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage_in_one_branch() { Variable(4, Boolean) = LogicalNot Variable(5, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } @@ -1188,8 +1173,7 @@ fn ssa_transform_inserts_phi_for_node_with_many_predecessors() { Variable(5, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -1236,8 +1220,7 @@ fn ssa_transform_inserts_phi_for_node_with_many_predecessors() { Variable(5, Boolean) = LogicalNot Variable(6, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } @@ -1412,8 +1395,7 @@ fn ssa_transform_inserts_phi_for_multiple_stored_values() { Variable(6, Boolean) = LogicalNot Variable(2, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -1453,8 +1435,7 @@ fn ssa_transform_inserts_phi_for_multiple_stored_values() { Variable(6, Boolean) = LogicalNot Variable(8, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } @@ -1734,8 +1715,7 @@ fn ssa_transform_inserts_phi_nodes_in_successive_blocks_for_chained_branches() { Variable(8, Boolean) = LogicalNot Variable(1, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -1786,8 +1766,7 @@ fn ssa_transform_inserts_phi_nodes_in_successive_blocks_for_chained_branches() { Variable(8, Boolean) = LogicalNot Variable(10, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } @@ -2026,8 +2005,7 @@ fn ssa_transform_inerts_phi_nodes_for_early_return_graph_pattern() { Variable(7, Boolean) = LogicalNot Variable(1, Boolean) Jump(3) config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: Base num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -2075,8 +2053,7 @@ fn ssa_transform_inerts_phi_nodes_for_early_return_graph_pattern() { Variable(4, Boolean) = LogicalNot Variable(9, Boolean) Return config: Config: - remap_qubits_on_reuse: false - defer_measurements: false + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]].assert_eq(&program.to_string()); } diff --git a/compiler/qsc_rir/src/rir.rs b/compiler/qsc_rir/src/rir.rs index 8d0687fe7b..b5ad15690c 100644 --- a/compiler/qsc_rir/src/rir.rs +++ b/compiler/qsc_rir/src/rir.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use indenter::{indented, Indented}; -use qsc_data_structures::index_map::IndexMap; +use qsc_data_structures::{index_map::IndexMap, target::TargetCapabilityFlags}; use std::fmt::{self, Display, Formatter, Write}; /// The root of the RIR. @@ -60,8 +60,7 @@ impl Program { #[derive(Default)] pub struct Config { - pub remap_qubits_on_reuse: bool, - pub defer_measurements: bool, + pub capabilities: TargetCapabilityFlags, } impl Display for Config { @@ -69,12 +68,11 @@ impl Display for Config { let mut indent = set_indentation(indented(f), 0); write!(indent, "Config:",)?; indent = set_indentation(indent, 1); - write!( - indent, - "\nremap_qubits_on_reuse: {}", - self.remap_qubits_on_reuse - )?; - write!(indent, "\ndefer_measurements: {}", self.defer_measurements)?; + if self.capabilities.is_empty() { + write!(indent, "\ncapabilities: Base")?; + } else { + write!(indent, "\ncapabilities: {:?}", self.capabilities)?; + } Ok(()) } } @@ -82,7 +80,7 @@ impl Display for Config { impl Config { #[must_use] pub fn is_base(&self) -> bool { - self.remap_qubits_on_reuse || self.defer_measurements + self.capabilities == TargetCapabilityFlags::empty() } } diff --git a/pip/tests/test_interpreter.py b/pip/tests/test_interpreter.py index bab27dcfbe..52cb3351f3 100644 --- a/pip/tests/test_interpreter.py +++ b/pip/tests/test_interpreter.py @@ -315,7 +315,7 @@ def test_adaptive_errors_are_raised_from_entry_expr() -> None: assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo) -def test_adaptive_qir_can_be_generated() -> None: +def test_quantinuum_qir_can_be_generated() -> None: adaptive_input = """ namespace Test { open Microsoft.Quantum.Math; @@ -362,6 +362,87 @@ def test_adaptive_qir_can_be_generated() -> None: ; module flags + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"classical_ints", i1 true} + !5 = !{i32 1, !"qubit_resetting", i1 true} + !6 = !{i32 1, !"classical_floats", i1 false} + !7 = !{i32 1, !"backwards_branching", i1 false} + !8 = !{i32 1, !"classical_fixed_points", i1 false} + !9 = !{i32 1, !"user_functions", i1 false} + !10 = !{i32 1, !"multiple_target_branching", i1 false} + """ + ) + + +def test_base_qir_can_be_generated() -> None: + base_input = """ + namespace Test { + open Microsoft.Quantum.Math; + open QIR.Intrinsic; + @EntryPoint() + operation Main() : Result { + use q = Qubit(); + let pi_over_two = 4.0 / 2.0; + __quantum__qis__rz__body(pi_over_two, q); + mutable some_angle = ArcSin(0.0); + __quantum__qis__rz__body(some_angle, q); + set some_angle = ArcCos(-1.0) / PI(); + __quantum__qis__rz__body(some_angle, q); + __quantum__qis__mresetz__body(q) + } + } + """ + e = Interpreter(TargetProfile.Base) + e.interpret(base_input) + qir = e.qir("Test.Main()") + assert qir == dedent( + """\ + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + ret void + } + + declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) + declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__rx__body(double, %Qubit*) + declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__ry__body(double, %Qubit*) + declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__rz__body(double, %Qubit*) + declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) + declare void @__quantum__qis__h__body(%Qubit*) + declare void @__quantum__qis__s__body(%Qubit*) + declare void @__quantum__qis__s__adj(%Qubit*) + declare void @__quantum__qis__t__body(%Qubit*) + declare void @__quantum__qis__t__adj(%Qubit*) + declare void @__quantum__qis__x__body(%Qubit*) + declare void @__quantum__qis__y__body(%Qubit*) + declare void @__quantum__qis__z__body(%Qubit*) + declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) + declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + declare void @__quantum__rt__result_record_output(%Result*, i8*) + declare void @__quantum__rt__array_record_output(i64, i8*) + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + !llvm.module.flags = !{!0, !1, !2, !3} !0 = !{i32 1, !"qir_major_version", i32 1}