From 308c57866638144b5a353b50661e49e6d325b616 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Tue, 21 May 2024 10:30:38 -0700 Subject: [PATCH] Use RCA based code generation for all scenarios (#1523) new QSC option: ``` -p, --profile Target QIR profile for code generation Possible values: - unrestricted: This is the default profile, which allows all operations - base: This profile restricts the set of operations to those that are supported by the Base profile - adaptive-ri: This profile restricts the set of operations to those that are supported by the AdaptiveRI profile ``` --- compiler/qsc/src/bin/qsc.rs | 99 +- compiler/qsc/src/codegen.rs | 59 +- compiler/qsc/src/codegen/tests.rs | 32 +- compiler/qsc/src/compile.rs | 9 +- compiler/qsc/src/incremental.rs | 4 +- compiler/qsc/src/interpret.rs | 85 +- compiler/qsc/src/interpret/tests.rs | 153 +- compiler/qsc_codegen/src/lib.rs | 1 - compiler/qsc_codegen/src/qir_base.rs | 552 ------ compiler/qsc_codegen/src/qir_base/postfix.ll | 38 - compiler/qsc_codegen/src/qir_base/prefix.ll | 4 - compiler/qsc_codegen/src/qir_base/tests.rs | 1657 ----------------- compiler/qsc_codegen/src/qsharp/test_utils.rs | 16 +- .../src/language_features.rs | 2 - compiler/qsc_eval/src/intrinsic/tests.rs | 16 +- compiler/qsc_eval/src/tests.rs | 30 +- compiler/qsc_fir/src/fir.rs | 2 +- compiler/qsc_passes/src/baseprofck.rs | 114 -- compiler/qsc_passes/src/lib.rs | 35 +- language_service/src/compilation.rs | 5 - language_service/src/protocol.rs | 1 + language_service/src/state.rs | 48 +- language_service/src/state/tests.rs | 99 +- pip/tests-qir/test_qir.py | 4 +- pip/tests/test_interpreter.py | 31 +- playground/src/editor.tsx | 4 +- .../code_distance_iterators/tests.rs | 8 +- vscode/package.json | 10 - vscode/src/config.ts | 14 - vscode/src/qirGeneration.ts | 45 +- vscode/src/statusbar.ts | 23 +- wasm/Cargo.toml | 2 +- wasm/src/language_service.rs | 5 + wasm/src/lib.rs | 60 +- wasm/src/tests.rs | 28 +- 35 files changed, 448 insertions(+), 2847 deletions(-) delete mode 100644 compiler/qsc_codegen/src/qir_base.rs delete mode 100644 compiler/qsc_codegen/src/qir_base/postfix.ll delete mode 100644 compiler/qsc_codegen/src/qir_base/prefix.ll delete mode 100644 compiler/qsc_codegen/src/qir_base/tests.rs delete mode 100644 compiler/qsc_passes/src/baseprofck.rs diff --git a/compiler/qsc/src/bin/qsc.rs b/compiler/qsc/src/bin/qsc.rs index 83bd128a04..9dcfd7334e 100644 --- a/compiler/qsc/src/bin/qsc.rs +++ b/compiler/qsc/src/bin/qsc.rs @@ -6,14 +6,16 @@ allocator::assign_global!(); use clap::{crate_version, ArgGroup, Parser, ValueEnum}; use log::info; use miette::{Context, IntoDiagnostic, Report}; -use qsc::compile::compile; -use qsc_codegen::qir_base; +use qsc::hir::PackageId; +use qsc::{compile::compile, PassContext}; +use qsc_codegen::qir::fir_to_qir; use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; use qsc_frontend::{ compile::{PackageStore, SourceContents, SourceMap, SourceName}, error::WithSource, }; -use qsc_hir::hir::{Package, PackageId}; +use qsc_hir::hir::Package; +use qsc_partial_eval::ProgramEntry; use qsc_passes::PackageType; use qsc_project::{FileSystem, Manifest, StdFs}; use std::{ @@ -24,6 +26,28 @@ use std::{ string::String, }; +#[derive(clap::ValueEnum, Clone, Debug, Default, PartialEq)] +pub enum Profile { + /// This is the default profile, which allows all operations. + #[default] + Unrestricted, + /// This profile restricts the set of operations to those that are supported by the Base profile. + Base, + /// This profile restricts the set of operations to those that are supported by the AdaptiveRI profile. + AdaptiveRI, +} + +// convert Profile into qsc::target::Profile +impl From for qsc::target::Profile { + fn from(profile: Profile) -> Self { + match profile { + Profile::Unrestricted => qsc::target::Profile::Unrestricted, + Profile::Base => qsc::target::Profile::Base, + Profile::AdaptiveRI => qsc::target::Profile::AdaptiveRI, + } + } +} + #[derive(Debug, Parser)] #[command(version = concat!(crate_version!(), " (", env!("QSHARP_GIT_HASH"), ")"), arg_required_else_help(false))] #[clap(group(ArgGroup::new("input").args(["entry", "sources"]).required(false).multiple(true)))] @@ -48,6 +72,10 @@ struct Cli { #[arg(short, long)] entry: Option, + /// Target QIR profile for code generation + #[arg(short, long)] + profile: Option, + /// Q# source files to compile, or `-` to read from stdin. #[arg()] sources: Vec, @@ -72,11 +100,12 @@ fn main() -> miette::Result { let cli = Cli::parse(); let mut store = PackageStore::new(qsc::compile::core()); let mut dependencies = Vec::new(); - - let (package_type, capabilities) = if cli.emit.contains(&Emit::Qir) { - (PackageType::Exe, TargetCapabilityFlags::empty()) + let profile: qsc::target::Profile = cli.profile.unwrap_or_default().into(); + let capabilities = profile.into(); + let package_type = if cli.emit.contains(&Emit::Qir) { + PackageType::Exe } else { - (PackageType::Lib, TargetCapabilityFlags::all()) + PackageType::Lib }; if !cli.nostdlib { @@ -124,8 +153,21 @@ fn main() -> miette::Result { match emit { Emit::Hir => emit_hir(&unit.package, out_dir)?, Emit::Qir => { + if package_type != PackageType::Exe { + eprintln!("QIR generation is only supported for executable packages"); + return Ok(ExitCode::FAILURE); + } + if capabilities == TargetCapabilityFlags::all() { + eprintln!("QIR generation is not supported for unrestricted profile"); + return Ok(ExitCode::FAILURE); + } if errors.is_empty() { - emit_qir(out_dir, &store, package_id)?; + if let Err(reports) = emit_qir(out_dir, &store, package_id, capabilities) { + for report in reports { + eprintln!("{report:?}"); + } + return Ok(ExitCode::FAILURE); + } } } } @@ -172,11 +214,36 @@ fn emit_hir(package: &Package, dir: impl AsRef) -> miette::Result<()> { .with_context(|| format!("could not emit HIR file `{}`", path.display())) } -fn emit_qir(out_dir: &Path, store: &PackageStore, package_id: PackageId) -> Result<(), Report> { - let path = out_dir.join("qir.ll"); - let result = qir_base::generate_qir(store, package_id); - match result { +fn emit_qir( + out_dir: &Path, + store: &PackageStore, + package_id: PackageId, + capabilities: TargetCapabilityFlags, +) -> Result<(), Vec> { + let (fir_store, fir_package_id) = qsc_passes::lower_hir_to_fir(store, package_id); + let package = fir_store.get(fir_package_id); + let entry = ProgramEntry { + exec_graph: package.entry_exec_graph.clone(), + expr: ( + fir_package_id, + package + .entry + .expect("package must have an entry expression"), + ) + .into(), + }; + + let results = PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities); + if results.is_err() { + let errors = results.expect_err("should have errors"); + let errors = errors.into_iter().map(Report::new).collect(); + return Err(errors); + } + let compute_properties = results.expect("should have compute properties"); + + match fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry) { Ok(qir) => { + let path = out_dir.join("qir.ll"); info!( "Writing QIR output file to: {}", path.to_str().unwrap_or_default() @@ -184,10 +251,14 @@ fn emit_qir(out_dir: &Path, store: &PackageStore, package_id: PackageId) -> Resu fs::write(&path, qir) .into_diagnostic() .with_context(|| format!("could not emit QIR file `{}`", path.display())) + .map_err(|err| vec![err]) } - Err((error, _)) => { + Err(error) => { let unit = store.get(package_id).expect("package should be in store"); - Err(Report::new(WithSource::from_map(&unit.sources, error))) + Err(vec![Report::new(WithSource::from_map( + &unit.sources, + error, + ))]) } } } diff --git a/compiler/qsc/src/codegen.rs b/compiler/qsc/src/codegen.rs index 80b893b2cf..7203f9c71f 100644 --- a/compiler/qsc/src/codegen.rs +++ b/compiler/qsc/src/codegen.rs @@ -6,18 +6,23 @@ mod tests; use qsc_codegen::qir::fir_to_qir; use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags}; -use qsc_frontend::compile::{PackageStore, SourceMap}; +use qsc_frontend::{ + compile::{PackageStore, SourceMap}, + error::WithSource, +}; use qsc_partial_eval::ProgramEntry; use qsc_passes::{PackageType, PassContext}; -use qsc_rca::Analyzer; -use crate::compile; +use crate::{compile, interpret::Error}; pub fn get_qir( sources: SourceMap, language_features: LanguageFeatures, capabilities: TargetCapabilityFlags, -) -> Result { +) -> Result> { + if capabilities == TargetCapabilityFlags::all() { + return Err(vec![Error::UnsupportedRuntimeCapabilities]); + } let core = compile::core(); let mut package_store = PackageStore::new(core); let std = compile::std(&package_store, capabilities); @@ -34,15 +39,7 @@ pub fn get_qir( // Ensure it compiles before trying to add it to the store. if !errors.is_empty() { - // 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); + return Err(errors.iter().map(|e| Error::Compile(e.clone())).collect()); } let package_id = package_store.insert(unit); @@ -59,20 +56,26 @@ pub fn get_qir( .into(), }; - 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()); - }; + let compute_properties = + PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities).map_err( + |errors| { + let source_package = package_store + .get(package_id) + .expect("package should be in store"); + errors + .iter() + .map(|e| Error::Pass(WithSource::from_map(&source_package.sources, e.clone()))) + .collect::>() + }, + )?; - fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry) - .map_err(|e| e.to_string()) + fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry).map_err(|e| { + let source_package = package_store + .get(package_id) + .expect("package should be in store"); + vec![Error::PartialEvaluation(WithSource::from_map( + &source_package.sources, + e, + ))] + }) } diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs index 40160e820a..29505a6c46 100644 --- a/compiler/qsc/src/codegen/tests.rs +++ b/compiler/qsc/src/codegen/tests.rs @@ -22,7 +22,37 @@ fn code_with_errors_returns_errors() { expect![[r#" Err( - "Failed to generate QIR. Could not compile sources.:\nsyntax error\n", + [ + Compile( + WithSource { + sources: [ + Source { + name: "test.qs", + contents: "namespace Test {\n @EntryPoint()\n operation Main() : Unit {\n use q = Qubit()\n let pi_over_two = 4.0 / 2.0;\n }\n }", + offset: 0, + }, + ], + error: Frontend( + Error( + Parse( + Error( + Token( + Semi, + Keyword( + Let, + ), + Span { + lo: 129, + hi: 132, + }, + ), + ), + ), + ), + ), + }, + ), + ], ) "#]] .assert_debug_eq(&get_qir(sources, language_features, capabilities)); diff --git a/compiler/qsc/src/compile.rs b/compiler/qsc/src/compile.rs index 246238d232..a0964beafe 100644 --- a/compiler/qsc/src/compile.rs +++ b/compiler/qsc/src/compile.rs @@ -52,7 +52,7 @@ pub fn compile_ast( capabilities, vec![], ); - process_compile_unit(store, package_type, capabilities, unit) + process_compile_unit(store, package_type, unit) } /// Compiles a package from its source representation. @@ -72,7 +72,7 @@ pub fn compile( capabilities, language_features, ); - process_compile_unit(store, package_type, capabilities, unit) + process_compile_unit(store, package_type, unit) } #[must_use] @@ -80,7 +80,6 @@ pub fn compile( fn process_compile_unit( store: &PackageStore, package_type: PackageType, - capabilities: TargetCapabilityFlags, mut unit: CompileUnit, ) -> (CompileUnit, Vec) { let mut errors = Vec::new(); @@ -89,7 +88,7 @@ fn process_compile_unit( } if errors.is_empty() { - for error in run_default_passes(store.core(), &mut unit, package_type, capabilities) { + for error in run_default_passes(store.core(), &mut unit, package_type) { errors.push(WithSource::from_map(&unit.sources, error.into())); } } @@ -126,7 +125,7 @@ pub fn core() -> CompileUnit { #[must_use] pub fn std(store: &PackageStore, capabilities: TargetCapabilityFlags) -> CompileUnit { let mut unit = qsc_frontend::compile::std(store, capabilities); - let pass_errors = run_default_passes(store.core(), &mut unit, PackageType::Lib, capabilities); + let pass_errors = run_default_passes(store.core(), &mut unit, PackageType::Lib); if pass_errors.is_empty() { unit } else { diff --git a/compiler/qsc/src/incremental.rs b/compiler/qsc/src/incremental.rs index 536affb1a4..1629e5e8ef 100644 --- a/compiler/qsc/src/incremental.rs +++ b/compiler/qsc/src/incremental.rs @@ -79,7 +79,7 @@ impl Compiler { store, source_package_id, frontend, - passes: PassContext::new(capabilities), + passes: PassContext::default(), }) } @@ -97,7 +97,7 @@ impl Compiler { store, source_package_id, frontend, - passes: PassContext::new(capabilities), + passes: PassContext::default(), }) } diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs index ffbca883c7..0eacf5104d 100644 --- a/compiler/qsc/src/interpret.rs +++ b/compiler/qsc/src/interpret.rs @@ -40,7 +40,7 @@ use qsc_circuit::{ operations::entry_expr_for_qubit_operation, Builder as CircuitBuilder, Circuit, Config as CircuitConfig, }; -use qsc_codegen::{qir::fir_to_qir, qir_base::BaseProfSim}; +use qsc_codegen::qir::fir_to_qir; use qsc_data_structures::{ functors::FunctorApp, language_features::LanguageFeatures, @@ -409,56 +409,45 @@ impl Interpreter { if self.capabilities == TargetCapabilityFlags::all() { return Err(vec![Error::UnsupportedRuntimeCapabilities]); } - if self.capabilities == TargetCapabilityFlags::empty() { - let mut sim = BaseProfSim::new(); - let mut stdout = std::io::sink(); - let mut out = GenericReceiver::new(&mut stdout); - let val = self.run_with_sim(&mut sim, &mut out, expr)??; + // Compile the expression. This operation will set the expression as + // the entry-point in the FIR store. + let (graph, compute_properties) = self.compile_entry_expr(expr)?; - Ok(sim.finish(&val)) - } else { - // Compile the expression. This operation will set the expression as - // the entry-point in the FIR store. - let (graph, compute_properties) = self.compile_entry_expr(expr)?; - - let Some(compute_properties) = compute_properties else { - // This can only happen if capability analysis was not run. This would be a bug - // and we are in a bad state and can't proceed. - panic!( - "internal error: compute properties not set after lowering entry expression" - ); - }; - let package = self.fir_store.get(self.package); - let entry = ProgramEntry { - exec_graph: graph.into(), - expr: ( - self.package, - package - .entry - .expect("package must have an entry expression"), - ) - .into(), - }; - // Generate QIR - fir_to_qir( - &self.fir_store, - self.capabilities, - Some(compute_properties), - &entry, + let Some(compute_properties) = compute_properties else { + // This can only happen if capability analysis was not run. This would be a bug + // and we are in a bad state and can't proceed. + panic!("internal error: compute properties not set after lowering entry expression"); + }; + let package = self.fir_store.get(self.package); + let entry = ProgramEntry { + exec_graph: graph.into(), + expr: ( + self.package, + package + .entry + .expect("package must have an entry expression"), ) - .map_err(|e| { - let source_package = self - .compiler - .package_store() - .get(map_fir_package_to_hir(self.package)) - .expect("package should exist in the package store"); - vec![Error::PartialEvaluation(WithSource::from_map( - &source_package.sources, - e, - ))] - }) - } + .into(), + }; + // Generate QIR + fir_to_qir( + &self.fir_store, + self.capabilities, + Some(compute_properties), + &entry, + ) + .map_err(|e| { + let source_package = self + .compiler + .package_store() + .get(map_fir_package_to_hir(self.package)) + .expect("package should exist in the package store"); + vec![Error::PartialEvaluation(WithSource::from_map( + &source_package.sources, + e, + ))] + }) } /// Generates a circuit representation for the program. diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs index 3ecba807ec..cb30c785f7 100644 --- a/compiler/qsc/src/interpret/tests.rs +++ b/compiler/qsc/src/interpret/tests.rs @@ -654,7 +654,7 @@ mod given_interpreter { } #[test] - fn normal_qirgen() { + fn base_qirgen() { let mut interpreter = Interpreter::new( true, SourceMap::default(), @@ -674,37 +674,22 @@ mod given_interpreter { %Qubit = type opaque define void @ENTRYPOINT__main() #0 { + block_0: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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__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__qis__cz__body(%Qubit*, %Qubit*) + 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*) + + 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"="1" } attributes #1 = { "irreversible" } @@ -924,37 +909,22 @@ mod given_interpreter { %Qubit = type opaque define void @ENTRYPOINT__main() #0 { + block_0: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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__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__qis__cz__body(%Qubit*, %Qubit*) + 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*) + + 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"="1" } attributes #1 = { "irreversible" } @@ -993,37 +963,22 @@ mod given_interpreter { %Qubit = type opaque define void @ENTRYPOINT__main() #0 { + block_0: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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__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__qis__cz__body(%Qubit*, %Qubit*) + 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*) + + 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"="1" } attributes #1 = { "irreversible" } @@ -1102,37 +1057,22 @@ mod given_interpreter { %Qubit = type opaque define void @ENTRYPOINT__main() #0 { + block_0: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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__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__qis__cz__body(%Qubit*, %Qubit*) + 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*) + + 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"="1" } attributes #1 = { "irreversible" } @@ -1184,37 +1124,22 @@ mod given_interpreter { %Qubit = type opaque define void @ENTRYPOINT__main() #0 { + block_0: call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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__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__qis__cz__body(%Qubit*, %Qubit*) + 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*) + + 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"="1" } attributes #1 = { "irreversible" } @@ -1246,9 +1171,9 @@ mod given_interpreter { is_error( &res, &expect![[r#" - non-Result return type in entry expression - [] [1] - "#]], + cannot use an integer value as an output + [] [1] + "#]], ); } diff --git a/compiler/qsc_codegen/src/lib.rs b/compiler/qsc_codegen/src/lib.rs index 8fb76b4e12..b4023885f0 100644 --- a/compiler/qsc_codegen/src/lib.rs +++ b/compiler/qsc_codegen/src/lib.rs @@ -2,6 +2,5 @@ // Licensed under the MIT License. pub mod qir; -pub mod qir_base; pub mod qsharp; pub mod remapper; diff --git a/compiler/qsc_codegen/src/qir_base.rs b/compiler/qsc_codegen/src/qir_base.rs deleted file mode 100644 index 5a57a5d843..0000000000 --- a/compiler/qsc_codegen/src/qir_base.rs +++ /dev/null @@ -1,552 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#[cfg(test)] -mod tests; - -use crate::remapper::{HardwareId, Remapper}; -use num_bigint::BigUint; -use num_complex::Complex; -use qsc_eval::{ - backend::Backend, debug::Frame, eval, output::GenericReceiver, val::Value, Env, Error, -}; -use qsc_fir::fir; -use qsc_frontend::compile::PackageStore; -use qsc_hir::hir::{self}; -use qsc_lowerer::map_hir_package_to_fir; -use rustc_hash::FxHashSet; -use std::fmt::{Display, Write}; - -/// # Errors -/// -/// This function will return an error if execution was unable to complete. -/// # Panics -/// -/// This function will panic if compiler state is invalid or in out-of-memory conditions. -pub fn generate_qir( - store: &PackageStore, - package: hir::PackageId, -) -> std::result::Result)> { - let mut fir_store = fir::PackageStore::new(); - for (id, unit) in store { - fir_store.insert( - map_hir_package_to_fir(id), - qsc_lowerer::Lowerer::new().lower_package(&unit.package), - ); - } - - let package = map_hir_package_to_fir(package); - let unit = fir_store.get(package); - - let mut sim = BaseProfSim::default(); - let mut stdout = std::io::sink(); - let mut out = GenericReceiver::new(&mut stdout); - let result = eval( - package, - None, - unit.entry_exec_graph.clone(), - &fir_store, - &mut Env::default(), - &mut sim, - &mut out, - ); - match result { - Ok(val) => Ok(sim.finish(&val)), - Err((err, stack)) => Err((err, stack)), - } -} - -pub struct BaseProfSim { - instrs: String, - decls: String, - decl_names: FxHashSet, - remapper: Remapper, -} - -impl Default for BaseProfSim { - fn default() -> Self { - Self::new() - } -} - -impl BaseProfSim { - #[must_use] - pub fn new() -> Self { - let mut sim = BaseProfSim { - instrs: String::new(), - decls: String::new(), - decl_names: FxHashSet::default(), - remapper: Remapper::default(), - }; - sim.instrs.push_str(include_str!("./qir_base/prefix.ll")); - sim - } - - #[must_use] - pub fn finish(mut self, val: &Value) -> String { - for (mapped_q, id) in self.remapper.measurements() { - writeln!( - self.instrs, - " call void @__quantum__qis__mz__body({}, {}) #1", - Qubit(*mapped_q), - Result(*id), - ) - .expect("writing to string should succeed"); - } - self.write_output_recording(val) - .expect("writing to string should succeed"); - - write!( - self.instrs, - include_str!("./qir_base/postfix.ll"), - self.decls, - self.remapper.num_qubits(), - self.remapper.num_measurements() - ) - .expect("writing to string should succeed"); - - self.instrs - } - - fn map(&mut self, qubit: usize) -> HardwareId { - self.remapper.map(qubit) - } - - fn write_output_recording(&mut self, val: &Value) -> std::fmt::Result { - match val { - Value::Array(arr) => { - self.write_array_recording(arr.len())?; - for val in arr.iter() { - self.write_output_recording(val)?; - } - } - Value::Result(r) => { - self.write_result_recording(r.unwrap_id()); - } - Value::Tuple(tup) => { - self.write_tuple_recording(tup.len())?; - for val in tup.iter() { - self.write_output_recording(val)?; - } - } - _ => panic!("unexpected value type: {val:?}"), - } - Ok(()) - } - - fn write_result_recording(&mut self, res: usize) { - writeln!( - self.instrs, - " call void @__quantum__rt__result_record_output({}, i8* null)", - Result(res), - ) - .expect("writing to string should succeed"); - } - - fn write_tuple_recording(&mut self, size: usize) -> std::fmt::Result { - writeln!( - self.instrs, - " call void @__quantum__rt__tuple_record_output(i64 {size}, i8* null)" - ) - } - - fn write_array_recording(&mut self, size: usize) -> std::fmt::Result { - writeln!( - self.instrs, - " call void @__quantum__rt__array_record_output(i64 {size}, i8* null)" - ) - } - - fn write_arg(&mut self, arg: &Value) -> std::result::Result<(), String> { - match arg { - Value::Qubit(q) => { - let q = self.map(q.0); - write!(self.instrs, "{}", Qubit(q)) - } - Value::Double(d) => write!(self.instrs, "{}", Double(*d)), - Value::Bool(b) => write!(self.instrs, "{}", Bool(*b)), - Value::Int(i) => write!(self.instrs, "{}", Int(*i)), - _ => return Err(format!("unsupported argument type: {}", arg.type_name())), - } - .expect("writing to string should succeed"); - Ok(()) - } - - fn write_decl_type(&mut self, ty: &Value) -> std::result::Result<(), String> { - match ty { - Value::Qubit(_) => write!(self.decls, "%Qubit*"), - Value::Double(_) => write!(self.decls, "double"), - Value::Bool(_) => write!(self.decls, "i1"), - Value::Int(_) => write!(self.decls, "i64"), - _ => return Err(format!("unsupported argument type: {}", ty.type_name())), - } - .expect("writing to string should succeed"); - Ok(()) - } - - fn write_decl(&mut self, name: &str, arg: &Value) -> std::result::Result<(), String> { - if self.decl_names.insert(name.to_string()) { - write!(self.decls, "declare void @{name}(").expect("writing to string should succeed"); - if let Value::Tuple(args) = arg { - if let Some((first, rest)) = args.split_first() { - self.write_decl_type(first)?; - for arg in rest { - write!(self.decls, ", ").expect("writing to string should succeed"); - self.write_decl_type(arg)?; - } - } - } else { - self.write_decl_type(arg)?; - } - writeln!(self.decls, ")").expect("writing to string should succeed"); - } - - Ok(()) - } -} - -impl Backend for BaseProfSim { - type ResultType = usize; - - fn ccx(&mut self, ctl0: usize, ctl1: usize, q: usize) { - let ctl0 = self.map(ctl0); - let ctl1 = self.map(ctl1); - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__ccx__body({}, {}, {})", - Qubit(ctl0), - Qubit(ctl1), - Qubit(q) - ) - .expect("writing to string should succeed"); - } - - fn cx(&mut self, ctl: usize, q: usize) { - let ctl = self.map(ctl); - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__cx__body({}, {})", - Qubit(ctl), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn cy(&mut self, ctl: usize, q: usize) { - let ctl = self.map(ctl); - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__cy__body({}, {})", - Qubit(ctl), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn cz(&mut self, ctl: usize, q: usize) { - let ctl = self.map(ctl); - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__cz__body({}, {})", - Qubit(ctl), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn h(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__h__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn m(&mut self, q: usize) -> Self::ResultType { - // Measurements are tracked separately from instructions, so that they can be - // deferred until the end of the program. - self.remapper.mreset(q) - } - - fn mresetz(&mut self, q: usize) -> Self::ResultType { - self.remapper.mreset(q) - } - - fn reset(&mut self, q: usize) { - // Reset is a no-op in Base Profile, but does force qubit remapping so that future - // operations on the given qubit id are performed on a fresh qubit. Clear the entry in the map - // so it is known to require remapping on next use. - self.remapper.reset(q); - } - - fn rx(&mut self, theta: f64, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__rx__body({}, {})", - Double(theta), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn rxx(&mut self, theta: f64, q0: usize, q1: usize) { - let q0 = self.map(q0); - let q1 = self.map(q1); - writeln!( - self.instrs, - " call void @__quantum__qis__rxx__body({}, {}, {})", - Double(theta), - Qubit(q0), - Qubit(q1), - ) - .expect("writing to string should succeed"); - } - - fn ry(&mut self, theta: f64, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__ry__body({}, {})", - Double(theta), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn ryy(&mut self, theta: f64, q0: usize, q1: usize) { - let q0 = self.map(q0); - let q1 = self.map(q1); - writeln!( - self.instrs, - " call void @__quantum__qis__ryy__body({}, {}, {})", - Double(theta), - Qubit(q0), - Qubit(q1), - ) - .expect("writing to string should succeed"); - } - - fn rz(&mut self, theta: f64, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__rz__body({}, {})", - Double(theta), - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn rzz(&mut self, theta: f64, q0: usize, q1: usize) { - let q0 = self.map(q0); - let q1 = self.map(q1); - writeln!( - self.instrs, - " call void @__quantum__qis__rzz__body({}, {}, {})", - Double(theta), - Qubit(q0), - Qubit(q1), - ) - .expect("writing to string should succeed"); - } - - fn sadj(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__s__adj({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn s(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__s__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn swap(&mut self, q0: usize, q1: usize) { - let q0 = self.map(q0); - let q1 = self.map(q1); - writeln!( - self.instrs, - " call void @__quantum__qis__swap__body({}, {})", - Qubit(q0), - Qubit(q1), - ) - .expect("writing to string should succeed"); - } - - fn tadj(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__t__adj({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn t(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__t__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn x(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__x__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn y(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__y__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn z(&mut self, q: usize) { - let q = self.map(q); - writeln!( - self.instrs, - " call void @__quantum__qis__z__body({})", - Qubit(q), - ) - .expect("writing to string should succeed"); - } - - fn qubit_allocate(&mut self) -> usize { - self.remapper.qubit_allocate() - } - - fn qubit_release(&mut self, q: usize) { - self.remapper.qubit_release(q); - } - - fn capture_quantum_state(&mut self) -> (Vec<(BigUint, Complex)>, usize) { - (Vec::new(), 0) - } - - fn qubit_is_zero(&mut self, _q: usize) -> bool { - // Because `qubit_is_zero` is called on every qubit release, this must return - // true to avoid a panic. - true - } - - fn custom_intrinsic( - &mut self, - name: &str, - arg: Value, - ) -> Option> { - // Global phase is a special case that is non-physical, so there is no need to generate - // a call here, just do a shortcut return. - if name == "GlobalPhase" { - return Some(Ok(Value::unit())); - } - - match self.write_decl(name, &arg) { - Ok(()) => {} - Err(e) => return Some(Err(e)), - } - write!(self.instrs, " call void @{name}(").expect("writing to string should succeed"); - - if let Value::Tuple(args) = arg { - if let Some((first, rest)) = args.split_first() { - match self.write_arg(first) { - Ok(()) => {} - Err(e) => return Some(Err(e)), - } - for arg in rest { - write!(self.instrs, ", ").expect("writing to string should succeed"); - match self.write_arg(arg) { - Ok(()) => {} - Err(e) => return Some(Err(e)), - } - } - } - } else { - match self.write_arg(&arg) { - Ok(()) => {} - Err(e) => return Some(Err(e)), - } - } - - writeln!(self.instrs, ")").expect("writing to string should succeed"); - Some(Ok(Value::unit())) - } -} - -struct Qubit(HardwareId); - -impl Display for Qubit { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "%Qubit* inttoptr (i64 {} to %Qubit*)", self.0 .0) - } -} - -struct Result(usize); - -impl Display for Result { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "%Result* inttoptr (i64 {} to %Result*)", self.0) - } -} - -struct Double(f64); - -impl Display for Double { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let v = self.0; - if (v.floor() - v.ceil()).abs() < f64::EPSILON { - // The value is a whole number, which requires at least one decimal point - // to differentiate it from an integer value. - write!(f, "double {v:.1}") - } else { - write!(f, "double {v}") - } - } -} - -struct Bool(bool); - -impl Display for Bool { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.0 { - write!(f, "i1 true") - } else { - write!(f, "i1 false") - } - } -} - -struct Int(i64); - -impl Display for Int { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "i64 {}", self.0) - } -} diff --git a/compiler/qsc_codegen/src/qir_base/postfix.ll b/compiler/qsc_codegen/src/qir_base/postfix.ll deleted file mode 100644 index b21a1c4266..0000000000 --- a/compiler/qsc_codegen/src/qir_base/postfix.ll +++ /dev/null @@ -1,38 +0,0 @@ - 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"="{}" "required_num_results"="{}" }} -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_base/prefix.ll b/compiler/qsc_codegen/src/qir_base/prefix.ll deleted file mode 100644 index 4165317910..0000000000 --- a/compiler/qsc_codegen/src/qir_base/prefix.ll +++ /dev/null @@ -1,4 +0,0 @@ -%Result = type opaque -%Qubit = type opaque - -define void @ENTRYPOINT__main() #0 { diff --git a/compiler/qsc_codegen/src/qir_base/tests.rs b/compiler/qsc_codegen/src/qir_base/tests.rs deleted file mode 100644 index 8cf1af5fec..0000000000 --- a/compiler/qsc_codegen/src/qir_base/tests.rs +++ /dev/null @@ -1,1657 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#![allow(clippy::too_many_lines)] -#![allow(clippy::needless_raw_string_hashes)] - -use std::sync::Arc; - -use expect_test::{expect, Expect}; -use indoc::indoc; -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; - -fn check(program: &str, expr: Option<&str>, expect: &Expect) { - let mut core = compile::core(); - assert!(run_core_passes(&mut core).is_empty()); - let mut store = PackageStore::new(core); - let mut std = compile::std(&store, TargetCapabilityFlags::empty()); - assert!(run_default_passes( - store.core(), - &mut std, - PackageType::Lib, - TargetCapabilityFlags::empty() - ) - .is_empty()); - let std = store.insert(std); - - let expr_as_arc: Option> = expr.map(|s| Arc::from(s.to_string())); - let sources = SourceMap::new([("test".into(), program.into())], expr_as_arc); - - let mut unit = compile( - &store, - &[std], - sources, - TargetCapabilityFlags::empty(), - LanguageFeatures::default(), - ); - assert!(unit.errors.is_empty(), "{:?}", unit.errors); - assert!(run_default_passes( - store.core(), - &mut unit, - PackageType::Exe, - TargetCapabilityFlags::empty() - ) - .is_empty()); - let package = store.insert(unit); - - let qir = generate_qir(&store, package); - match qir { - Ok(qir) => expect.assert_eq(&qir), - Err((err, _)) => expect.assert_debug_eq(&err), - } -} - -#[test] -fn simple_entry_program_is_valid() { - check( - indoc! {r#" - namespace Sample { - @EntryPoint() - operation Entry() : Result - { - use q = Qubit(); - H(q); - M(q) - } - } - "#}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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"="2" "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} - "#]], - ); -} - -#[test] -fn simple_program_is_valid() { - check( - "", - Some(indoc! {r#" - { - use q = Qubit(); - H(q); - M(q) - } - "#}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 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"="2" "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} - "#]], - ); -} - -#[test] -fn output_recording_array() { - check( - "", - Some(indoc! {"{use q = Qubit(); [M(q), M(q)]}"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - 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__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"="3" "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} - "#]], - ); -} - -#[test] -fn output_recording_tuple() { - check( - "", - Some(indoc! {"{use q = Qubit(); (M(q), M(q))}"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - 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__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"="3" "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} - "#]], - ); -} - -#[test] -fn reset_allocates_new_qubit_id() { - check( - "", - Some(indoc! {"{use q = Qubit(); H(q); Reset(q); H(q); M(q)}"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 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"="3" "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} - "#]], - ); -} - -#[test] -fn reuse_after_measurement_uses_fresh_aux_qubit_id() { - check( - "", - Some(indoc! {"{use q = Qubit(); H(q); M(q); H(q); M(q)}"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 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"="3" "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} - "#]], - ); -} - -#[test] -fn qubit_allocation_allows_reuse_of_unmeasured_qubits() { - check( - "", - Some(indoc! {"{ - { use (c, q) = (Qubit(), Qubit()); CNOT(c, q); MResetZ(q); } - { use (c, q) = (Qubit(), Qubit()); CNOT(c, q); MResetZ(q) } - }"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 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"="3" "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} - "#]], - ); -} - -#[test] -fn verify_all_intrinsics() { - check( - "", - Some(indoc! {"{ - use (q1, q2, q3) = (Qubit(), Qubit(), Qubit()); - CCNOT(q1, q2, q3); - CX(q1, q2); - CY(q1, q2); - CZ(q1, q2); - Rx(0.0, q1); - Rxx(0.0, q1, q2); - Ry(0.0, q1); - Ryy(0.0, q1, q2); - Rz(0.0, q1); - Rzz(0.0, q1, q2); - H(q1); - S(q1); - Adjoint S(q1); - T(q1); - Adjoint T(q1); - X(q1); - Y(q1); - Z(q1); - SWAP(q1, q2); - Reset(q1); - (M(q1), - Microsoft.Quantum.Measurement.MResetZ(q1)) - }"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cy__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__rx__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__rxx__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__ry__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__ryy__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__rzz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__s__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__s__adj(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__y__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__z__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__swap__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - 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__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"="5" "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} - "#]], - ); -} - -#[test] -fn complex_program_is_valid() { - check( - "", - Some(indoc! {"{ - open Microsoft.Quantum.Math; - - operation SWAPfromExp(q1 : Qubit, q2 : Qubit) : Unit is Ctl + Adj { - let theta = PI() / 4.0; - Exp([PauliX, PauliX], theta, [q1, q2]); - Exp([PauliY, PauliY], theta, [q1, q2]); - Exp([PauliZ, PauliZ], theta, [q1, q2]); - } - - use (aux, ctls, qs) = (Qubit(), Qubit[3], Qubit[2]); - within { - H(aux); - ApplyToEachA(CNOT(aux, _), ctls + qs); - } - apply { - Controlled SWAPfromExp(ctls, (qs[0], qs[1])); - - Controlled Adjoint SWAP(ctls, (qs[0], qs[1])); - } - - MResetEachZ([aux] + ctls + qs) - }"}), - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__s__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__s__adj(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__t__body(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Result* inttoptr (i64 4 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 5 to %Qubit*), %Result* inttoptr (i64 5 to %Result*)) #1 - call void @__quantum__rt__array_record_output(i64 6, 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) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 3 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 4 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 5 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"="8" "required_num_results"="6" } - 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} - "#]], - ); -} - -#[test] -fn qubit_ids_properly_reused() { - check( - indoc! {" - namespace Test { - - open Microsoft.Quantum.Intrinsic; - - // Verifies the use of the CNOT quantum gate from Q#'s Microsoft.Quantum.Intrinsic namespace. - // Expected simulation output: ([0, 0], [1, 1]). - @EntryPoint() - operation IntrinsicCNOT() : (Result[], Result[]) { - use registerA = Qubit[2]; // |00⟩ - CNOT(registerA[0], registerA[1]); // |00⟩ - let resultsA = MeasureEachZ(registerA); - ResetAll(registerA); - - use registerB = Qubit[2]; // |00⟩ - X(registerB[0]); // |10⟩ - CNOT(registerB[0], registerB[1]); // |11⟩ - let resultsB = MeasureEachZ(registerB); - ResetAll(registerB); - - return (resultsA, resultsB); - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) - call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 6 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Qubit* inttoptr (i64 5 to %Qubit*)) - call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 7 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 6 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 7 to %Qubit*), %Result* inttoptr (i64 3 to %Result*)) #1 - call void @__quantum__rt__tuple_record_output(i64 2, i8* null) - 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) - call void @__quantum__rt__array_record_output(i64 2, i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) - call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 3 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"="8" "required_num_results"="4" } - 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} - "#]], - ); -} - -#[test] -fn custom_intrinsic_on_single_qubit() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : (Result, Result) { - use (q1, q2) = (Qubit(), Qubit()); - MyCustomGate(q1); - MyCustomGate(q2); - return (MResetZ(q1), MResetZ(q2)); - } - - operation MyCustomGate(q : Qubit) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @MyCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @MyCustomGate(%Qubit* inttoptr (i64 1 to %Qubit*)) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - 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__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*) - declare void @MyCustomGate(%Qubit*) - - 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} - "#]], - ); -} - -#[test] -fn multiple_custom_intrinsic_calls() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : (Result, Result) { - use (q0, q1) = (Qubit(), Qubit()); - MySecondCustomGate(q0, q1); - MyCustomGate(q0); - MyCustomFunc(42); - return (MResetZ(q0), MResetZ(q1)); - } - - operation MyCustomGate(q : Qubit) : Unit { - body intrinsic; - } - - operation MySecondCustomGate(q0 : Qubit, q1 : Qubit) : Unit { - body intrinsic; - } - - function MyCustomFunc(n : Int) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @MySecondCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) - call void @MyCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*)) - call void @MyCustomFunc(i64 42) - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 - call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 - 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__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*) - declare void @MySecondCustomGate(%Qubit*, %Qubit*) - declare void @MyCustomGate(%Qubit*) - declare void @MyCustomFunc(i64) - - 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} - "#]], - ); -} - -#[test] -fn custom_intrinsic_on_qubit_and_double() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let d = 3.14; - MyCustomGate(q, d); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, d : Double) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @MyCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*), double 3.14) - 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*) - declare void @MyCustomGate(%Qubit*, double) - - 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} - "#]], - ); -} - -#[test] -fn custom_intrinsic_on_qubit_and_bool() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let b = true; - MyCustomGate(q, b); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, b : Bool) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @MyCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*), i1 true) - 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*) - declare void @MyCustomGate(%Qubit*, i1) - - 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} - "#]], - ); -} - -#[test] -fn custom_intrinsic_on_qubit_and_int() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let i = 42; - MyCustomGate(q, i); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, i : Int) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - call void @MyCustomGate(%Qubit* inttoptr (i64 0 to %Qubit*), i64 42) - 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*) - declare void @MyCustomGate(%Qubit*, i64) - - 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} - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_result_arg() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let r = MResetZ(q); - MyCustomGate(q, r); - return r; - } - - operation MyCustomGate(q : Qubit, r : Result) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - IntrinsicFail( - "MyCustomGate", - "unsupported argument type: Result", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 177, - hi: 261, - }, - }, - ) - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_bigint_arg() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let i = 42L; - MyCustomGate(q, i); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, i : BigInt) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - IntrinsicFail( - "MyCustomGate", - "unsupported argument type: BigInt", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 179, - hi: 263, - }, - }, - ) - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_string_arg() { - check( - indoc! {r#" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let s = "hello, world"; - MyCustomGate(q, s); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, s : String) : Unit { - body intrinsic; - } - } - "#}, - None, - &expect![[r#" - IntrinsicFail( - "MyCustomGate", - "unsupported argument type: String", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 190, - hi: 274, - }, - }, - ) - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_array_arg() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let a = [1, 2, 3]; - MyCustomGate(q, a); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, a : Int[]) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - IntrinsicFail( - "MyCustomGate", - "unsupported argument type: Array", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 185, - hi: 268, - }, - }, - ) - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_tuple_arg() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - let t = (1, 2, 3); - MyCustomGate(q, t); - return MResetZ(q); - } - - operation MyCustomGate(q : Qubit, t : (Int, Int, Int)) : Unit { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - IntrinsicFail( - "MyCustomGate", - "unsupported argument type: Tuple", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 185, - hi: 278, - }, - }, - ) - "#]], - ); -} - -#[test] -fn custom_intrinsic_fail_on_non_unit_return() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - MyCustomGate(q); - return MResetZ(q); - } - - function MyCustomGate(q : Qubit) : Int { - body intrinsic; - } - } - "}, - None, - &expect![[r#" - UnsupportedIntrinsicType( - "MyCustomGate", - PackageSpan { - package: PackageId( - 2, - ), - span: Span { - lo: 155, - hi: 225, - }, - }, - ) - "#]], - ); -} - -#[test] -fn pauli_i_rotation_for_global_phase_is_noop() { - check( - indoc! {" - namespace Test { - @EntryPoint() - operation Test() : Result { - use q = Qubit(); - R(PauliI, 1.0, q); - return MResetZ(q); - } - } - "}, - None, - &expect![[r#" - %Result = type opaque - %Qubit = type opaque - - define void @ENTRYPOINT__main() #0 { - 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} - !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/qsharp/test_utils.rs b/compiler/qsc_codegen/src/qsharp/test_utils.rs index e13d728e7a..3fb9acec67 100644 --- a/compiler/qsc_codegen/src/qsharp/test_utils.rs +++ b/compiler/qsc_codegen/src/qsharp/test_utils.rs @@ -34,13 +34,7 @@ pub(crate) fn get_compilation(sources: Option) -> (PackageId, Package assert!(run_core_passes(&mut core).is_empty()); let mut store = PackageStore::new(core); let mut std = compile::std(&store, TargetCapabilityFlags::empty()); - assert!(run_default_passes( - store.core(), - &mut std, - PackageType::Lib, - TargetCapabilityFlags::empty() - ) - .is_empty()); + assert!(run_default_passes(store.core(), &mut std, PackageType::Lib).is_empty()); let std = store.insert(std); let mut unit = compile( @@ -51,13 +45,7 @@ pub(crate) fn get_compilation(sources: Option) -> (PackageId, Package LanguageFeatures::empty(), ); assert!(unit.errors.is_empty(), "{:?}", unit.errors); - assert!(run_default_passes( - store.core(), - &mut unit, - PackageType::Lib, - TargetCapabilityFlags::all() - ) - .is_empty()); + assert!(run_default_passes(store.core(), &mut unit, PackageType::Lib,).is_empty()); let package_id = store.insert(unit); (package_id, store) } diff --git a/compiler/qsc_data_structures/src/language_features.rs b/compiler/qsc_data_structures/src/language_features.rs index e6a4094caf..f3d5105c9f 100644 --- a/compiler/qsc_data_structures/src/language_features.rs +++ b/compiler/qsc_data_structures/src/language_features.rs @@ -10,7 +10,6 @@ pub struct LanguageFeatures(u8); bitflags! { impl LanguageFeatures: u8 { const V2PreviewSyntax = 0b1; - const PreviewQirGen = 0b10; } } @@ -34,7 +33,6 @@ where iter.into_iter().fold(LanguageFeatures::empty(), |acc, x| { acc | match x.as_ref() { "v2-preview-syntax" => LanguageFeatures::V2PreviewSyntax, - "preview-qir-gen" => LanguageFeatures::PreviewQirGen, _ => LanguageFeatures::empty(), } }) diff --git a/compiler/qsc_eval/src/intrinsic/tests.rs b/compiler/qsc_eval/src/intrinsic/tests.rs index 9aa50bc015..cca7842d7f 100644 --- a/compiler/qsc_eval/src/intrinsic/tests.rs +++ b/compiler/qsc_eval/src/intrinsic/tests.rs @@ -154,13 +154,7 @@ fn check_intrinsic(file: &str, expr: &str, out: &mut impl Receiver) -> Result Result fmt::Result { let mut indent = set_indentation(indented(f), 0); - write!(indent, "SpecImpl:",)?; + write!(indent, "SpecImpl:")?; indent = set_indentation(indent, 1); write!(indent, "\nbody: {}", self.body)?; match &self.adj { diff --git a/compiler/qsc_passes/src/baseprofck.rs b/compiler/qsc_passes/src/baseprofck.rs deleted file mode 100644 index 73650d3344..0000000000 --- a/compiler/qsc_passes/src/baseprofck.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#[cfg(test)] -mod tests; - -use miette::Diagnostic; -use qsc_data_structures::span::Span; -use qsc_hir::{ - hir::{BinOp, CallableKind, Expr, ExprKind, Item, ItemKind, Lit, Package, SpecBody, SpecGen}, - ty::{Prim, Ty}, - visit::{walk_expr, walk_item, Visitor}, -}; -use thiserror::Error; - -#[derive(Clone, Debug, Diagnostic, Error)] -pub enum Error { - #[error("cannot compare measurement results")] - #[diagnostic(help( - "comparing measurement results is not supported when performing base profile QIR generation" - ))] - #[diagnostic(code("Qsc.BaseProfCk.ResultComparison"))] - ResultComparison(#[label] Span), - - #[error("result literals are not supported")] - #[diagnostic(help( - "result literals `One` and `Zero` are not supported when performing base profile QIR generation" - ))] - #[diagnostic(code("Qsc.BaseProfCk.ResultLiteral"))] - ResultLiteral(#[label] Span), - - #[error("non-Result return type in entry expression")] - #[diagnostic(help( - "returning types other than Result from the entry expression is not supported when performing base profile QIR generation" - ))] - #[diagnostic(code("Qsc.BaseProfCk.ReturnNonResult"))] - ReturnNonResult(#[label] Span), - - #[error("intrinsic operations that return types other than Result or Unit are not supported")] - #[diagnostic(help( - "intrinsic operations that return types other than Result or Unit are not supported when performing base profile QIR generation" - ))] - #[diagnostic(code("Qsc.BaseProfCk.UnsupportedIntrinsic"))] - UnsupportedIntrinsic(#[label] Span), -} - -#[must_use] -pub fn check_base_profile_compliance(package: &Package) -> Vec { - let mut checker = Checker { errors: Vec::new() }; - if let Some(entry) = &package.entry { - if any_non_result_ty(&entry.ty) { - checker.errors.push(Error::ReturnNonResult(entry.span)); - } - } - checker.visit_package(package); - - checker.errors -} - -struct Checker { - errors: Vec, -} - -impl<'a> Visitor<'a> for Checker { - fn visit_item(&mut self, item: &'a Item) { - match &item.kind { - ItemKind::Callable(callable) - if callable.kind == CallableKind::Operation - && callable.body.body == SpecBody::Gen(SpecGen::Intrinsic) - && callable.output != Ty::Prim(Prim::Result) - && callable.output != Ty::Prim(Prim::Qubit) - && callable.output != Ty::UNIT => - { - self.errors - .push(Error::UnsupportedIntrinsic(callable.name.span)); - } - _ => {} - } - - walk_item(self, item); - } - - fn visit_expr(&mut self, expr: &'a Expr) { - match &expr.kind { - ExprKind::BinOp(BinOp::Eq | BinOp::Neq, lhs, _) if any_result_ty(&lhs.ty) => { - self.errors.push(Error::ResultComparison(expr.span)); - } - ExprKind::Lit(Lit::Result(_)) => { - self.errors.push(Error::ResultLiteral(expr.span)); - } - _ => {} - } - walk_expr(self, expr); - } -} - -fn any_result_ty(ty: &Ty) -> bool { - match ty { - Ty::Array(ty) => any_result_ty(ty), - Ty::Prim(Prim::Result) => true, - Ty::Tuple(tys) => tys.iter().any(any_result_ty), - _ => false, - } -} - -fn any_non_result_ty(ty: &Ty) -> bool { - match ty { - Ty::Array(ty) => any_non_result_ty(ty), - Ty::Prim(Prim::Result) => false, - Ty::Tuple(tys) if tys.is_empty() => true, - Ty::Tuple(tys) => tys.iter().any(any_non_result_ty), - _ => true, - } -} diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs index 948b6f493b..f142605c9c 100644 --- a/compiler/qsc_passes/src/lib.rs +++ b/compiler/qsc_passes/src/lib.rs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -mod baseprofck; mod borrowck; mod callable_limits; mod capabilitiesck; @@ -43,7 +42,6 @@ pub(crate) static QIR_RUNTIME_NAMESPACE: &[&str] = &["QIR", "Runtime"]; #[diagnostic(transparent)] #[error(transparent)] pub enum Error { - BaseProfCk(baseprofck::Error), BorrowCk(borrowck::Error), CallableLimits(callable_limits::Error), CapabilitiesCk(qsc_rca::errors::Error), @@ -69,15 +67,19 @@ pub fn lower_hir_to_fir( } pub struct PassContext { - capabilities: TargetCapabilityFlags, borrow_check: borrowck::Checker, } +impl Default for PassContext { + fn default() -> Self { + Self::new() + } +} + impl PassContext { #[must_use] - pub fn new(capabilities: TargetCapabilityFlags) -> Self { + pub fn new() -> Self { Self { - capabilities, borrow_check: borrowck::Checker::default(), } } @@ -117,12 +119,6 @@ impl PassContext { ReplaceQubitAllocation::new(core, assigner).visit_package(package); Validator::default().visit_package(package); - let base_prof_errors = if self.capabilities == TargetCapabilityFlags::empty() { - baseprofck::check_base_profile_compliance(package) - } else { - Vec::new() - }; - callable_errors .into_iter() .map(Error::CallableLimits) @@ -130,7 +126,6 @@ impl PassContext { .chain(spec_errors.into_iter().map(Error::SpecGen)) .chain(conjugate_errors.into_iter().map(Error::ConjInvert)) .chain(entry_point_errors) - .chain(base_prof_errors.into_iter().map(Error::BaseProfCk)) .collect() } @@ -148,14 +143,8 @@ pub fn run_default_passes( core: &Table, unit: &mut CompileUnit, package_type: PackageType, - capabilities: TargetCapabilityFlags, ) -> Vec { - PassContext::new(capabilities).run_default_passes( - &mut unit.package, - &mut unit.assigner, - core, - package_type, - ) + PassContext::new().run_default_passes(&mut unit.package, &mut unit.assigner, core, package_type) } pub fn run_core_passes(core: &mut CompileUnit) -> Vec { @@ -174,13 +163,7 @@ pub fn run_core_passes(core: &mut CompileUnit) -> Vec { ReplaceQubitAllocation::new(&table, &mut core.assigner).visit_package(&mut core.package); Validator::default().visit_package(&core.package); - let base_prof_errors = baseprofck::check_base_profile_compliance(&core.package); - - borrow_errors - .into_iter() - .map(Error::BorrowCk) - .chain(base_prof_errors.into_iter().map(Error::BaseProfCk)) - .collect() + borrow_errors.into_iter().map(Error::BorrowCk).collect() } pub fn run_fir_passes( diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs index 35f919fa94..a3a61602ca 100644 --- a/language_service/src/compilation.rs +++ b/language_service/src/compilation.rs @@ -257,11 +257,6 @@ fn run_fir_passes( return; } - if target_profile == Profile::Base { - // baseprofchk will handle the case where the target profile is Base - return; - } - if target_profile == Profile::Unrestricted { // no point in running passes on unrestricted profile return; diff --git a/language_service/src/protocol.rs b/language_service/src/protocol.rs index 2ec8c97939..de37d4e376 100644 --- a/language_service/src/protocol.rs +++ b/language_service/src/protocol.rs @@ -10,6 +10,7 @@ use qsc_project::Manifest; pub struct WorkspaceConfigurationUpdate { pub target_profile: Option, pub package_type: Option, + pub language_features: Option, } #[derive(Debug)] diff --git a/language_service/src/state.rs b/language_service/src/state.rs index fa40339fc6..0706bfd9dd 100644 --- a/language_service/src/state.rs +++ b/language_service/src/state.rs @@ -91,9 +91,9 @@ struct PartialConfiguration { } impl PartialConfiguration { - pub fn from_language_features(features: LanguageFeatures) -> Self { + pub fn from_language_features(features: Option) -> Self { Self { - language_features: Some(features), + language_features: features, ..Default::default() } } @@ -128,7 +128,7 @@ pub(super) struct CompilationStateUpdater<'a> { struct LoadManifestResult { compilation_uri: Arc, sources: Vec<(Arc, Arc)>, - language_features: LanguageFeatures, + language_features: Option, lints: Vec, } @@ -180,7 +180,7 @@ impl<'a> CompilationStateUpdater<'a> { LoadManifestResult { compilation_uri: doc_uri.clone(), sources: vec![(doc_uri.clone(), text.clone())], - language_features: LanguageFeatures::default(), + language_features: None, lints: Vec::default(), } }); @@ -228,11 +228,13 @@ impl<'a> CompilationStateUpdater<'a> { Ok(o) => Some(LoadManifestResult { compilation_uri: manifest.compilation_uri(), sources: o.sources, - language_features: manifest - .manifest - .language_features - .iter() - .collect::(), + language_features: Some( + manifest + .manifest + .language_features + .iter() + .collect::(), + ), lints: manifest.manifest.lints.clone(), }), Err(e) => { @@ -254,7 +256,7 @@ impl<'a> CompilationStateUpdater<'a> { &mut self, mut sources: Vec<(Arc, Arc)>, compilation_uri: &Arc, - language_features: LanguageFeatures, + language_features: Option, lints_config: &[LintConfig], ) { self.with_state_mut(|state| { @@ -268,20 +270,22 @@ impl<'a> CompilationStateUpdater<'a> { } } + let compilation_overrides = + PartialConfiguration::from_language_features(language_features); + + let configuration = merge_configurations(&compilation_overrides, &self.configuration); + let compilation = Compilation::new( &sources, - self.configuration.package_type, - self.configuration.target_profile, - language_features, + configuration.package_type, + configuration.target_profile, + configuration.language_features, lints_config, ); state.compilations.insert( compilation_uri.clone(), - ( - compilation, - PartialConfiguration::from_language_features(language_features), - ), + (compilation, compilation_overrides), ); }); } @@ -478,6 +482,11 @@ impl<'a> CompilationStateUpdater<'a> { self.configuration.target_profile = target_profile; } + if let Some(language_features) = configuration.language_features { + need_recompile |= self.configuration.language_features != language_features; + self.configuration.language_features = language_features; + } + // Possible optimization: some projects will have overrides for these configurations, // so workspace updates won't impact them. We could exclude those projects // from recompilation, but we don't right now. @@ -493,9 +502,6 @@ impl<'a> CompilationStateUpdater<'a> { for (compilation, package_specific_configuration) in state.compilations.values_mut() { let configuration = merge_configurations(package_specific_configuration, &self.configuration); - let language_features = package_specific_configuration - .language_features - .unwrap_or_default(); let lints_config = package_specific_configuration .lints_config .clone() @@ -503,7 +509,7 @@ impl<'a> CompilationStateUpdater<'a> { compilation.recompile( configuration.package_type, configuration.target_profile, - language_features, + configuration.language_features, &lints_config, ); } diff --git a/language_service/src/state/tests.rs b/language_service/src/state/tests.rs index ecc93fd48c..a215d8d5fa 100644 --- a/language_service/src/state/tests.rs +++ b/language_service/src/state/tests.rs @@ -314,6 +314,7 @@ async fn rca_errors_are_reported_when_compilation_succeeds() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: Some(Profile::AdaptiveRI), package_type: Some(PackageType::Lib), + language_features: None, }); updater @@ -358,6 +359,69 @@ async fn rca_errors_are_reported_when_compilation_succeeds() { ); } +#[tokio::test] +async fn base_profile_rca_errors_are_reported_when_compilation_succeeds() { + let errors = RefCell::new(Vec::new()); + let mut updater = new_updater(&errors); + + updater.update_configuration(WorkspaceConfigurationUpdate { + target_profile: Some(Profile::Base), + package_type: Some(PackageType::Lib), + language_features: None, + }); + + updater + .update_document("single/foo.qs", 1, "namespace Test { operation RcaCheck() : Double { use q = Qubit(); mutable x = 1.0; if MResetZ(q) == One { set x = 2.0; } x } }") + .await; + + // we expect two errors, one for `set x = 2.0` and one for `x` + expect_errors( + &errors, + &expect![[r#" + [ + ( + "single/foo.qs", + Some( + 1, + ), + [ + Pass( + CapabilitiesCk( + UseOfDynamicBool( + Span { + lo: 86, + hi: 103, + }, + ), + ), + ), + Pass( + CapabilitiesCk( + UseOfDynamicDouble( + Span { + lo: 106, + hi: 117, + }, + ), + ), + ), + Pass( + CapabilitiesCk( + UseOfDynamicDouble( + Span { + lo: 121, + hi: 122, + }, + ), + ), + ), + ], + ), + ] + "#]], + ); +} + #[tokio::test] async fn package_type_update_causes_error() { let errors = RefCell::new(Vec::new()); @@ -366,6 +430,7 @@ async fn package_type_update_causes_error() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: None, package_type: Some(PackageType::Lib), + language_features: None, }); updater @@ -386,6 +451,7 @@ async fn package_type_update_causes_error() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: None, package_type: Some(PackageType::Exe), + language_features: None, }); expect_errors( @@ -418,13 +484,14 @@ async fn target_profile_update_fixes_error() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: Some(Profile::Base), package_type: Some(PackageType::Lib), + language_features: None, }); updater .update_document( "single/foo.qs", 1, - r#"namespace Foo { operation Main() : Unit { if Zero == Zero { Message("hi") } } }"#, + r#"namespace Foo { operation Main() : Unit { use q = Qubit(); if M(q) == Zero { Message("hi") } } }"#, ) .await; @@ -439,31 +506,11 @@ async fn target_profile_update_fixes_error() { ), [ Pass( - BaseProfCk( - ResultComparison( - Span { - lo: 45, - hi: 57, - }, - ), - ), - ), - Pass( - BaseProfCk( - ResultLiteral( - Span { - lo: 45, - hi: 49, - }, - ), - ), - ), - Pass( - BaseProfCk( - ResultLiteral( + CapabilitiesCk( + UseOfDynamicBool( Span { - lo: 53, - hi: 57, + lo: 62, + hi: 74, }, ), ), @@ -477,6 +524,7 @@ async fn target_profile_update_fixes_error() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: Some(Profile::Unrestricted), package_type: None, + language_features: None, }); expect_errors( @@ -516,6 +564,7 @@ async fn target_profile_update_causes_error_in_stdlib() { updater.update_configuration(WorkspaceConfigurationUpdate { target_profile: Some(Profile::Base), package_type: None, + language_features: None, }); expect_errors( diff --git a/pip/tests-qir/test_qir.py b/pip/tests-qir/test_qir.py index 56fc6fb5b4..95e1dcde3c 100644 --- a/pip/tests-qir/test_qir.py +++ b/pip/tests-qir/test_qir.py @@ -21,7 +21,7 @@ def test_compile_qir_input_data() -> None: qir = operation._repr_qir_() assert isinstance(qir, bytes) module = Module.from_ir(Context(), qir.decode(), "module") - assert len(module.functions) == 24 + assert len(module.functions) == 5 assert module.functions[0].name == "ENTRYPOINT__main" func = module.functions[0] assert len(func.basic_blocks) == 1 @@ -71,7 +71,7 @@ def test_compile_qir_all_gates() -> None: qir = operation._repr_qir_() assert isinstance(qir, bytes) module = Module.from_ir(Context(), qir.decode(), "module") - assert len(module.functions) == 24 + assert len(module.functions) == 23 assert module.functions[0].name == "ENTRYPOINT__main" func = module.functions[0] assert len(func.basic_blocks) == 1 diff --git a/pip/tests/test_interpreter.py b/pip/tests/test_interpreter.py index d1a7cfcf2f..a90171e92a 100644 --- a/pip/tests/test_interpreter.py +++ b/pip/tests/test_interpreter.py @@ -181,7 +181,9 @@ def test_value_array() -> None: def test_target_error() -> None: e = Interpreter(TargetProfile.Base) with pytest.raises(QSharpError) as excinfo: - e.interpret("operation Program() : Result { return Zero }") + e.interpret( + "operation Program() : Result { use q = Qubit(); if M(q) == Zero { return Zero } else { return One } }" + ) assert str(excinfo.value).startswith("Qsc.BaseProfCk.ResultLiteral") != -1 @@ -406,37 +408,20 @@ def test_base_qir_can_be_generated() -> None: %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*)) #1 + 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__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*) + + 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" } diff --git a/playground/src/editor.tsx b/playground/src/editor.tsx index 903a61e796..95a6945bbe 100644 --- a/playground/src/editor.tsx +++ b/playground/src/editor.tsx @@ -130,7 +130,7 @@ export function Editor(props: { const config = { sources: [["code", code]], - languageFeatures: ["preview-qir-gen"], + languageFeatures: [], profile: profile, } as ProgramConfig; @@ -183,7 +183,7 @@ export function Editor(props: { props.evtTarget.clearResults(); const config = { sources: [["code", code]], - languageFeatures: ["preview-qir-gen"], + languageFeatures: [], profile: profile, } as ProgramConfig; diff --git a/resource_estimator/src/system/optimization/code_distance_iterators/tests.rs b/resource_estimator/src/system/optimization/code_distance_iterators/tests.rs index 1842d03fd6..0352ad3fab 100644 --- a/resource_estimator/src/system/optimization/code_distance_iterators/tests.rs +++ b/resource_estimator/src/system/optimization/code_distance_iterators/tests.rs @@ -16,7 +16,7 @@ fn test_switch_to_non_comparable_code_distance_indexes_basic() { &left, &right, 3 - ),); + )); assert_eq!(current, [1, 4, 4]); } @@ -31,7 +31,7 @@ fn test_switch_to_non_comparable_code_distance_indexes_overflow() { &left, &right, 3 - ),); + )); assert_eq!(current, [2, 2, 3]); } @@ -45,7 +45,7 @@ fn test_switch_to_non_comparable_code_distance_indexes_finding_left() { &left, &right, 5 - ),); + )); assert_eq!(current, [2, 2, 3, 3, 3]); } @@ -59,7 +59,7 @@ fn test_switch_to_non_comparable_code_distance_indexes_all_max() { &left, &right, 3 - ),); + )); } #[test] diff --git a/vscode/package.json b/vscode/package.json index 8974520f9d..fbfce4a034 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -110,16 +110,6 @@ "The Adaptive_RI target profile includes all of the required Adaptive Profile capabilities, as well as the optional integer computation and qubit reset capabilities, as defined by the QIR specification." ], "markdownDescription": "Setting the target profile allows the Q# extension to generate programs that are compatible with a specific target. The target is the hardware or simulator which will be used to run the Q# program. [Learn more](https://aka.ms/qdk.qir)" - }, - "Q#.qir.experimentalCodeGeneration": { - "type": "boolean", - "default": false, - "markdownDescription": "Enable the experimental QIR code generation functionality. (Required to generate code targeting the Adaptive Profile). [Learn more](https://aka.ms/qdk.qir)" - }, - "Q#.qir.enableAdaptiveProfile": { - "type": "boolean", - "default": false, - "markdownDescription": "Enable Adaptive Profile as a target. [Learn more](https://aka.ms/qdk.qir)" } } }, diff --git a/vscode/src/config.ts b/vscode/src/config.ts index 1b0b7d59bb..bd045d4a83 100644 --- a/vscode/src/config.ts +++ b/vscode/src/config.ts @@ -41,17 +41,3 @@ export function getTargetFriendlyName(targetProfile?: string) { return "Q#: invalid"; } } - -export function getEnablePreviewQirGen(): boolean { - return vscode.workspace.getConfiguration("Q#").get( - "qir.experimentalCodeGeneration", - false, // The default value should be set in `package.json` as well. - ); -} - -export function getEnableAdaptiveProfile(): boolean { - return vscode.workspace.getConfiguration("Q#").get( - "qir.enableAdaptiveProfile", - false, // The default value should be set in `package.json` as well. - ); -} diff --git a/vscode/src/qirGeneration.ts b/vscode/src/qirGeneration.ts index 1ae5f7f703..1630bd31c4 100644 --- a/vscode/src/qirGeneration.ts +++ b/vscode/src/qirGeneration.ts @@ -6,7 +6,7 @@ import { getCompilerWorker, log, ProgramConfig } from "qsharp-lang"; import { isQsharpDocument } from "./common"; import { EventType, sendTelemetryEvent } from "./telemetry"; import { getRandomGuid } from "./utils"; -import { getTarget, getEnablePreviewQirGen, setTarget } from "./config"; +import { getTarget, setTarget } from "./config"; import { loadProject } from "./projectSystem"; const generateQirTimeoutMs = 30000; @@ -30,26 +30,25 @@ export async function getQirForActiveWindow(): Promise { ); } - // Check that the current target is base profile, and current doc has no errors. + // Check that the current target is base or adaptive_ri profile, and current doc has no errors. const targetProfile = getTarget(); - const enablePreviewQirGen = getEnablePreviewQirGen(); - if (targetProfile !== "base") { - const allowed = targetProfile === "adaptive_ri" && enablePreviewQirGen; - if (!allowed) { - const result = await vscode.window.showWarningMessage( - "Submitting to Azure is only supported when targeting the QIR base profile.", - { modal: true }, - { title: "Change the QIR target profile and continue", action: "set" }, - { title: "Cancel", action: "cancel", isCloseAffordance: true }, + if (targetProfile === "unrestricted") { + const result = await vscode.window.showWarningMessage( + "Submitting to Azure is only supported when targeting the QIR base or adaptive_ri profile.", + { modal: true }, + { + title: "Set the QIR target profile to Base and continue", + action: "set", + }, + { title: "Cancel", action: "cancel", isCloseAffordance: true }, + ); + if (result?.action !== "set") { + throw new QirGenerationError( + "Submitting to Azure is not supported when using the unrestricted profile. " + + "Please update the QIR target via the status bar selector or extension settings.", ); - if (result?.action !== "set") { - throw new QirGenerationError( - "Submitting to Azure is only supported when targeting the QIR base profile. " + - "Please update the QIR target via the status bar selector or extension settings.", - ); - } else { - setTarget("base"); - } + } else { + setTarget("base"); } } let sources: [string, string][] = []; @@ -81,9 +80,7 @@ export async function getQirForActiveWindow(): Promise { const associationId = getRandomGuid(); const start = performance.now(); sendTelemetryEvent(EventType.GenerateQirStart, { associationId }, {}); - if (enablePreviewQirGen) { - languageFeatures.push("preview-qir-gen"); - } + const config = { sources, languageFeatures, @@ -100,8 +97,8 @@ export async function getQirForActiveWindow(): Promise { } catch (e: any) { log.error("Codegen error. ", e.toString()); throw new QirGenerationError( - `Code generation failed due to error: "${e.toString()}". Please ensure the code is compatible with the QIR base profile ` + - "by setting the target QIR profile to 'base' and fixing any errors.", + `Code generation failed due to error: "${e.toString()}". Please ensure the code is compatible with a QIR profile ` + + "by setting the target QIR profile to 'base' or 'adaptive_ri' and fixing any errors.", ); } finally { worker.terminate(); diff --git a/vscode/src/statusbar.ts b/vscode/src/statusbar.ts index 132517ce7f..e24af6ec92 100644 --- a/vscode/src/statusbar.ts +++ b/vscode/src/statusbar.ts @@ -4,12 +4,7 @@ import { log, TargetProfile } from "qsharp-lang"; import * as vscode from "vscode"; import { isQsharpDocument } from "./common"; -import { - getEnableAdaptiveProfile, - getTarget, - getTargetFriendlyName, - setTarget, -} from "./config"; +import { getTarget, getTargetFriendlyName, setTarget } from "./config"; export function activateTargetProfileStatusBarItem(): vscode.Disposable[] { const disposables = []; @@ -86,7 +81,7 @@ function registerTargetProfileCommand() { "qsharp-vscode.setTargetProfile", async () => { const target = await vscode.window.showQuickPick( - getTargetProfiles().map((profile) => ({ + targetProfiles.map((profile) => ({ label: profile.uiText, })), { placeHolder: "Select the QIR target profile" }, @@ -105,20 +100,6 @@ const targetProfiles = [ { configName: "unrestricted", uiText: "Q#: unrestricted" }, ]; -function getTargetProfiles(): { - configName: string; - uiText: string; -}[] { - const allow_adaptive_ri = getEnableAdaptiveProfile(); - if (allow_adaptive_ri) { - return targetProfiles; - } else { - return targetProfiles.filter( - (profile) => profile.configName !== "adaptive_ri", - ); - } -} - function getTargetProfileSetting(uiText: string): TargetProfile { switch (uiText) { case "Q#: QIR base": diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 5a437d353b..2027eea028 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -19,7 +19,7 @@ futures-util = { workspace = true } js-sys = { workspace = true } katas = { path = "../katas"} log = { workspace = true } -miette = { workspace = true} +miette = { workspace = true } num-bigint = { workspace = true } num-complex = { workspace = true } qsls = { path = "../language_service" } diff --git a/wasm/src/language_service.rs b/wasm/src/language_service.rs index 304c2ab9f3..e3abf60c33 100644 --- a/wasm/src/language_service.rs +++ b/wasm/src/language_service.rs @@ -100,6 +100,9 @@ impl LanguageService { "exe" => PackageType::Exe, _ => panic!("invalid package type"), }), + language_features: config + .languageFeatures + .map(|features| features.iter().collect::()), }); } @@ -331,10 +334,12 @@ serializable_type! { { pub targetProfile: Option, pub packageType: Option, + pub languageFeatures: Option>, }, r#"export interface IWorkspaceConfiguration { targetProfile?: TargetProfile; packageType?: "exe" | "lib"; + languageFeatures?: LanguageFeatures[]; }"#, IWorkspaceConfiguration } diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index ac1fe0033d..12099c9843 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -7,6 +7,7 @@ use diagnostic::{interpret_errors_into_vs_diagnostics, VSDiagnostic}; use katas::check_solution; use language_service::IOperationInfo; +use miette::JSONReportHandler; use num_bigint::BigUint; use num_complex::Complex64; use project_system::into_async_rust_fn_with; @@ -22,7 +23,6 @@ use qsc::{ LanguageFeatures, PackageStore, PackageType, SourceContents, SourceMap, SourceName, SparseSim, TargetCapabilityFlags, }; -use qsc_codegen::qir_base::generate_qir; use resource_estimator::{self as re, estimate_entry}; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -86,39 +86,30 @@ pub fn get_qir( if profile == Profile::Unrestricted { return Err("Invalid target profile for QIR generation".to_string()); } - if language_features.contains(LanguageFeatures::PreviewQirGen) { - qsc::codegen::get_qir(sources, language_features, profile.into()) - } else { - _get_qir(sources, language_features) - } -} - -// allows testing without wasm bindings. -fn _get_qir(sources: SourceMap, language_features: LanguageFeatures) -> Result { - let core = compile::core(); - let mut store = PackageStore::new(core); - let std = compile::std(&store, Profile::Base.into()); - let std = store.insert(std); - let (unit, errors) = qsc::compile::compile( - &store, - &[std], - sources, - PackageType::Exe, - Profile::Base.into(), - language_features, - ); - - // 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()); - } + _get_qir(sources, language_features, profile.into()) +} - let package = store.insert(unit); +pub(crate) fn _get_qir( + sources: SourceMap, + language_features: LanguageFeatures, + capabilities: TargetCapabilityFlags, +) -> Result { + qsc::codegen::get_qir(sources, language_features, capabilities).map_err(|errors| { + let mut msg = String::new(); + msg.push('['); + for error in errors { + if msg.len() > 1 { + msg.push(','); + } + JSONReportHandler::new() + .render_report(&mut msg, &error) + .expect("rendering should succeed"); + } - generate_qir(&store, package).map_err(|e| e.0.to_string()) + msg.push(']'); + msg + }) } #[wasm_bindgen] @@ -494,5 +485,10 @@ pub fn generate_docs( #[wasm_bindgen(typescript_custom_section)] const TARGET_PROFILE: &'static str = r#" -export type TargetProfile = "base" | "adaptive_ri" |"unrestricted"; +export type TargetProfile = "base" | "adaptive_ri" | "unrestricted"; +"#; + +#[wasm_bindgen(typescript_custom_section)] +const LANGUAGE_FEATURES: &'static str = r#" +export type LanguageFeatures = "v2-preview-syntax"; "#; diff --git a/wasm/src/tests.rs b/wasm/src/tests.rs index c5335ab775..515e72c6a4 100644 --- a/wasm/src/tests.rs +++ b/wasm/src/tests.rs @@ -3,7 +3,9 @@ use expect_test::expect; use indoc::indoc; -use qsc::{interpret, LanguageFeatures, SourceMap}; +use qsc::{interpret, LanguageFeatures, SourceMap, TargetCapabilityFlags}; + +use crate::_get_qir; use super::run_internal_with_features; @@ -39,9 +41,10 @@ fn test_compile() { M(q) }}"; - let result = crate::_get_qir( + let result = qsc::codegen::get_qir( SourceMap::new([("test.qs".into(), code.into())], None), LanguageFeatures::default(), + TargetCapabilityFlags::empty(), ); assert!(result.is_ok()); } @@ -463,3 +466,24 @@ fn test_doc_gen() { } } } + +#[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( + "[{\"message\": \"syntax error\",\"code\": \"Qsc.Parse.Token\",\"severity\": \"error\",\"causes\": [\"expected `;`, found keyword `let`\"],\"filename\": \"test.qs\",\"labels\": [{\"span\": {\"offset\": 129,\"length\": 3}}],\"related\": []}]", + ) + "#]] + .assert_debug_eq(&_get_qir(sources, language_features, capabilities)); +}