Repository: microsoft/qsharp
Pull request #1400 (2024-04-18 01:22:52)

Use new QIR gen API for Base Profile

github-actions[bot]: ## Benchmark for 59c7f03
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 350.0±4.38µs | 348.5±1.74µs | -0.43% |
| Array literal evaluation | **183.5±1.75µs** | 195.8±3.12µs | **+6.70%** |
| Array update evaluation | 438.4±2.88µs | **432.1±2.58µs** | **-1.44%** |
| Core + Standard library compilation | **16.9±0.15ms** | 17.5±0.79ms | **+3.55%** |
| Deutsch-Jozsa evaluation | **5.0±0.04ms** | 5.2±0.12ms | **+4.00%** |
| Large file parity evaluation | 33.7±0.47ms | 33.7±0.39ms | 0.00% |
| Large input file compilation | 11.3±0.72ms | 11.3±0.18ms | 0.00% |
| Large input file compilation (interpreter) | 43.9±1.13ms | 43.8±0.83ms | -0.23% |
| Large nested iteration | **33.6±0.22ms** | 34.4±0.43ms | **+2.38%** |
| Perform Runtime Capabilities Analysis (RCA) on Deutsch-Jozsa sample | 1491.8±30.64µs | 1497.5±29.87µs | +0.38% |
| Perform Runtime Capabilities Analysis (RCA) on large file sample | 7.7±0.07ms | 7.7±0.06ms | 0.00% |
| Perform Runtime Capabilities Analysis (RCA) on teleport sample | 1404.5±29.87µs | 1412.1±39.81µs | +0.54% |
| Perform Runtime Capabilities Analysis (RCA) on the core and std libraries | 21.0±0.13ms | 21.1±0.53ms | +0.48% |
| Teleport evaluation | 81.6±3.82µs | 82.3±4.62µs | +0.86% |

  </details>
  
github-actions[bot]: ## Benchmark for 8f5175d
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 337.1±2.46µs | 338.5±4.46µs | +0.42% |
| Array literal evaluation | 180.6±1.57µs | 180.8±5.04µs | +0.11% |
| Array update evaluation | 422.7±3.26µs | **419.1±3.56µs** | **-0.85%** |
| Core + Standard library compilation | 19.2±1.32ms | **17.5±1.21ms** | **-8.85%** |
| Deutsch-Jozsa evaluation | 5.1±0.07ms | 5.1±0.06ms | 0.00% |
| Large file parity evaluation | 34.5±0.34ms | 34.2±0.68ms | -0.87% |
| Large input file compilation | 12.6±0.93ms | 13.3±1.09ms | +5.56% |
| Large input file compilation (interpreter) | 51.0±5.90ms | 50.0±4.80ms | -1.96% |
| Large nested iteration | 33.0±0.31ms | 33.1±0.40ms | +0.30% |
| Perform Runtime Capabilities Analysis (RCA) on Deutsch-Jozsa sample | 1531.6±117.80µs | 1531.4±163.79µs | -0.01% |
| Perform Runtime Capabilities Analysis (RCA) on large file sample | 8.5±0.49ms | 8.3±0.42ms | -2.35% |
| Perform Runtime Capabilities Analysis (RCA) on teleport sample | 1456.2±157.33µs | **1419.0±28.84µs** | **-2.55%** |
| Perform Runtime Capabilities Analysis (RCA) on the core and std libraries | 22.2±0.88ms | 21.8±0.61ms | -1.80% |
| Teleport evaluation | 82.4±4.22µs | 80.8±3.81µs | -1.94% |

  </details>
  
github-actions[bot]: ## Benchmark for a8f0670
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | 339.2±6.42µs | **336.8±2.03µs** | **-0.71%** |
| Array literal evaluation | **180.2±1.29µs** | 184.5±16.48µs | **+2.39%** |
| Array update evaluation | 421.7±2.67µs | **416.8±1.17µs** | **-1.16%** |
| Core + Standard library compilation | 19.7±0.68ms | **18.9±0.79ms** | **-4.06%** |
| Deutsch-Jozsa evaluation | **5.1±0.06ms** | 5.2±0.10ms | **+1.96%** |
| Large file parity evaluation | 33.7±0.10ms | 33.7±0.08ms | 0.00% |
| Large input file compilation | 12.8±0.55ms | 12.7±0.47ms | -0.78% |
| Large input file compilation (interpreter) | 49.3±1.40ms | 48.7±1.20ms | -1.22% |
| Large nested iteration | 33.1±0.31ms | 32.9±0.32ms | -0.60% |
| Perform Runtime Capabilities Analysis (RCA) on Deutsch-Jozsa sample | 1558.8±156.28µs | 1537.5±143.70µs | -1.37% |
| Perform Runtime Capabilities Analysis (RCA) on large file sample | 8.3±0.18ms | **8.0±0.30ms** | **-3.61%** |
| Perform Runtime Capabilities Analysis (RCA) on teleport sample | 1458.6±111.36µs | 1455.8±141.36µs | -0.19% |
| Perform Runtime Capabilities Analysis (RCA) on the core and std libraries | 22.7±0.54ms | **22.2±0.35ms** | **-2.20%** |
| Teleport evaluation | 83.2±6.75µs | 82.5±7.27µs | -0.84% |

  </details>
  
github-actions[bot]: ## Benchmark for 0c1cb47
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| Array append evaluation | **340.9±12.24µs** | 367.1±31.14µs | **+7.69%** |
| Array literal evaluation | 201.9±4.31µs | **195.8±2.31µs** | **-3.02%** |
| Array update evaluation | **424.4±5.89µs** | 457.3±36.22µs | **+7.75%** |
| Core + Standard library compilation | 17.5±0.74ms | 17.9±0.65ms | +2.29% |
| Deutsch-Jozsa evaluation | 5.0±0.04ms | 5.0±0.05ms | 0.00% |
| Large file parity evaluation | 33.8±0.31ms | 33.5±0.47ms | -0.89% |
| Large input file compilation | 11.7±0.35ms | 11.9±0.63ms | +1.71% |
| Large input file compilation (interpreter) | 49.0±4.87ms | 46.3±3.95ms | -5.51% |
| Large nested iteration | **33.1±0.15ms** | 33.4±0.36ms | **+0.91%** |
| Perform Runtime Capabilities Analysis (RCA) on Deutsch-Jozsa sample | 1502.2±45.20µs | 1497.6±31.85µs | -0.31% |
| Perform Runtime Capabilities Analysis (RCA) on large file sample | 7.9±0.19ms | 7.8±0.12ms | -1.27% |
| Perform Runtime Capabilities Analysis (RCA) on teleport sample | 1412.6±30.36µs | 1413.4±33.61µs | +0.06% |
| Perform Runtime Capabilities Analysis (RCA) on the core and std libraries | 21.4±0.33ms | 21.3±0.20ms | -0.47% |
| Teleport evaluation | 81.6±5.14µs | 80.1±4.00µs | -1.84% |

  </details>
  

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 32e5a336..b2864bcb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1015,6 +1015,7 @@ version = "0.0.0"
 dependencies = [
  "expect-test",
  "qsc_ast",
+ "qsc_data_structures",
  "qsc_frontend",
  "qsc_hir",
  "regex-lite",
@@ -1068,7 +1069,6 @@ dependencies = [
 name = "qsc_frontend"
 version = "0.0.0"
 dependencies = [
- "bitflags 2.4.2",
  "expect-test",
  "indoc",
  "library",
diff --git a/compiler/qsc/benches/eval.rs b/compiler/qsc/benches/eval.rs
index df4b5492..bc4dceb3 100644
--- a/compiler/qsc/benches/eval.rs
+++ b/compiler/qsc/benches/eval.rs
@@ -5,10 +5,10 @@ allocator::assign_global!();
 
 use criterion::{criterion_group, criterion_main, Criterion};
 use indoc::indoc;
-use qsc::{interpret::Interpreter, PackageType};
+use qsc::{interpret::Interpreter, PackageType, TargetCapabilityFlags};
 use qsc_data_structures::language_features::LanguageFeatures;
 use qsc_eval::output::GenericReceiver;
-use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::SourceMap;
 
 const TELEPORT: &str = include_str!("../../../samples/algorithms/Teleportation.qs");
 const DEUTSCHJOZSA: &str = include_str!("../../../samples/algorithms/DeutschJozsa.qs");
diff --git a/compiler/qsc/benches/large.rs b/compiler/qsc/benches/large.rs
index 5025108d..b89a20ee 100644
--- a/compiler/qsc/benches/large.rs
+++ b/compiler/qsc/benches/large.rs
@@ -4,9 +4,12 @@
 allocator::assign_global!();
 
 use criterion::{criterion_group, criterion_main, Criterion};
-use qsc::compile::{self, compile};
+use qsc::{
+    compile::{self, compile},
+    TargetCapabilityFlags,
+};
 use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore, SourceMap};
 use qsc_passes::PackageType;
 
 const INPUT: &str = include_str!("./large.qs");
diff --git a/compiler/qsc/benches/library.rs b/compiler/qsc/benches/library.rs
index 3decd16b..e1b80b00 100644
--- a/compiler/qsc/benches/library.rs
+++ b/compiler/qsc/benches/library.rs
@@ -4,8 +4,8 @@
 allocator::assign_global!();
 
 use criterion::{criterion_group, criterion_main, Criterion};
-use qsc::compile;
-use qsc_frontend::compile::{PackageStore, TargetCapabilityFlags};
+use qsc::{compile, TargetCapabilityFlags};
+use qsc_frontend::compile::PackageStore;
 
 pub fn library(c: &mut Criterion) {
     c.bench_function("Core + Standard library compilation", |b| {
diff --git a/compiler/qsc/benches/rca.rs b/compiler/qsc/benches/rca.rs
index c9005f9a..0cbbfd70 100644
--- a/compiler/qsc/benches/rca.rs
+++ b/compiler/qsc/benches/rca.rs
@@ -3,9 +3,9 @@
 
 use criterion::{criterion_group, criterion_main, Criterion};
 use qsc::incremental::Compiler;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_fir::fir::PackageStore;
-use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap};
 use qsc_lowerer::{map_hir_package_to_fir, Lowerer};
 use qsc_passes::PackageType;
 use qsc_rca::{Analyzer, PackageStoreComputeProperties};
diff --git a/compiler/qsc/src/bin/qsc.rs b/compiler/qsc/src/bin/qsc.rs
index 91f3f8b1..83bd128a 100644
--- a/compiler/qsc/src/bin/qsc.rs
+++ b/compiler/qsc/src/bin/qsc.rs
@@ -8,9 +8,9 @@ use log::info;
 use miette::{Context, IntoDiagnostic, Report};
 use qsc::compile::compile;
 use qsc_codegen::qir_base;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_frontend::{
-    compile::{PackageStore, SourceContents, SourceMap, SourceName, TargetCapabilityFlags},
+    compile::{PackageStore, SourceContents, SourceMap, SourceName},
     error::WithSource,
 };
 use qsc_hir::hir::{Package, PackageId};
diff --git a/compiler/qsc/src/bin/qsi.rs b/compiler/qsc/src/bin/qsi.rs
index c987012e..1876787c 100644
--- a/compiler/qsc/src/bin/qsi.rs
+++ b/compiler/qsc/src/bin/qsi.rs
@@ -8,13 +8,13 @@ use miette::{Context, IntoDiagnostic, Report, Result};
 use num_bigint::BigUint;
 use num_complex::Complex64;
 use qsc::interpret::{self, InterpretResult, Interpreter};
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_eval::{
     output::{self, Receiver},
     state::format_state_id,
     val::Value,
 };
-use qsc_frontend::compile::{SourceContents, SourceMap, SourceName, TargetCapabilityFlags};
+use qsc_frontend::compile::{SourceContents, SourceMap, SourceName};
 use qsc_passes::PackageType;
 use qsc_project::{FileSystem, Manifest, StdFs};
 use std::{
diff --git a/compiler/qsc/src/codegen.rs b/compiler/qsc/src/codegen.rs
index d0d8b692..80b893b2 100644
--- a/compiler/qsc/src/codegen.rs
+++ b/compiler/qsc/src/codegen.rs
@@ -1,11 +1,15 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
+#[cfg(test)]
+mod tests;
+
 use qsc_codegen::qir::fir_to_qir;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore, SourceMap};
 use qsc_partial_eval::ProgramEntry;
 use qsc_passes::{PackageType, PassContext};
+use qsc_rca::Analyzer;
 
 use crate::compile;
 
@@ -30,14 +34,19 @@ pub fn get_qir(
 
     // Ensure it compiles before trying to add it to the store.
     if !errors.is_empty() {
-        // This should never happen, as the program should be checked for errors before trying to
-        // generate code for it. But just in case, simply report the failure.
-        return Err("Failed to generate QIR".to_string());
+        // This will happen when QIR generation is attempted on a program that has errors.
+        // This can happen in the playground.
+        let mut error_message =
+            String::from("Failed to generate QIR. Could not compile sources.:\n");
+        for error in errors {
+            error_message.push_str(&format!("{error}\n"));
+        }
+
+        return Err(error_message);
     }
 
     let package_id = package_store.insert(unit);
     let (fir_store, fir_package_id) = qsc_passes::lower_hir_to_fir(&package_store, package_id);
-    let caps_results = PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities);
     let package = fir_store.get(fir_package_id);
     let entry = ProgramEntry {
         exec_graph: package.entry_exec_graph.clone(),
@@ -49,16 +58,21 @@ pub fn get_qir(
         )
             .into(),
     };
-    // Ensure it compiles before trying to add it to the store.
-    match caps_results {
-        Ok(compute_properties) => {
-            fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry)
-                .map_err(|e| e.to_string())
-        }
-        Err(_) => {
-            // This should never happen, as the program should be checked for errors before trying to
-            // generate code for it. But just in case, simply report the failure.
-            Err("Failed to generate QIR".to_string())
-        }
-    }
+
+    let compute_properties = if capabilities == TargetCapabilityFlags::empty() {
+        // baseprofchk already handled compliance, run the analyzer to get the compute properties.
+        let analyzer = Analyzer::init(&fir_store);
+        Ok(analyzer.analyze_all())
+    } else {
+        PassContext::run_fir_passes_on_fir(&fir_store, fir_package_id, capabilities)
+    };
+
+    let Ok(compute_properties) = compute_properties else {
+        // This should never happen, as the program should be checked for errors before trying to
+        // generate code for it. But just in case, simply report the failure.
+        return Err("Failed to generate QIR. Could not generate compute properties.".to_string());
+    };
+
+    fir_to_qir(&fir_store, capabilities, Some(compute_properties), &entry)
+        .map_err(|e| e.to_string())
 }
diff --git a/compiler/qsc/src/compile.rs b/compiler/qsc/src/compile.rs
index 593ce375..85d9958e 100644
--- a/compiler/qsc/src/compile.rs
+++ b/compiler/qsc/src/compile.rs
@@ -2,9 +2,9 @@
 // Licensed under the MIT License.
 
 use miette::{Diagnostic, Report};
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_frontend::{
-    compile::{CompileUnit, PackageStore, SourceMap, TargetCapabilityFlags},
+    compile::{CompileUnit, PackageStore, SourceMap},
     error::WithSource,
 };
 use qsc_hir::hir::PackageId;
diff --git a/compiler/qsc/src/incremental.rs b/compiler/qsc/src/incremental.rs
index a4473ce6..536affb1 100644
--- a/compiler/qsc/src/incremental.rs
+++ b/compiler/qsc/src/incremental.rs
@@ -3,10 +3,12 @@
 
 use crate::compile::{self, compile, core, std};
 use miette::Diagnostic;
+
 use qsc_ast::ast;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+
 use qsc_frontend::{
-    compile::{OpenPackageStore, PackageStore, SourceMap, TargetCapabilityFlags},
+    compile::{OpenPackageStore, PackageStore, SourceMap},
     error::WithSource,
     incremental::Increment,
 };
diff --git a/compiler/qsc/src/interpret.rs b/compiler/qsc/src/interpret.rs
index b1f9dbce..ffbca883 100644
--- a/compiler/qsc/src/interpret.rs
+++ b/compiler/qsc/src/interpret.rs
@@ -46,6 +46,7 @@ use qsc_data_structures::{
     language_features::LanguageFeatures,
     line_column::{Encoding, Range},
     span::Span,
+    target::TargetCapabilityFlags,
 };
 use qsc_eval::{
     backend::{Backend, Chain as BackendChain, SparseSim},
@@ -58,7 +59,7 @@ use qsc_fir::{
     visit::{self, Visitor},
 };
 use qsc_frontend::{
-    compile::{CompileUnit, PackageStore, Source, SourceMap, TargetCapabilityFlags},
+    compile::{CompileUnit, PackageStore, Source, SourceMap},
     error::WithSource,
     incremental::Increment,
 };
diff --git a/compiler/qsc/src/lib.rs b/compiler/qsc/src/lib.rs
index 7d48b480..4712f815 100644
--- a/compiler/qsc/src/lib.rs
+++ b/compiler/qsc/src/lib.rs
@@ -11,9 +11,7 @@ pub mod target;
 
 pub use qsc_formatter::formatter;
 
-pub use qsc_frontend::compile::{
-    CompileUnit, PackageStore, SourceContents, SourceMap, SourceName, TargetCapabilityFlags,
-};
+pub use qsc_frontend::compile::{CompileUnit, PackageStore, SourceContents, SourceMap, SourceName};
 
 pub mod resolve {
     pub use qsc_frontend::resolve::{Local, LocalKind, Locals, Res};
@@ -35,7 +33,9 @@ pub mod project {
     pub use qsc_project::{DirEntry, EntryType, FileSystem, Manifest, ManifestDescriptor};
 }
 
-pub use qsc_data_structures::{language_features::LanguageFeatures, span::Span};
+pub use qsc_data_structures::{
+    language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags,
+};
 
 pub use qsc_passes::{lower_hir_to_fir, PackageType, PassContext};
 
diff --git a/compiler/qsc/src/location.rs b/compiler/qsc/src/location.rs
index 851c679f..dd3bde2b 100644
--- a/compiler/qsc/src/location.rs
+++ b/compiler/qsc/src/location.rs
@@ -62,8 +62,9 @@ mod tests {
     use expect_test::expect;
     use qsc_data_structures::{
         language_features::LanguageFeatures, line_column::Encoding, span::Span,
+        target::TargetCapabilityFlags,
     };
-    use qsc_frontend::compile::{PackageStore, SourceMap, TargetCapabilityFlags};
+    use qsc_frontend::compile::{PackageStore, SourceMap};
     use qsc_hir::hir::PackageId;
     use qsc_passes::PackageType;
 
diff --git a/compiler/qsc/src/target.rs b/compiler/qsc/src/target.rs
index f34de255..b1cd27c0 100644
--- a/compiler/qsc/src/target.rs
+++ b/compiler/qsc/src/target.rs
@@ -3,7 +3,7 @@
 
 use std::str::FromStr;
 
-use qsc_frontend::compile::TargetCapabilityFlags;
+use qsc_data_structures::target::TargetCapabilityFlags;
 
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub enum Profile {
diff --git a/compiler/qsc_codegen/src/qir.rs b/compiler/qsc_codegen/src/qir.rs
index 03085b0b..568e7e73 100644
--- a/compiler/qsc_codegen/src/qir.rs
+++ b/compiler/qsc_codegen/src/qir.rs
@@ -7,12 +7,12 @@ mod instruction_tests;
 #[cfg(test)]
 mod tests;
 
-use qsc_frontend::compile::TargetCapabilityFlags;
+use qsc_data_structures::target::TargetCapabilityFlags;
 use qsc_lowerer::map_hir_package_to_fir;
 use qsc_partial_eval::{partially_evaluate, ProgramEntry};
 use qsc_rca::PackageStoreComputeProperties;
 use qsc_rir::{
-    passes::{check_and_transform, defer_quantum_measurements},
+    passes::check_and_transform,
     rir::{self, ConditionCode},
     utils::get_all_block_successors,
 };
@@ -43,11 +43,8 @@ pub fn fir_to_qir(
     compute_properties: Option<PackageStoreComputeProperties>,
     entry: &ProgramEntry,
 ) -> Result<String, qsc_partial_eval::Error> {
-    let mut program = get_rir_from_compilation(fir_store, compute_properties, entry)?;
+    let mut program = get_rir_from_compilation(fir_store, compute_properties, entry, capabilities)?;
     check_and_transform(&mut program);
-    if capabilities.is_empty() {
-        defer_quantum_measurements(&mut program);
-    }
     Ok(ToQir::<String>::to_qir(&program, &program))
 }
 
@@ -55,13 +52,14 @@ fn get_rir_from_compilation(
     fir_store: &qsc_fir::fir::PackageStore,
     compute_properties: Option<PackageStoreComputeProperties>,
     entry: &ProgramEntry,
+    capabilities: TargetCapabilityFlags,
 ) -> Result<rir::Program, qsc_partial_eval::Error> {
     let compute_properties = compute_properties.unwrap_or_else(|| {
         let analyzer = qsc_rca::Analyzer::init(fir_store);
         analyzer.analyze_all()
     });
 
-    partially_evaluate(fir_store, &compute_properties, entry)
+    partially_evaluate(fir_store, &compute_properties, entry, capabilities)
 }
 
 /// A trait for converting a type into QIR of type `T`.
@@ -546,9 +544,92 @@ impl ToQir<String> for rir::Program {
         } else {
             "adaptive_profile"
         };
-        format!(
+        let body = format!(
             include_str!("./qir/template.ll"),
             callables, profile, self.num_qubits, self.num_results
-        )
+        );
+        let flags = get_module_metadata(self);
+        body + "\n" + &flags
+    }
+}
+
+/// Create the module metadata for the given program.
+/// creating the `llvm.module.flags` and its associated values.
+fn get_module_metadata(program: &rir::Program) -> String {
+    let mut flags = String::new();
+
+    // push the default attrs, we don't have any config values
+    // for now that would change any of them.
+    flags.push_str(
+        r#"
+!0 = !{i32 1, !"qir_major_version", i32 1}
+!1 = !{i32 7, !"qir_minor_version", i32 0}
+!2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+!3 = !{i32 1, !"dynamic_result_management", i1 false}
+"#,
+    );
+
+    let mut index = 4;
+
+    // If we are not in the base profile, we need to add the capabilities
+    // associated with the adaptive profile.
+    if !program.config.is_base() {
+        // loop through the capabilities and add them to the metadata
+        // for values that we can generate.
+        for cap in program.config.capabilities.iter() {
+            let name = match cap {
+                TargetCapabilityFlags::QubitReset => "qubit_resetting",
+                TargetCapabilityFlags::IntegerComputations => "classical_ints",
+                TargetCapabilityFlags::FloatingPointComputations => "classical_floats",
+                TargetCapabilityFlags::BackwardsBranching => "backwards_branching",
+                _ => continue,
+            };
+            flags.push_str(&format!(
+                "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
+                index, 1, name, true
+            ));
+            index += 1;
+        }
+
+        // loop through the capabilities that are missing and add them to the metadata
+        // as not supported.
+        let missing = TargetCapabilityFlags::all().difference(program.config.capabilities);
+        for cap in missing.iter() {
+            let name = match cap {
+                TargetCapabilityFlags::QubitReset => "qubit_resetting",
+                TargetCapabilityFlags::IntegerComputations => "classical_ints",
+                TargetCapabilityFlags::FloatingPointComputations => "classical_floats",
+                TargetCapabilityFlags::BackwardsBranching => "backwards_branching",
+                _ => continue,
+            };
+            flags.push_str(&format!(
+                "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
+                index, 1, name, false
+            ));
+            index += 1;
+        }
+
+        // Add the remaining extension capabilities as not supported.
+        // We can't generate these values yet so we just add them as false.
+        let unmapped_capabilities = [
+            "classical_fixed_points",
+            "user_functions",
+            "multiple_target_branching",
+        ];
+        for capability in unmapped_capabilities {
+            flags.push_str(&format!(
+                "!{} = !{{i32 {}, !\"{}\", i1 {}}}\n",
+                index, 1, capability, false
+            ));
+            index += 1;
+        }
+    }
+
+    let mut metadata_def = String::new();
+    metadata_def.push_str("!llvm.module.flags = !{");
+    for i in 0..index - 1 {
+        metadata_def.push_str(&format!("!{i}, "));
     }
+    metadata_def.push_str(&format!("!{}}}\n", index - 1));
+    metadata_def + &flags
 }
diff --git a/compiler/qsc_codegen/src/qir/template.ll b/compiler/qsc_codegen/src/qir/template.ll
index fae49bd7..b58560a7 100644
--- a/compiler/qsc_codegen/src/qir/template.ll
+++ b/compiler/qsc_codegen/src/qir/template.ll
@@ -7,10 +7,3 @@ attributes #0 = {{ "entry_point" "output_labeling_schema" "qir_profiles"="{}" "r
 attributes #1 = {{ "irreversible" }}
 
 ; module flags
-
-!llvm.module.flags = !{{!0, !1, !2, !3}}
-
-!0 = !{{i32 1, !"qir_major_version", i32 1}}
-!1 = !{{i32 7, !"qir_minor_version", i32 0}}
-!2 = !{{i32 1, !"dynamic_qubit_management", i1 false}}
-!3 = !{{i32 1, !"dynamic_result_management", i1 false}}
diff --git a/compiler/qsc_data_structures/src/lib.rs b/compiler/qsc_data_structures/src/lib.rs
index 53a850c6..68fc178c 100644
--- a/compiler/qsc_data_structures/src/lib.rs
+++ b/compiler/qsc_data_structures/src/lib.rs
@@ -7,3 +7,4 @@ pub mod index_map;
 pub mod language_features;
 pub mod line_column;
 pub mod span;
+pub mod target;
diff --git a/compiler/qsc_data_structures/src/target.rs b/compiler/qsc_data_structures/src/target.rs
new file mode 100644
index 00000000..f9030ba2
--- /dev/null
+++ b/compiler/qsc_data_structures/src/target.rs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+use bitflags::bitflags;
+
+bitflags! {
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    pub struct TargetCapabilityFlags: u32 {
+        const Adaptive = 0b0000_0001;
+        const IntegerComputations = 0b0000_0010;
+        const FloatingPointComputations = 0b0000_0100;
+        const BackwardsBranching = 0b0000_1000;
+        const HigherLevelConstructs = 0b0001_0000;
+        const QubitReset = 0b0010_0000;
+    }
+}
+
+impl std::str::FromStr for TargetCapabilityFlags {
+    type Err = ();
+
+    fn from_str(value: &str) -> Result<Self, Self::Err> {
+        match value {
+            "Base" => Ok(TargetCapabilityFlags::empty()),
+            "Adaptive" => Ok(TargetCapabilityFlags::Adaptive),
+            "IntegerComputations" => Ok(TargetCapabilityFlags::IntegerComputations),
+            "FloatingPointComputations" => Ok(TargetCapabilityFlags::FloatingPointComputations),
+            "BackwardsBranching" => Ok(TargetCapabilityFlags::BackwardsBranching),
+            "HigherLevelConstructs" => Ok(TargetCapabilityFlags::HigherLevelConstructs),
+            "QubitReset" => Ok(TargetCapabilityFlags::QubitReset),
+            "Unrestricted" => Ok(TargetCapabilityFlags::all()),
+            _ => Err(()),
+        }
+    }
+}
+
+impl Default for TargetCapabilityFlags {
+    fn default() -> Self {
+        TargetCapabilityFlags::empty()
+    }
+}
diff --git a/compiler/qsc_doc_gen/Cargo.toml b/compiler/qsc_doc_gen/Cargo.toml
index 7249c2e6..ddc9e37e 100644
--- a/compiler/qsc_doc_gen/Cargo.toml
+++ b/compiler/qsc_doc_gen/Cargo.toml
@@ -13,6 +13,7 @@ expect-test = { workspace = true }
 
 [dependencies]
 regex-lite = { workspace = true }
+qsc_data_structures = { path = "../qsc_data_structures" }
 qsc_frontend = { path = "../qsc_frontend" }
 qsc_ast = { path = "../qsc_ast" }
 qsc_hir = { path = "../qsc_hir" }
diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs
index e03b76b4..311c2c70 100644
--- a/compiler/qsc_doc_gen/src/generate_docs.rs
+++ b/compiler/qsc_doc_gen/src/generate_docs.rs
@@ -7,7 +7,8 @@ mod tests;
 use crate::display::{increase_header_level, parse_doc_for_summary};
 use crate::display::{CodeDisplay, Lookup};
 use qsc_ast::ast;
-use qsc_frontend::compile::{self, PackageStore, TargetCapabilityFlags};
+use qsc_data_structures::target::TargetCapabilityFlags;
+use qsc_frontend::compile::{self, PackageStore};
 use qsc_frontend::resolve;
 use qsc_hir::hir::{CallableKind, Item, ItemKind, Package, PackageId, Visibility};
 use qsc_hir::{hir, ty};
diff --git a/compiler/qsc_frontend/Cargo.toml b/compiler/qsc_frontend/Cargo.toml
index 0ee1895d..a59b4663 100644
--- a/compiler/qsc_frontend/Cargo.toml
+++ b/compiler/qsc_frontend/Cargo.toml
@@ -9,7 +9,6 @@ edition.workspace = true
 license.workspace = true
 
 [dependencies]
-bitflags = { workspace = true }
 miette = { workspace = true }
 qsc_data_structures = { path = "../qsc_data_structures" }
 qsc_ast = { path = "../qsc_ast" }
diff --git a/compiler/qsc_frontend/src/compile.rs b/compiler/qsc_frontend/src/compile.rs
index 0f89a4f5..86e816a4 100644
--- a/compiler/qsc_frontend/src/compile.rs
+++ b/compiler/qsc_frontend/src/compile.rs
@@ -12,7 +12,7 @@ use crate::{
     resolve::{self, Locals, Names, Resolver},
     typeck::{self, Checker, Table},
 };
-use bitflags::bitflags;
+
 use miette::{Diagnostic, Report};
 use preprocess::TrackedName;
 use qsc_ast::{
@@ -26,6 +26,7 @@ use qsc_data_structures::{
     index_map::{self, IndexMap},
     language_features::LanguageFeatures,
     span::Span,
+    target::TargetCapabilityFlags,
 };
 use qsc_hir::{
     assigner::Assigner as HirAssigner,
@@ -34,39 +35,9 @@ use qsc_hir::{
     validate::Validator as HirValidator,
     visit::Visitor as _,
 };
-use std::{fmt::Debug, str::FromStr, sync::Arc};
+use std::{fmt::Debug, sync::Arc};
 use thiserror::Error;
 
-bitflags! {
-    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-    pub struct TargetCapabilityFlags: u32 {
-        const Adaptive = 0b0000_0001;
-        const IntegerComputations = 0b0000_0010;
-        const FloatingPointComputations = 0b0000_0100;
-        const BackwardsBranching = 0b0000_1000;
-        const HigherLevelConstructs = 0b0001_0000;
-        const QubitReset = 0b0010_0000;
-    }
-}
-
-impl FromStr for TargetCapabilityFlags {
-    type Err = ();
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "Base" => Ok(TargetCapabilityFlags::empty()),
-            "Adaptive" => Ok(TargetCapabilityFlags::Adaptive),
-            "IntegerComputations" => Ok(TargetCapabilityFlags::IntegerComputations),
-            "FloatingPointComputations" => Ok(TargetCapabilityFlags::FloatingPointComputations),
-            "BackwardsBranching" => Ok(TargetCapabilityFlags::BackwardsBranching),
-            "HigherLevelConstructs" => Ok(TargetCapabilityFlags::HigherLevelConstructs),
-            "QubitReset" => Ok(TargetCapabilityFlags::QubitReset),
-            "Unrestricted" => Ok(TargetCapabilityFlags::all()),
-            _ => Err(()),
-        }
-    }
-}
-
 #[derive(Debug, Default)]
 pub struct CompileUnit {
     pub package: hir::Package,
diff --git a/compiler/qsc_frontend/src/incremental.rs b/compiler/qsc_frontend/src/incremental.rs
index 7cd4eb62..75a3d8f4 100644
--- a/compiler/qsc_frontend/src/incremental.rs
+++ b/compiler/qsc_frontend/src/incremental.rs
@@ -5,10 +5,7 @@
 mod tests;
 
 use crate::{
-    compile::{
-        self, preprocess, AstPackage, CompileUnit, Offsetter, PackageStore, SourceMap,
-        TargetCapabilityFlags,
-    },
+    compile::{self, preprocess, AstPackage, CompileUnit, Offsetter, PackageStore, SourceMap},
     error::WithSource,
     lower::Lowerer,
     resolve::{self, Resolver},
@@ -21,7 +18,7 @@ use qsc_ast::{
     validate::Validator as AstValidator,
     visit::Visitor as AstVisitor,
 };
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_hir::{
     assigner::Assigner as HirAssigner,
     hir::{self, PackageId},
diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs
index 9d58f804..a5ecfa45 100644
--- a/compiler/qsc_frontend/src/lower.rs
+++ b/compiler/qsc_frontend/src/lower.rs
@@ -6,13 +6,12 @@ mod tests;
 
 use crate::{
     closure::{self, Lambda, PartialApp},
-    compile::TargetCapabilityFlags,
     resolve::{self, Names},
     typeck::{self, convert},
 };
 use miette::Diagnostic;
 use qsc_ast::ast;
-use qsc_data_structures::{index_map::IndexMap, span::Span};
+use qsc_data_structures::{index_map::IndexMap, span::Span, target::TargetCapabilityFlags};
 use qsc_hir::{
     assigner::Assigner,
     hir::{self, LocalItemId},
diff --git a/compiler/qsc_linter/src/linter.rs b/compiler/qsc_linter/src/linter.rs
index b7901f0b..9e9d2af9 100644
--- a/compiler/qsc_linter/src/linter.rs
+++ b/compiler/qsc_linter/src/linter.rs
@@ -9,7 +9,7 @@ use crate::lints::{ast::AstLint, hir::HirLint};
 use miette::{Diagnostic, LabeledSpan};
 use qsc_data_structures::span::Span;
 use qsc_frontend::compile::CompileUnit;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
 use std::fmt::Display;
 
 /// The entry point to the linter. It takes a [`qsc_frontend::compile::CompileUnit`]
@@ -73,7 +73,7 @@ impl Diagnostic for Lint {
 
 /// A lint level. This defines if a lint will be treated as a warning or an error,
 /// and if the lint level can be overriden by the user.
-#[derive(Debug, Clone, Copy, Deserialize)]
+#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub enum LintLevel {
     /// The lint is effectively disabled.
@@ -101,7 +101,7 @@ impl Display for LintLevel {
 }
 
 /// End-user configuration for each lint level.
-#[derive(Debug, Clone, Deserialize)]
+#[derive(Debug, Clone, Deserialize, Serialize)]
 pub struct LintConfig {
     #[serde(rename = "lint")]
     /// Represents the lint name.
@@ -111,7 +111,7 @@ pub struct LintConfig {
 }
 
 /// Represents a lint name.
-#[derive(Debug, Clone, Copy, Deserialize)]
+#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
 #[serde(untagged)]
 pub enum LintKind {
     /// AST lint name.
diff --git a/compiler/qsc_linter/src/linter/ast.rs b/compiler/qsc_linter/src/linter/ast.rs
index a5dad4fa..d62cd008 100644
--- a/compiler/qsc_linter/src/linter/ast.rs
+++ b/compiler/qsc_linter/src/linter/ast.rs
@@ -131,9 +131,9 @@ macro_rules! declare_ast_lints {
 
     // Declare the `AstLint` enum.
     (@CONFIG_ENUM $($lint_name:ident),*) => {
-        use serde::Deserialize;
+        use serde::{Deserialize, Serialize};
 
-        #[derive(Debug, Clone, Copy, Deserialize)]
+        #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
         #[serde(rename_all = "camelCase")]
         pub enum AstLint {
             $($lint_name),*
diff --git a/compiler/qsc_linter/src/linter/hir.rs b/compiler/qsc_linter/src/linter/hir.rs
index e6e2d1e0..ee64c05b 100644
--- a/compiler/qsc_linter/src/linter/hir.rs
+++ b/compiler/qsc_linter/src/linter/hir.rs
@@ -119,9 +119,9 @@ macro_rules! declare_hir_lints {
 
     // Declare the `HirLint` enum.
     (@CONFIG_ENUM $($lint_name:ident),*) => {
-        use serde::Deserialize;
+        use serde::{Deserialize, Serialize};
 
-        #[derive(Debug, Clone, Copy, Deserialize)]
+        #[derive(Debug, Clone, Copy, Deserialize, Serialize)]
         #[serde(rename_all = "camelCase")]
         pub enum HirLint {
             $($lint_name),*
diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs
index 5c0ec744..1cc325d5 100644
--- a/compiler/qsc_partial_eval/src/lib.rs
+++ b/compiler/qsc_partial_eval/src/lib.rs
@@ -7,8 +7,8 @@ mod management;
 use evaluation_context::{Arg, BlockNode, EvaluationContext, Scope};
 use management::{QuantumIntrinsicsChecker, ResourceManager};
 use miette::Diagnostic;
-use qsc_data_structures::functors::FunctorApp;
 use qsc_data_structures::span::Span;
+use qsc_data_structures::{functors::FunctorApp, target::TargetCapabilityFlags};
 use qsc_eval::{
     self, exec_graph_section,
     output::GenericReceiver,
@@ -46,8 +46,10 @@ pub fn partially_evaluate(
     package_store: &PackageStore,
     compute_properties: &PackageStoreComputeProperties,
     entry: &ProgramEntry,
+    capabilities: TargetCapabilityFlags,
 ) -> Result<Program, Error> {
-    let partial_evaluator = PartialEvaluator::new(package_store, compute_properties, entry);
+    let partial_evaluator =
+        PartialEvaluator::new(package_store, compute_properties, entry, capabilities);
     partial_evaluator.eval()
 }
 
@@ -123,10 +125,12 @@ impl<'a> PartialEvaluator<'a> {
         package_store: &'a PackageStore,
         compute_properties: &'a PackageStoreComputeProperties,
         entry: &'a ProgramEntry,
+        capabilities: TargetCapabilityFlags,
     ) -> Self {
         // Create the entry-point callable.
         let mut resource_manager = ResourceManager::default();
         let mut program = Program::new();
+        program.config.capabilities = capabilities;
         let entry_block_id = resource_manager.next_block();
         program.blocks.insert(entry_block_id, rir::Block::default());
         let entry_point_id = resource_manager.next_callable();
diff --git a/compiler/qsc_passes/src/capabilitiesck.rs b/compiler/qsc_passes/src/capabilitiesck.rs
index a7e5d648..406501b6 100644
--- a/compiler/qsc_passes/src/capabilitiesck.rs
+++ b/compiler/qsc_passes/src/capabilitiesck.rs
@@ -14,7 +14,7 @@ mod tests_adaptive_plus_integers;
 pub mod tests_common;
 
 use miette::Diagnostic;
-use qsc_data_structures::span::Span;
+use qsc_data_structures::{span::Span, target::TargetCapabilityFlags};
 
 use qsc_fir::{
     fir::{
@@ -25,7 +25,7 @@ use qsc_fir::{
     ty::FunctorSetValue,
     visit::Visitor,
 };
-use qsc_frontend::compile::TargetCapabilityFlags;
+
 use qsc_lowerer::map_hir_package_to_fir;
 use qsc_rca::{
     Analyzer, ComputeKind, ItemComputeProperties, PackageComputeProperties,
diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs
index 9116ee60..45ff8988 100644
--- a/compiler/qsc_passes/src/lib.rs
+++ b/compiler/qsc_passes/src/lib.rs
@@ -20,8 +20,9 @@ use capabilitiesck::{check_supported_capabilities, lower_store, run_rca_pass};
 use entry_point::generate_entry_expr;
 use loop_unification::LoopUni;
 use miette::Diagnostic;
+use qsc_data_structures::target::TargetCapabilityFlags;
 use qsc_fir::fir;
-use qsc_frontend::compile::{CompileUnit, TargetCapabilityFlags};
+use qsc_frontend::compile::CompileUnit;
 use qsc_hir::{
     assigner::Assigner,
     global::{self, Table},
diff --git a/compiler/qsc_project/src/manifest.rs b/compiler/qsc_project/src/manifest.rs
index 4f39462a..8341d04a 100644
--- a/compiler/qsc_project/src/manifest.rs
+++ b/compiler/qsc_project/src/manifest.rs
@@ -10,13 +10,13 @@ use std::{
 };
 
 pub use qsc_linter::LintConfig;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
 use std::{path::PathBuf, sync::Arc};
 
 pub const MANIFEST_FILE_NAME: &str = "qsharp.json";
 
 /// A Q# manifest, used to describe project metadata.
-#[derive(Deserialize, Debug, Default, Clone)]
+#[derive(Deserialize, Serialize, Debug, Default, Clone)]
 #[serde(rename_all = "camelCase")]
 pub struct Manifest {
     pub author: Option<String>,
diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs
index 7a7544ea..5ee665a8 100644
--- a/compiler/qsc_rca/src/lib.rs
+++ b/compiler/qsc_rca/src/lib.rs
@@ -18,7 +18,10 @@ mod scaffolding;
 use crate::common::set_indentation;
 use bitflags::bitflags;
 use indenter::indented;
-use qsc_data_structures::index_map::{IndexMap, Iter};
+use qsc_data_structures::{
+    index_map::{IndexMap, Iter},
+    target::TargetCapabilityFlags,
+};
 use qsc_fir::{
     fir::{
         BlockId, ExprId, LocalItemId, PackageId, StmtId, StoreBlockId, StoreExprId, StoreItemId,
@@ -26,7 +29,7 @@ use qsc_fir::{
     },
     ty::Ty,
 };
-use qsc_frontend::compile::TargetCapabilityFlags;
+
 use std::{
     cmp::Ord,
     fmt::{self, Debug, Display, Formatter, Write},
diff --git a/compiler/qsc_rir/src/builder.rs b/compiler/qsc_rir/src/builder.rs
index 135f74c7..aa67cc07 100644
--- a/compiler/qsc_rir/src/builder.rs
+++ b/compiler/qsc_rir/src/builder.rs
@@ -1,6 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
+use qsc_data_structures::target::TargetCapabilityFlags;
+
 use crate::rir::{
     Block, BlockId, Callable, CallableId, CallableType, Instruction, Literal, Operand, Program, Ty,
     Variable, VariableId,
@@ -260,8 +262,6 @@ pub fn bell_program() -> Program {
     );
     program.num_qubits = 2;
     program.num_results = 2;
-    program.config.defer_measurements = true;
-    program.config.remap_qubits_on_reuse = true;
     program
 }
 
@@ -269,6 +269,7 @@ pub fn bell_program() -> Program {
 #[must_use]
 pub fn teleport_program() -> Program {
     let mut program = Program::default();
+    program.config.capabilities = TargetCapabilityFlags::Adaptive;
     program.callables.insert(CallableId(0), h_decl());
     program.callables.insert(CallableId(1), z_decl());
     program.callables.insert(CallableId(2), x_decl());
diff --git a/compiler/qsc_rir/src/passes.rs b/compiler/qsc_rir/src/passes.rs
index d6cd3076..6a730733 100644
--- a/compiler/qsc_rir/src/passes.rs
+++ b/compiler/qsc_rir/src/passes.rs
@@ -12,6 +12,7 @@ mod unreachable_code_check;
 
 use build_dominator_graph::build_dominator_graph;
 use defer_meas::defer_measurements;
+use qsc_data_structures::target::TargetCapabilityFlags;
 use reindex_qubits::reindex_qubits;
 use remap_block_ids::remap_block_ids;
 use ssa_check::check_ssa_form;
@@ -37,12 +38,18 @@ pub fn check_and_transform(program: &mut Program) {
     check_ssa_form(program, &preds, &doms);
     check_unreachable_code(program);
     check_types(program);
-}
 
-/// Run the RIR passes that are necessary for targets with no mid-program measurement.
-/// This requires that qubits are not reused after measurement or reset, so qubit ids must be reindexed.
-/// This also requires that the program is a single block and will panic otherwise.
-pub fn defer_quantum_measurements(program: &mut Program) {
-    reindex_qubits(program);
-    defer_measurements(program);
+    // Run the RIR passes that are necessary for targets with no mid-program measurement.
+    // This requires that qubits are not reused after measurement or reset, so qubit ids must be reindexed.
+    // This also requires that the program is a single block and will panic otherwise.
+    if !program
+        .config
+        .capabilities
+        .contains(TargetCapabilityFlags::QubitReset)
+    {
+        reindex_qubits(program);
+    }
+    if program.config.capabilities == TargetCapabilityFlags::empty() {
+        defer_measurements(program);
+    }
 }
diff --git a/compiler/qsc_rir/src/rir.rs b/compiler/qsc_rir/src/rir.rs
index 8d0687fe..b5ad1569 100644
--- a/compiler/qsc_rir/src/rir.rs
+++ b/compiler/qsc_rir/src/rir.rs
@@ -2,7 +2,7 @@
 // Licensed under the MIT License.
 
 use indenter::{indented, Indented};
-use qsc_data_structures::index_map::IndexMap;
+use qsc_data_structures::{index_map::IndexMap, target::TargetCapabilityFlags};
 use std::fmt::{self, Display, Formatter, Write};
 
 /// The root of the RIR.
@@ -60,8 +60,7 @@ impl Program {
 
 #[derive(Default)]
 pub struct Config {
-    pub remap_qubits_on_reuse: bool,
-    pub defer_measurements: bool,
+    pub capabilities: TargetCapabilityFlags,
 }
 
 impl Display for Config {
@@ -69,12 +68,11 @@ impl Display for Config {
         let mut indent = set_indentation(indented(f), 0);
         write!(indent, "Config:",)?;
         indent = set_indentation(indent, 1);
-        write!(
-            indent,
-            "\nremap_qubits_on_reuse: {}",
-            self.remap_qubits_on_reuse
-        )?;
-        write!(indent, "\ndefer_measurements: {}", self.defer_measurements)?;
+        if self.capabilities.is_empty() {
+            write!(indent, "\ncapabilities: Base")?;
+        } else {
+            write!(indent, "\ncapabilities: {:?}", self.capabilities)?;
+        }
         Ok(())
     }
 }
@@ -82,7 +80,7 @@ impl Display for Config {
 impl Config {
     #[must_use]
     pub fn is_base(&self) -> bool {
-        self.remap_qubits_on_reuse || self.defer_measurements
+        self.capabilities == TargetCapabilityFlags::empty()
     }
 }
 
diff --git a/language_service/src/compilation.rs b/language_service/src/compilation.rs
index e721f911..fb2e4284 100644
--- a/language_service/src/compilation.rs
+++ b/language_service/src/compilation.rs
@@ -72,6 +72,21 @@ impl Compilation {
             language_features,
         );
 
+        // Compute new lints and append them to the errors Vec.
+        // Lints are only computed if the erros vector is empty. For performance
+        // reasons we don't want to waste time running lints every few keystrokes,
+        // if the user is in the middle of typing a statement, for example.
+        if errors.is_empty() {
+            let lints = qsc::linter::run_lints(&unit, Some(lints_config));
+            let lints: Vec<_> = lints
+                .into_iter()
+                .map(|lint| {
+                    WithSource::from_map(&unit.sources, qsc::compile::ErrorKind::Lint(lint))
+                })
+                .collect();
+            errors.extend(lints);
+        }
+
         let package_id = package_store.insert(unit);
         let unit = package_store
             .get(package_id)
@@ -106,6 +121,7 @@ impl Compilation {
         cells: I,
         target_profile: Profile,
         language_features: LanguageFeatures,
+        lints_config: &[LintConfig],
     ) -> Self
     where
         I: Iterator<Item = (Arc<str>, Arc<str>)>,
@@ -138,6 +154,21 @@ impl Compilation {
             .get(package_id)
             .expect("expected to find user package");
 
+        // Compute new lints and append them to the errors Vec.
+        // Lints are only computed if the erros vector is empty. For performance
+        // reasons we don't want to waste time running lints every few keystrokes,
+        // if the user is in the middle of typing a statement, for example.
+        if errors.is_empty() {
+            let lints = qsc::linter::run_lints(unit, Some(lints_config));
+            let lints: Vec<_> = lints
+                .into_iter()
+                .map(|lint| {
+                    WithSource::from_map(&unit.sources, qsc::compile::ErrorKind::Lint(lint))
+                })
+                .collect();
+            errors.extend(lints);
+        }
+
         run_fir_passes(
             &mut errors,
             target_profile,
@@ -234,7 +265,7 @@ impl Compilation {
                 lints_config,
             ),
             CompilationKind::Notebook => {
-                Self::new_notebook(sources, target_profile, language_features)
+                Self::new_notebook(sources, target_profile, language_features, lints_config)
             }
         };
         self.package_store = new.package_store;
diff --git a/language_service/src/protocol.rs b/language_service/src/protocol.rs
index a6826c41..2ec8c979 100644
--- a/language_service/src/protocol.rs
+++ b/language_service/src/protocol.rs
@@ -3,6 +3,7 @@
 
 use qsc::line_column::Range;
 use qsc::{compile::Error, target::Profile, LanguageFeatures, PackageType};
+use qsc_project::Manifest;
 
 /// A change to the workspace configuration
 #[derive(Clone, Debug, Default, Copy)]
@@ -122,6 +123,7 @@ pub struct ParameterInformation {
 pub struct NotebookMetadata {
     pub target_profile: Option<Profile>,
     pub language_features: LanguageFeatures,
+    pub manifest: Option<Manifest>,
 }
 
 #[derive(Debug)]
diff --git a/language_service/src/state.rs b/language_service/src/state.rs
index 2bb93feb..fa40339f 100644
--- a/language_service/src/state.rs
+++ b/language_service/src/state.rs
@@ -367,7 +367,7 @@ impl<'a> CompilationStateUpdater<'a> {
                 target_profile: notebook_metadata.target_profile,
                 package_type: None,
                 language_features: Some(notebook_metadata.language_features),
-                lints_config: None,
+                lints_config: notebook_metadata.manifest.map(|manifest| manifest.lints),
             };
             let configuration = merge_configurations(&notebook_configuration, &configuration);
 
@@ -387,6 +387,10 @@ impl<'a> CompilationStateUpdater<'a> {
                 }),
                 configuration.target_profile,
                 notebook_configuration.language_features.unwrap_or_default(),
+                notebook_configuration
+                    .lints_config
+                    .as_ref()
+                    .unwrap_or(&vec![]),
             );
 
             state.compilations.insert(
diff --git a/pip/qsharp/_qsharp.py b/pip/qsharp/_qsharp.py
index 48eb06e9..f0218288 100644
--- a/pip/qsharp/_qsharp.py
+++ b/pip/qsharp/_qsharp.py
@@ -23,7 +23,12 @@ class Config:
     Configuration hints for the language service.
     """
 
-    def __init__(self, target_profile: TargetProfile, language_features: List[str]):
+    def __init__(
+        self,
+        target_profile: TargetProfile,
+        language_features: Optional[List[str]],
+        manifest: Optional[str],
+    ):
         if target_profile == TargetProfile.Quantinuum:
             self._config = {"targetProfile": "quantinuum"}
             warn("The Quantinuum target profile is a preview feature.")
@@ -34,6 +39,7 @@ class Config:
             self._config = {"targetProfile": "unrestricted"}
 
         self._config["languageFeatures"] = language_features
+        self._config["manifest"] = manifest
 
     def __repr__(self) -> str:
         return "Q# initialized with configuration: " + str(self._config)
@@ -55,7 +61,7 @@ def init(
     *,
     target_profile: TargetProfile = TargetProfile.Unrestricted,
     project_root: Optional[str] = None,
-    language_features: List[str] = [],
+    language_features: Optional[List[str]] = None,
 ) -> Config:
     """
     Initializes the Q# interpreter.
@@ -71,6 +77,7 @@ def init(
 
     global _interpreter
 
+    manifest_contents = None
     manifest_descriptor = None
     if project_root is not None:
         qsharp_json = join(project_root, "qsharp.json")
@@ -83,26 +90,13 @@ def init(
         manifest_descriptor["manifest_dir"] = project_root
 
         try:
-            (_, file_contents) = read_file(qsharp_json)
+            (_, manifest_contents) = read_file(qsharp_json)
+            manifest_descriptor["manifest"] = manifest_contents
         except Exception as e:
             raise QSharpError(
                 f"Error reading {qsharp_json}. qsharp.json should exist at the project root and be a valid JSON file."
             ) from e
 
-        try:
-            manifest_descriptor["manifest"] = json.loads(file_contents)
-        except Exception as e:
-            raise QSharpError(
-                f"Error parsing {qsharp_json}. qsharp.json should exist at the project root and be a valid JSON file."
-            ) from e
-
-    # if no features were passed in as an argument, use the features from the manifest.
-    # this way we prefer the features from the argument over those from the manifest.
-    if language_features == [] and manifest_descriptor != None:
-        language_features = (
-            manifest_descriptor["manifest"].get("languageFeatures") or []
-        )
-
     _interpreter = Interpreter(
         target_profile,
         language_features,
@@ -113,7 +107,7 @@ def init(
 
     # Return the configuration information to provide a hint to the
     # language service through the cell output.
-    return Config(target_profile, language_features)
+    return Config(target_profile, language_features, manifest_contents)
 
 
 def get_interpreter() -> Interpreter:
diff --git a/pip/src/interpreter.rs b/pip/src/interpreter.rs
index 25037f7b..a8c3ef3f 100644
--- a/pip/src/interpreter.rs
+++ b/pip/src/interpreter.rs
@@ -81,22 +81,19 @@ impl FromPyObject<'_> for PyManifestDescriptor {
         let manifest_dir = get_dict_opt_string(dict, "manifest_dir")?.ok_or(
             PyException::new_err("missing key `manifest_dir` in manifest descriptor"),
         )?;
-        let manifest = dict
-            .get_item("manifest")?
-            .ok_or(PyException::new_err(
-                "missing key `manifest` in manifest descriptor",
-            ))?
-            .downcast::<PyDict>()?;
 
-        let language_features = get_dict_opt_list_string(manifest, "features")?;
+        let manifest = get_dict_opt_string(dict, "manifest")?.ok_or(PyException::new_err(
+            "missing key `manifest` in manifest descriptor",
+        ))?;
+
+        let manifest = serde_json::from_str::<Manifest>(&manifest).map_err(|_| {
+            PyErr::new::<PyException, _>(format!(
+                "Error parsing {manifest_dir}/qsharp.json . Manifest should be a valid JSON file."
+            ))
+        })?;
 
         Ok(Self(ManifestDescriptor {
-            manifest: Manifest {
-                author: get_dict_opt_string(manifest, "author")?,
-                license: get_dict_opt_string(manifest, "license")?,
-                language_features,
-                lints: vec![],
-            },
+            manifest,
             manifest_dir: manifest_dir.into(),
         }))
     }
@@ -121,7 +118,15 @@ impl Interpreter {
             TargetProfile::Base => Profile::Base,
             TargetProfile::Unrestricted => Profile::Unrestricted,
         };
-        let language_features = language_features.unwrap_or_default();
+        // If no features were passed in as an argument, use the features from the manifest.
+        // this way we prefer the features from the argument over those from the manifest.
+        let language_features: Vec<String> = match (language_features, &manifest_descriptor) {
+            (Some(language_features), _) => language_features,
+            (_, Some(manifest_descriptor)) => {
+                manifest_descriptor.0.manifest.language_features.clone()
+            }
+            (None, None) => vec![],
+        };
 
         let sources = if let Some(manifest_descriptor) = manifest_descriptor {
             let project = file_system(
@@ -607,21 +612,3 @@ fn get_dict_opt_string(dict: &PyDict, key: &str) -> PyResult<Option<String>> {
         None => None,
     })
 }
-fn get_dict_opt_list_string(dict: &PyDict, key: &str) -> PyResult<Vec<String>> {
-    let value = dict.get_item(key)?;
-    let list: &PyList = match value {
-        Some(item) => item.downcast::<PyList>()?,
-        None => return Ok(vec![]),
-    };
-    match list
-        .iter()
-        .map(|item| {
-            item.downcast::<PyString>()
-                .map(|s| s.to_string_lossy().into())
-        })
-        .collect::<std::result::Result<Vec<String>, _>>()
-    {
-        Ok(list) => Ok(list),
-        Err(e) => Err(e.into()),
-    }
-}
diff --git a/wasm/src/language_service.rs b/wasm/src/language_service.rs
index 01e53e07..2db291ca 100644
--- a/wasm/src/language_service.rs
+++ b/wasm/src/language_service.rs
@@ -12,6 +12,7 @@ use crate::{
     serializable_type,
 };
 use qsc::{self, line_column::Encoding, target::Profile, LanguageFeatures, PackageType};
+use qsc_project::Manifest;
 use qsls::protocol::DiagnosticUpdate;
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
@@ -118,15 +119,26 @@ impl LanguageService {
     ) {
         let cells: Vec<Cell> = cells.into_iter().map(std::convert::Into::into).collect();
         let notebook_metadata: NotebookMetadata = notebook_metadata.into();
+        let manifest: Option<Manifest> = notebook_metadata
+            .manifest
+            .and_then(|manifest| serde_json::from_str(&manifest).ok());
+
+        // If no features were passed in as an argument, use the features from the manifest.
+        // this way we prefer the features from the argument over those from the manifest.
+        let language_features: Vec<String> = match (notebook_metadata.languageFeatures, &manifest) {
+            (Some(language_features), _) => language_features,
+            (_, Some(manifest)) => manifest.language_features.clone(),
+            (None, None) => vec![],
+        };
+
         self.0.update_notebook_document(
             notebook_uri,
             qsls::protocol::NotebookMetadata {
                 target_profile: notebook_metadata
                     .targetProfile
                     .map(|s| Profile::from_str(&s).expect("invalid target profile")),
-                language_features: LanguageFeatures::from_iter(
-                    notebook_metadata.languageFeatures.unwrap_or_default(),
-                ),
+                language_features: LanguageFeatures::from_iter(language_features),
+                manifest,
             },
             cells
                 .iter()
@@ -486,11 +498,13 @@ serializable_type! {
     NotebookMetadata,
     {
         pub targetProfile: Option<String>,
-        pub languageFeatures: Option<Vec<String>>
+        pub languageFeatures: Option<Vec<String>>,
+        pub manifest: Option<String>,
     },
     r#"export interface INotebookMetadata {
         targetProfile?: "base" | "quantinuum" | "unrestricted";
         languageFeatures?: "v2-preview-syntax"[];
+        manifest?: string;
     }"#,
     INotebookMetadata
 }


Test patch:
diff --git a/compiler/qsc/src/bin/memtest.rs b/compiler/qsc/src/bin/memtest.rs
index 05cb61bf..d7471b07 100644
--- a/compiler/qsc/src/bin/memtest.rs
+++ b/compiler/qsc/src/bin/memtest.rs
@@ -4,7 +4,8 @@
 //! Records the memory usage of the compiler.
 
 use qsc::{compile, CompileUnit};
-use qsc_frontend::compile::{PackageStore, TargetCapabilityFlags};
+use qsc_data_structures::target::TargetCapabilityFlags;
+use qsc_frontend::compile::PackageStore;
 use std::{
     alloc::{GlobalAlloc, Layout, System},
     sync::atomic::{AtomicU64, Ordering},
diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs
new file mode 100644
index 00000000..1c23e2f0
--- /dev/null
+++ b/compiler/qsc/src/codegen/tests.rs
@@ -0,0 +1,661 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+use expect_test::expect;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::SourceMap;
+
+use crate::codegen::get_qir;
+
+#[test]
+fn code_with_errors_returns_errors() {
+    let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : Unit {
+                use q = Qubit()
+                let pi_over_two = 4.0 / 2.0;
+            }
+        }";
+    let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+    let language_features = LanguageFeatures::default();
+    let capabilities = TargetCapabilityFlags::empty();
+
+    expect![[r#"
+        Err(
+            "Failed to generate QIR. Could not compile sources.:\nsyntax error\n",
+        )
+    "#]]
+    .assert_debug_eq(&get_qir(sources, language_features, capabilities));
+}
+
+mod base_profile {
+    use expect_test::expect;
+    use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+    use qsc_frontend::compile::SourceMap;
+
+    use crate::codegen::get_qir;
+
+    #[test]
+    fn simple() {
+        let source = "namespace Test {
+            open Microsoft.Quantum.Math;
+            open QIR.Intrinsic;
+            @EntryPoint()
+            operation Main() : Result {
+                use q = Qubit();
+                let pi_over_two = 4.0 / 2.0;
+                __quantum__qis__rz__body(pi_over_two, q);
+                mutable some_angle = ArcSin(0.0);
+                __quantum__qis__rz__body(some_angle, q);
+                set some_angle = ArcCos(-1.0) / PI();
+                __quantum__qis__rz__body(some_angle, q);
+                __quantum__qis__mresetz__body(q)
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::empty();
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__rz__body(double, %Qubit*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+        "#]]
+        .assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_reuse_triggers_reindexing() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : (Result, Result) {
+                use q = Qubit();
+                (MResetZ(q), MResetZ(q))
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::empty();
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__rt__tuple_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+        "#]].assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_measurements_get_deferred() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : Result[] {
+                use (q0, q1) = (Qubit(), Qubit());
+                X(q0);
+                let r0 = MResetZ(q0);
+                X(q1);
+                let r1 = MResetZ(q1);
+                [r0, r1]
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::empty();
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__array_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__x__body(%Qubit*)
+
+            declare void @__quantum__rt__array_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+        "#]].assert_eq(&qir);
+    }
+}
+
+mod adaptive_profile {
+    use expect_test::expect;
+    use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+    use qsc_frontend::compile::SourceMap;
+
+    use crate::codegen::get_qir;
+
+    #[test]
+    fn simple() {
+        let source = "namespace Test {
+            open Microsoft.Quantum.Math;
+            open QIR.Intrinsic;
+            @EntryPoint()
+            operation Main() : Result {
+                use q = Qubit();
+                let pi_over_two = 4.0 / 2.0;
+                __quantum__qis__rz__body(pi_over_two, q);
+                mutable some_angle = ArcSin(0.0);
+                __quantum__qis__rz__body(some_angle, q);
+                set some_angle = ArcCos(-1.0) / PI();
+                __quantum__qis__rz__body(some_angle, q);
+                __quantum__qis__mresetz__body(q)
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__rz__body(double, %Qubit*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 false}
+            !5 = !{i32 1, !"classical_floats", i1 false}
+            !6 = !{i32 1, !"backwards_branching", i1 false}
+            !7 = !{i32 1, !"qubit_resetting", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]]
+        .assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_reuse_triggers_reindexing() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : (Result, Result) {
+                use q = Qubit();
+                (MResetZ(q), MResetZ(q))
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__rt__tuple_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 false}
+            !5 = !{i32 1, !"classical_floats", i1 false}
+            !6 = !{i32 1, !"backwards_branching", i1 false}
+            !7 = !{i32 1, !"qubit_resetting", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]].assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_measurements_not_deferred() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : Result[] {
+                use (q0, q1) = (Qubit(), Qubit());
+                X(q0);
+                let r0 = MResetZ(q0);
+                X(q1);
+                let r1 = MResetZ(q1);
+                [r0, r1]
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
+              call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__array_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__x__body(%Qubit*)
+
+            declare void @__quantum__rt__array_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            declare void @__quantum__qis__mz__body(%Qubit*, %Result*) #1
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 false}
+            !5 = !{i32 1, !"classical_floats", i1 false}
+            !6 = !{i32 1, !"backwards_branching", i1 false}
+            !7 = !{i32 1, !"qubit_resetting", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]].assert_eq(&qir);
+    }
+}
+
+mod quantinuum_profile {
+    use expect_test::expect;
+    use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+    use qsc_frontend::compile::SourceMap;
+
+    use crate::codegen::get_qir;
+
+    #[test]
+    fn simple() {
+        let source = "namespace Test {
+            open Microsoft.Quantum.Math;
+            open QIR.Intrinsic;
+            @EntryPoint()
+            operation Main() : Result {
+                use q = Qubit();
+                let pi_over_two = 4.0 / 2.0;
+                __quantum__qis__rz__body(pi_over_two, q);
+                mutable some_angle = ArcSin(0.0);
+                __quantum__qis__rz__body(some_angle, q);
+                set some_angle = ArcCos(-1.0) / PI();
+                __quantum__qis__rz__body(some_angle, q);
+                __quantum__qis__mresetz__body(q)
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__rz__body(double, %Qubit*)
+
+            declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]]
+        .assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_reuse_allowed() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : (Result, Result) {
+                use q = Qubit();
+                (MResetZ(q), MResetZ(q))
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__tuple_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
+
+            declare void @__quantum__rt__tuple_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]].assert_eq(&qir);
+    }
+
+    #[test]
+    fn qubit_measurements_not_deferred() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : Result[] {
+                use (q0, q1) = (Qubit(), Qubit());
+                X(q0);
+                let r0 = MResetZ(q0);
+                X(q1);
+                let r1 = MResetZ(q1);
+                [r0, r1]
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
+              call void @__quantum__rt__array_record_output(i64 2, i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__x__body(%Qubit*)
+
+            declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
+
+            declare void @__quantum__rt__array_record_output(i64, i8*)
+
+            declare void @__quantum__rt__result_record_output(%Result*, i8*)
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]].assert_eq(&qir);
+    }
+
+    #[test]
+    fn dynamic_integer_with_branch_and_phi_supported() {
+        let source = "namespace Test {
+            @EntryPoint()
+            operation Main() : Int {
+                use q = Qubit();
+                H(q);
+                MResetZ(q) == Zero ? 0 | 1
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let qir =
+            get_qir(sources, language_features, capabilities).expect("Failed to generate QIR");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              %var_0 = call i1 @__quantum__qis__read_result__body(%Result* inttoptr (i64 0 to %Result*))
+              %var_1 = icmp eq i1 %var_0, false
+              br i1 %var_1, label %block_1, label %block_2
+            block_1:
+              br label %block_3
+            block_2:
+              br label %block_3
+            block_3:
+              %var_2 = phi i64 [0, %block_1], [1, %block_2]
+              call void @__quantum__rt__integer_record_output(i64 %var_2, i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__h__body(%Qubit*)
+
+            declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
+
+            declare i1 @__quantum__qis__read_result__body(%Result*)
+
+            declare void @__quantum__rt__integer_record_output(i64, i8*)
+
+            attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
+            attributes #1 = { "irreversible" }
+
+            ; module flags
+
+            !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+            !0 = !{i32 1, !"qir_major_version", i32 1}
+            !1 = !{i32 7, !"qir_minor_version", i32 0}
+            !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+            !3 = !{i32 1, !"dynamic_result_management", i1 false}
+            !4 = !{i32 1, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]].assert_eq(&qir);
+    }
+}
diff --git a/compiler/qsc/src/interpret/debug/tests.rs b/compiler/qsc/src/interpret/debug/tests.rs
index 3de86b72..c8bba1ef 100644
--- a/compiler/qsc/src/interpret/debug/tests.rs
+++ b/compiler/qsc/src/interpret/debug/tests.rs
@@ -5,9 +5,9 @@
 
 use indoc::indoc;
 use miette::Result;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_eval::{output::CursorReceiver, val::Value};
-use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::SourceMap;
 use qsc_passes::PackageType;
 use std::io::Cursor;
 
diff --git a/compiler/qsc/src/interpret/debugger_tests.rs b/compiler/qsc/src/interpret/debugger_tests.rs
index d3e73953..93f236a3 100644
--- a/compiler/qsc/src/interpret/debugger_tests.rs
+++ b/compiler/qsc/src/interpret/debugger_tests.rs
@@ -8,7 +8,7 @@ use crate::line_column::Encoding;
 use qsc_data_structures::language_features::LanguageFeatures;
 use qsc_eval::{output::CursorReceiver, StepAction, StepResult};
 use qsc_fir::fir::StmtId;
-use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::SourceMap;
 use std::io::Cursor;
 
 fn get_breakpoint_ids(debugger: &Debugger, path: &str) -> Vec<StmtId> {
@@ -123,6 +123,8 @@ mod given_debugger {
         }"#;
     #[cfg(test)]
     mod step {
+        use qsc_data_structures::target::TargetCapabilityFlags;
+
         use super::*;
 
         #[test]
diff --git a/compiler/qsc/src/interpret/tests.rs b/compiler/qsc/src/interpret/tests.rs
index 85cf65e7..796083a8 100644
--- a/compiler/qsc/src/interpret/tests.rs
+++ b/compiler/qsc/src/interpret/tests.rs
@@ -7,9 +7,9 @@ mod given_interpreter {
     use crate::interpret::{Error, InterpretResult, Interpreter};
     use expect_test::Expect;
     use miette::Diagnostic;
-    use qsc_data_structures::language_features::LanguageFeatures;
+    use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
     use qsc_eval::{output::CursorReceiver, val::Value};
-    use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags};
+    use qsc_frontend::compile::SourceMap;
     use qsc_passes::PackageType;
     use std::{fmt::Write, io::Cursor, iter, str::from_utf8};
 
@@ -53,7 +53,6 @@ mod given_interpreter {
     mod without_sources {
         use expect_test::expect;
         use indoc::indoc;
-        use qsc_frontend::compile::TargetCapabilityFlags;
 
         use super::*;
 
@@ -727,7 +726,9 @@ mod given_interpreter {
                 true,
                 SourceMap::default(),
                 PackageType::Lib,
-                TargetCapabilityFlags::Adaptive,
+                TargetCapabilityFlags::Adaptive
+                    | TargetCapabilityFlags::QubitReset
+                    | TargetCapabilityFlags::IntegerComputations,
                 LanguageFeatures::default(),
             )
             .expect("interpreter should be created");
@@ -778,12 +779,19 @@ mod given_interpreter {
 
                 ; module flags
 
-                !llvm.module.flags = !{!0, !1, !2, !3}
+                !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
 
                 !0 = !{i32 1, !"qir_major_version", i32 1}
                 !1 = !{i32 7, !"qir_minor_version", i32 0}
                 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
                 !3 = !{i32 1, !"dynamic_result_management", i1 false}
+                !4 = !{i32 1, !"classical_ints", i1 true}
+                !5 = !{i32 1, !"qubit_resetting", i1 true}
+                !6 = !{i32 1, !"classical_floats", i1 false}
+                !7 = !{i32 1, !"backwards_branching", i1 false}
+                !8 = !{i32 1, !"classical_fixed_points", i1 false}
+                !9 = !{i32 1, !"user_functions", i1 false}
+                !10 = !{i32 1, !"multiple_target_branching", i1 false}
             "#]]
             .assert_eq(&res);
         }
@@ -794,7 +802,7 @@ mod given_interpreter {
                 true,
                 SourceMap::default(),
                 PackageType::Lib,
-                TargetCapabilityFlags::Adaptive,
+                TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::QubitReset,
                 LanguageFeatures::default(),
             )
             .expect("interpreter should be created");
@@ -848,12 +856,19 @@ mod given_interpreter {
 
                 ; module flags
 
-                !llvm.module.flags = !{!0, !1, !2, !3}
+                !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
 
                 !0 = !{i32 1, !"qir_major_version", i32 1}
                 !1 = !{i32 7, !"qir_minor_version", i32 0}
                 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
                 !3 = !{i32 1, !"dynamic_result_management", i1 false}
+                !4 = !{i32 1, !"qubit_resetting", i1 true}
+                !5 = !{i32 1, !"classical_ints", i1 false}
+                !6 = !{i32 1, !"classical_floats", i1 false}
+                !7 = !{i32 1, !"backwards_branching", i1 false}
+                !8 = !{i32 1, !"classical_fixed_points", i1 false}
+                !9 = !{i32 1, !"user_functions", i1 false}
+                !10 = !{i32 1, !"multiple_target_branching", i1 false}
             "#]]
             .assert_eq(&res);
         }
@@ -1442,9 +1457,10 @@ mod given_interpreter {
         use crate::line_column::Encoding;
         use expect_test::expect;
         use indoc::indoc;
+
         use qsc_ast::ast::{Expr, ExprKind, NodeId, Package, Path, Stmt, StmtKind, TopLevelNode};
         use qsc_data_structures::span::Span;
-        use qsc_frontend::compile::{SourceMap, TargetCapabilityFlags};
+        use qsc_frontend::compile::SourceMap;
         use qsc_passes::PackageType;
 
         #[test]
diff --git a/compiler/qsc_circuit/src/operations/tests.rs b/compiler/qsc_circuit/src/operations/tests.rs
index 94254aba..dcbd4604 100644
--- a/compiler/qsc_circuit/src/operations/tests.rs
+++ b/compiler/qsc_circuit/src/operations/tests.rs
@@ -3,8 +3,10 @@
 
 use super::*;
 use expect_test::expect;
-use qsc_data_structures::{functors::FunctorApp, language_features::LanguageFeatures};
-use qsc_frontend::compile::{compile, core, std, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{
+    functors::FunctorApp, language_features::LanguageFeatures, target::TargetCapabilityFlags,
+};
+use qsc_frontend::compile::{compile, core, std, PackageStore, SourceMap};
 use qsc_hir::hir::{Item, ItemKind};
 
 fn compile_one_operation(code: &str) -> (Item, String) {
diff --git a/compiler/qsc_codegen/src/qir/tests.rs b/compiler/qsc_codegen/src/qir/tests.rs
index ddc887b9..80c8a0f5 100644
--- a/compiler/qsc_codegen/src/qir/tests.rs
+++ b/compiler/qsc_codegen/src/qir/tests.rs
@@ -219,11 +219,18 @@ fn teleport_program() {
 
         ; module flags
 
-        !llvm.module.flags = !{!0, !1, !2, !3}
+        !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
 
         !0 = !{i32 1, !"qir_major_version", i32 1}
         !1 = !{i32 7, !"qir_minor_version", i32 0}
         !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
         !3 = !{i32 1, !"dynamic_result_management", i1 false}
+        !4 = !{i32 1, !"classical_ints", i1 false}
+        !5 = !{i32 1, !"classical_floats", i1 false}
+        !6 = !{i32 1, !"backwards_branching", i1 false}
+        !7 = !{i32 1, !"qubit_resetting", i1 false}
+        !8 = !{i32 1, !"classical_fixed_points", i1 false}
+        !9 = !{i32 1, !"user_functions", i1 false}
+        !10 = !{i32 1, !"multiple_target_branching", i1 false}
     "#]].assert_eq(&program.to_qir(&program));
 }
diff --git a/compiler/qsc_codegen/src/qir_base/tests.rs b/compiler/qsc_codegen/src/qir_base/tests.rs
index 4002d546..9d3b2ef9 100644
--- a/compiler/qsc_codegen/src/qir_base/tests.rs
+++ b/compiler/qsc_codegen/src/qir_base/tests.rs
@@ -8,8 +8,8 @@ use std::sync::Arc;
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_passes::{run_core_passes, run_default_passes, PackageType};
 
 use crate::qir_base::generate_qir;
diff --git a/compiler/qsc_codegen/src/qsharp/test_utils.rs b/compiler/qsc_codegen/src/qsharp/test_utils.rs
index 2a8f8f29..e13d728e 100644
--- a/compiler/qsc_codegen/src/qsharp/test_utils.rs
+++ b/compiler/qsc_codegen/src/qsharp/test_utils.rs
@@ -8,8 +8,10 @@ use std::sync::Arc;
 
 use expect_test::Expect;
 use qsc_ast::{ast::Package, mut_visit::MutVisitor};
-use qsc_data_structures::{language_features::LanguageFeatures, span::Span};
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{
+    language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags,
+};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::hir::PackageId;
 use qsc_passes::{run_core_passes, run_default_passes, PackageType};
 
diff --git a/compiler/qsc_eval/src/intrinsic/tests.rs b/compiler/qsc_eval/src/intrinsic/tests.rs
index 30a3184a..7925858d 100644
--- a/compiler/qsc_eval/src/intrinsic/tests.rs
+++ b/compiler/qsc_eval/src/intrinsic/tests.rs
@@ -17,8 +17,9 @@ use expect_test::{expect, Expect};
 use indoc::indoc;
 use num_bigint::BigInt;
 use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::target::TargetCapabilityFlags;
 use qsc_fir::fir;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_lowerer::map_hir_package_to_fir;
 use qsc_passes::{run_core_passes, run_default_passes, PackageType};
 
diff --git a/compiler/qsc_eval/src/tests.rs b/compiler/qsc_eval/src/tests.rs
index 1d74c7ba..5263f05b 100644
--- a/compiler/qsc_eval/src/tests.rs
+++ b/compiler/qsc_eval/src/tests.rs
@@ -14,10 +14,10 @@ use crate::{
 };
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_fir::fir::{self, ExecGraphNode, StmtId};
 use qsc_fir::fir::{PackageId, PackageStoreLookup};
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_lowerer::map_hir_package_to_fir;
 use qsc_passes::{run_core_passes, run_default_passes, PackageType};
 
diff --git a/compiler/qsc_frontend/src/incremental/tests.rs b/compiler/qsc_frontend/src/incremental/tests.rs
index a055b2a3..ac077e60 100644
--- a/compiler/qsc_frontend/src/incremental/tests.rs
+++ b/compiler/qsc_frontend/src/incremental/tests.rs
@@ -3,13 +3,13 @@
 
 use super::{Compiler, Increment};
 use crate::{
-    compile::{self, CompileUnit, PackageStore, TargetCapabilityFlags},
+    compile::{self, CompileUnit, PackageStore},
     incremental::Error,
 };
 use expect_test::{expect, Expect};
 use indoc::indoc;
 use miette::Diagnostic;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use std::fmt::Write;
 
 #[allow(clippy::too_many_lines)]
diff --git a/compiler/qsc_frontend/src/lower/tests.rs b/compiler/qsc_frontend/src/lower/tests.rs
index 0f63a88b..8f0a9b65 100644
--- a/compiler/qsc_frontend/src/lower/tests.rs
+++ b/compiler/qsc_frontend/src/lower/tests.rs
@@ -3,10 +3,10 @@
 
 #![allow(clippy::needless_raw_string_hashes)]
 
-use crate::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use crate::compile::{self, compile, PackageStore, SourceMap};
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 
 fn check_hir(input: &str, expect: &Expect) {
     let sources = SourceMap::new([("test".into(), input.into())], None);
diff --git a/compiler/qsc_frontend/src/resolve/tests.rs b/compiler/qsc_frontend/src/resolve/tests.rs
index 0969ddd5..34222770 100644
--- a/compiler/qsc_frontend/src/resolve/tests.rs
+++ b/compiler/qsc_frontend/src/resolve/tests.rs
@@ -6,7 +6,6 @@
 use super::{Error, Locals, Names, Res};
 use crate::{
     compile,
-    compile::TargetCapabilityFlags,
     resolve::{LocalKind, Resolver},
 };
 use expect_test::{expect, Expect};
@@ -17,7 +16,9 @@ use qsc_ast::{
     mut_visit::MutVisitor,
     visit::{self, Visitor},
 };
-use qsc_data_structures::{language_features::LanguageFeatures, span::Span};
+use qsc_data_structures::{
+    language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags,
+};
 use qsc_hir::assigner::Assigner as HirAssigner;
 use std::fmt::Write;
 
diff --git a/compiler/qsc_linter/src/tests.rs b/compiler/qsc_linter/src/tests.rs
index 70c0c6ce..fe436d92 100644
--- a/compiler/qsc_linter/src/tests.rs
+++ b/compiler/qsc_linter/src/tests.rs
@@ -6,8 +6,8 @@ use crate::{
     Lint, LintConfig, LintLevel,
 };
 use expect_test::{expect, Expect};
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, CompileUnit, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, CompileUnit, PackageStore, SourceMap};
 use qsc_passes::PackageType;
 
 #[test]
diff --git a/compiler/qsc_partial_eval/tests/output_recording.rs b/compiler/qsc_partial_eval/tests/output_recording.rs
index 3c9cd3c6..15677e26 100644
--- a/compiler/qsc_partial_eval/tests/output_recording.rs
+++ b/compiler/qsc_partial_eval/tests/output_recording.rs
@@ -83,8 +83,7 @@ fn output_recording_for_tuple_of_different_types() {
                     Call id(5), args( Variable(1, Boolean), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 1
             num_results: 1"#]]
     .assert_eq(&program.to_string());
@@ -170,8 +169,7 @@ fn output_recording_for_nested_tuples() {
                     Call id(5), args( Variable(3, Boolean), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 1
             num_results: 1"#]]
     .assert_eq(&program.to_string());
@@ -265,8 +263,7 @@ fn output_recording_for_tuple_of_arrays() {
                     Call id(6), args( Variable(3, Boolean), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 1
             num_results: 1"#]]
     .assert_eq(&program.to_string());
@@ -360,8 +357,7 @@ fn output_recording_for_array_of_tuples() {
                     Call id(6), args( Variable(3, Boolean), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 1
             num_results: 1"#]]
     .assert_eq(&program.to_string());
@@ -403,8 +399,7 @@ fn output_recording_for_literal_bool() {
                     Call id(1), args( Bool(true), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -446,8 +441,7 @@ fn output_recording_for_literal_int() {
                     Call id(1), args( Integer(42), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -518,8 +512,7 @@ fn output_recording_for_mix_of_literal_and_variable() {
                     Call id(4), args( Bool(true), Pointer, )
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 1
             num_results: 1"#]]
     .assert_eq(&program.to_string());
diff --git a/compiler/qsc_partial_eval/tests/test_utils.rs b/compiler/qsc_partial_eval/tests/test_utils.rs
index 2552d808..8d558be4 100644
--- a/compiler/qsc_partial_eval/tests/test_utils.rs
+++ b/compiler/qsc_partial_eval/tests/test_utils.rs
@@ -3,9 +3,9 @@
 
 use expect_test::Expect;
 use qsc::{incremental::Compiler, PackageType};
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_fir::fir::PackageStore;
-use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap};
 use qsc_lowerer::{map_hir_package_to_fir, Lowerer};
 use qsc_partial_eval::{partially_evaluate, ProgramEntry};
 use qsc_rca::{Analyzer, PackageStoreComputeProperties};
@@ -38,6 +38,7 @@ pub fn compile_and_partially_evaluate(source: &str) -> Program {
         &compilation_context.fir_store,
         &compilation_context.compute_properties,
         &compilation_context.entry,
+        TargetCapabilityFlags::empty(),
     );
     match maybe_program {
         Ok(program) => program,
diff --git a/compiler/qsc_passes/src/baseprofck/tests.rs b/compiler/qsc_passes/src/baseprofck/tests.rs
index 9f87f983..2e6f202a 100644
--- a/compiler/qsc_passes/src/baseprofck/tests.rs
+++ b/compiler/qsc_passes/src/baseprofck/tests.rs
@@ -5,8 +5,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 
 use crate::baseprofck::check_base_profile_compliance;
 
diff --git a/compiler/qsc_passes/src/borrowck/tests.rs b/compiler/qsc_passes/src/borrowck/tests.rs
index c6c146f7..f09c5d5e 100644
--- a/compiler/qsc_passes/src/borrowck/tests.rs
+++ b/compiler/qsc_passes/src/borrowck/tests.rs
@@ -5,8 +5,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::visit::Visitor;
 
 use crate::borrowck::Checker;
diff --git a/compiler/qsc_passes/src/callable_limits/tests.rs b/compiler/qsc_passes/src/callable_limits/tests.rs
index 5bd2a0cd..7c09837d 100644
--- a/compiler/qsc_passes/src/callable_limits/tests.rs
+++ b/compiler/qsc_passes/src/callable_limits/tests.rs
@@ -5,8 +5,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::visit::Visitor;
 
 use crate::callable_limits::CallableLimits;
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
index 1f162693..69193cc9 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
@@ -15,7 +15,7 @@ use super::tests_common::{
     USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT,
 };
 use expect_test::{expect, Expect};
-use qsc_frontend::compile::TargetCapabilityFlags;
+use qsc_data_structures::target::TargetCapabilityFlags;
 
 fn check_profile(source: &str, expect: &Expect) {
     check(source, expect, TargetCapabilityFlags::Adaptive);
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs
index c38f8d39..0ee2903d 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs
@@ -17,7 +17,7 @@ use super::tests_common::{
     USE_DYNAMIC_QUBIT, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT,
 };
 use expect_test::{expect, Expect};
-use qsc_frontend::compile::TargetCapabilityFlags;
+use qsc_data_structures::target::TargetCapabilityFlags;
 
 fn check_profile(source: &str, expect: &Expect) {
     check(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
index 8410b7e4..56457eef 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
@@ -15,7 +15,7 @@ use super::tests_common::{
     USE_DYNAMIC_QUBIT, USE_DYNAMIC_RANGE, USE_DYNAMIC_STRING, USE_DYNAMIC_UDT,
 };
 use expect_test::{expect, Expect};
-use qsc_frontend::compile::TargetCapabilityFlags;
+use qsc_data_structures::target::TargetCapabilityFlags;
 
 fn check_profile(source: &str, expect: &Expect) {
     check(source, expect, TargetCapabilityFlags::empty());
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
index b0b6fd9d..48f0ac34 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
@@ -7,9 +7,9 @@ use expect_test::Expect;
 
 use crate::capabilitiesck::check_supported_capabilities;
 use qsc::{incremental::Compiler, PackageType};
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_fir::fir::{Package, PackageId, PackageStore};
-use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap};
 use qsc_lowerer::{map_hir_package_to_fir, Lowerer};
 use qsc_rca::{Analyzer, PackageComputeProperties, PackageStoreComputeProperties};
 
diff --git a/compiler/qsc_passes/src/conjugate_invert/tests.rs b/compiler/qsc_passes/src/conjugate_invert/tests.rs
index 34c8898b..b94e7eb5 100644
--- a/compiler/qsc_passes/src/conjugate_invert/tests.rs
+++ b/compiler/qsc_passes/src/conjugate_invert/tests.rs
@@ -6,8 +6,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::{validate::Validator, visit::Visitor};
 
 use crate::conjugate_invert::invert_conjugate_exprs;
diff --git a/compiler/qsc_passes/src/entry_point/tests.rs b/compiler/qsc_passes/src/entry_point/tests.rs
index 4639d431..b28a8eac 100644
--- a/compiler/qsc_passes/src/entry_point/tests.rs
+++ b/compiler/qsc_passes/src/entry_point/tests.rs
@@ -6,8 +6,8 @@
 use crate::entry_point::generate_entry_expr;
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 
 fn check(file: &str, expr: &str, expect: &Expect) {
     let sources = SourceMap::new([("test".into(), file.into())], Some(expr.into()));
diff --git a/compiler/qsc_passes/src/logic_sep/tests.rs b/compiler/qsc_passes/src/logic_sep/tests.rs
index 5a99cd52..58077d72 100644
--- a/compiler/qsc_passes/src/logic_sep/tests.rs
+++ b/compiler/qsc_passes/src/logic_sep/tests.rs
@@ -5,8 +5,10 @@
 #![allow(clippy::needless_raw_string_hashes)]
 
 use expect_test::{expect, Expect};
-use qsc_data_structures::{language_features::LanguageFeatures, span::Span};
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{
+    language_features::LanguageFeatures, span::Span, target::TargetCapabilityFlags,
+};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::{
     hir::{ExprKind, NodeId, Stmt},
     visit::{walk_stmt, Visitor},
diff --git a/compiler/qsc_passes/src/loop_unification/tests.rs b/compiler/qsc_passes/src/loop_unification/tests.rs
index 9ccf984c..f38145f8 100644
--- a/compiler/qsc_passes/src/loop_unification/tests.rs
+++ b/compiler/qsc_passes/src/loop_unification/tests.rs
@@ -5,8 +5,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::{mut_visit::MutVisitor, validate::Validator, visit::Visitor};
 
 use crate::loop_unification::LoopUni;
diff --git a/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs b/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs
index 42fa1be3..b9c4d140 100644
--- a/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs
+++ b/compiler/qsc_passes/src/replace_qubit_allocation/tests.rs
@@ -4,8 +4,8 @@
 use crate::replace_qubit_allocation::ReplaceQubitAllocation;
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::{mut_visit::MutVisitor, validate::Validator, visit::Visitor};
 
 fn check(file: &str, expect: &Expect) {
diff --git a/compiler/qsc_passes/src/spec_gen/tests.rs b/compiler/qsc_passes/src/spec_gen/tests.rs
index 3743801e..a791a8da 100644
--- a/compiler/qsc_passes/src/spec_gen/tests.rs
+++ b/compiler/qsc_passes/src/spec_gen/tests.rs
@@ -6,8 +6,8 @@
 
 use expect_test::{expect, Expect};
 use indoc::indoc;
-use qsc_data_structures::language_features::LanguageFeatures;
-use qsc_frontend::compile::{self, compile, PackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
 use qsc_hir::{validate::Validator, visit::Visitor};
 
 use crate::spec_gen::generate_specs;
diff --git a/compiler/qsc_rca/tests/test_utils.rs b/compiler/qsc_rca/tests/test_utils.rs
index a0b2db0b..b129af0e 100644
--- a/compiler/qsc_rca/tests/test_utils.rs
+++ b/compiler/qsc_rca/tests/test_utils.rs
@@ -3,9 +3,9 @@
 
 use expect_test::Expect;
 use qsc::incremental::Compiler;
-use qsc_data_structures::language_features::LanguageFeatures;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
 use qsc_fir::fir::{ItemKind, LocalItemId, Package, PackageStore, StoreItemId};
-use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap, TargetCapabilityFlags};
+use qsc_frontend::compile::{PackageStore as HirPackageStore, SourceMap};
 use qsc_lowerer::{map_hir_package_to_fir, Lowerer};
 use qsc_passes::PackageType;
 use qsc_rca::{Analyzer, ComputePropertiesLookup, PackageStoreComputeProperties};
@@ -19,12 +19,12 @@ pub struct CompilationContext {
 
 impl CompilationContext {
     #[must_use]
-    pub fn new(runtime_capabilities: TargetCapabilityFlags) -> Self {
+    pub fn new(capabilities: TargetCapabilityFlags) -> Self {
         let compiler = Compiler::new(
             true,
             SourceMap::default(),
             PackageType::Lib,
-            runtime_capabilities,
+            capabilities,
             LanguageFeatures::default(),
         )
         .expect("should be able to create a new compiler");
diff --git a/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs b/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs
index 7e16ca1f..2345e2cc 100644
--- a/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs
+++ b/compiler/qsc_rir/src/passes/remap_block_ids/tests.rs
@@ -54,8 +54,7 @@ fn remap_block_ids_no_changes() {
                 Block 2: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -79,8 +78,7 @@ fn remap_block_ids_no_changes() {
                 Block 2: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -128,8 +126,7 @@ fn remap_block_ids_out_of_order_no_branches() {
                 Block 7: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -153,8 +150,7 @@ fn remap_block_ids_out_of_order_no_branches() {
                 Block 2: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -215,8 +211,7 @@ fn remap_block_ids_out_of_order_with_one_branch() {
                 Block 3: Block:
                     Jump(1)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -243,8 +238,7 @@ fn remap_block_ids_out_of_order_with_one_branch() {
                 Block 3: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -300,8 +294,7 @@ fn remap_block_ids_simple_loop() {
                 Block 6: Block:
                     Jump(4)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -326,8 +319,7 @@ fn remap_block_ids_simple_loop() {
                 Block 2: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -370,8 +362,7 @@ fn remap_block_ids_infinite_loop() {
                 Block 4: Block:
                     Jump(0)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -394,8 +385,7 @@ fn remap_block_ids_infinite_loop() {
                 Block 1: Block:
                     Jump(0)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -459,8 +449,7 @@ fn remap_block_ids_nested_branching_loops() {
                 Block 6: Block:
                     Branch Variable(1, Boolean), 4, 2
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -485,8 +474,7 @@ fn remap_block_ids_nested_branching_loops() {
                 Block 2: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -589,8 +577,7 @@ fn remap_block_ids_ensures_acyclic_program_gets_topological_ordering() {
                 Block 8: Block:
                     Jump(7)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -627,8 +614,7 @@ fn remap_block_ids_ensures_acyclic_program_gets_topological_ordering() {
                 Block 8: Block:
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
diff --git a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs
index da9d6406..2a343ec2 100644
--- a/compiler/qsc_rir/src/passes/ssa_transform/tests.rs
+++ b/compiler/qsc_rir/src/passes/ssa_transform/tests.rs
@@ -4,6 +4,7 @@
 #![allow(clippy::too_many_lines, clippy::needless_raw_string_hashes)]
 
 use expect_test::expect;
+use qsc_data_structures::target::TargetCapabilityFlags;
 
 use crate::{
     builder::{bell_program, new_program, teleport_program},
@@ -14,19 +15,15 @@ use crate::{
     },
 };
 fn transform_program(program: &mut Program) {
-    // When this configuration is replaced by target capabilities, set them to "all" here.
-    program.config.defer_measurements = false;
-    program.config.remap_qubits_on_reuse = false;
+    program.config.capabilities = TargetCapabilityFlags::all();
     check_and_transform(program);
 }
 
 #[test]
 fn ssa_transform_leaves_program_without_store_instruction_unchanged() {
     let mut program = bell_program();
-    program.config.defer_measurements = false;
-    program.config.remap_qubits_on_reuse = false;
+    program.config.capabilities = TargetCapabilityFlags::all();
     let program_string_orignal = program.to_string();
-
     transform_program(&mut program);
 
     assert_eq!(program_string_orignal, program.to_string());
@@ -35,8 +32,8 @@ fn ssa_transform_leaves_program_without_store_instruction_unchanged() {
 #[test]
 fn ssa_transform_leaves_branching_program_without_store_instruction_unchanged() {
     let mut program = teleport_program();
+    program.config.capabilities = TargetCapabilityFlags::all();
     let program_string_orignal = program.to_string();
-
     transform_program(&mut program);
 
     assert_eq!(program_string_orignal, program.to_string());
@@ -115,8 +112,7 @@ fn ssa_transform_removes_store_in_single_block_program() {
                     Variable(2, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -145,8 +141,7 @@ fn ssa_transform_removes_store_in_single_block_program() {
                     Variable(2, Boolean) = LogicalNot Variable(0, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -269,8 +264,7 @@ fn ssa_transform_removes_multiple_stores_in_single_block_program() {
                     Variable(4, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -301,8 +295,7 @@ fn ssa_transform_removes_multiple_stores_in_single_block_program() {
                     Variable(4, Boolean) = LogicalNot Variable(3, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -434,8 +427,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks() {
                     Variable(4, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -472,8 +464,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks() {
                     Variable(4, Boolean) = LogicalNot Variable(0, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -578,8 +569,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks_without_i
                     Variable(4, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -614,8 +604,7 @@ fn ssa_transform_store_dominating_usage_propagates_to_successor_blocks_without_i
                     Variable(4, Boolean) = LogicalNot Variable(0, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -769,8 +758,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage() {
                     Variable(4, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -808,8 +796,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage() {
                     Variable(4, Boolean) = LogicalNot Variable(5, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
@@ -937,8 +924,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage_in_one_branch() {
                     Variable(4, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -975,8 +961,7 @@ fn ssa_transform_inserts_phi_for_store_not_dominating_usage_in_one_branch() {
                     Variable(4, Boolean) = LogicalNot Variable(5, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
@@ -1188,8 +1173,7 @@ fn ssa_transform_inserts_phi_for_node_with_many_predecessors() {
                     Variable(5, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -1236,8 +1220,7 @@ fn ssa_transform_inserts_phi_for_node_with_many_predecessors() {
                     Variable(5, Boolean) = LogicalNot Variable(6, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
@@ -1412,8 +1395,7 @@ fn ssa_transform_inserts_phi_for_multiple_stored_values() {
                     Variable(6, Boolean) = LogicalNot Variable(2, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -1453,8 +1435,7 @@ fn ssa_transform_inserts_phi_for_multiple_stored_values() {
                     Variable(6, Boolean) = LogicalNot Variable(8, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
@@ -1734,8 +1715,7 @@ fn ssa_transform_inserts_phi_nodes_in_successive_blocks_for_chained_branches() {
                     Variable(8, Boolean) = LogicalNot Variable(1, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -1786,8 +1766,7 @@ fn ssa_transform_inserts_phi_nodes_in_successive_blocks_for_chained_branches() {
                     Variable(8, Boolean) = LogicalNot Variable(10, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
@@ -2026,8 +2005,7 @@ fn ssa_transform_inerts_phi_nodes_for_early_return_graph_pattern() {
                     Variable(7, Boolean) = LogicalNot Variable(1, Boolean)
                     Jump(3)
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: Base
             num_qubits: 0
             num_results: 0"#]]
     .assert_eq(&program.to_string());
@@ -2075,8 +2053,7 @@ fn ssa_transform_inerts_phi_nodes_for_early_return_graph_pattern() {
                     Variable(4, Boolean) = LogicalNot Variable(9, Boolean)
                     Return
             config: Config:
-                remap_qubits_on_reuse: false
-                defer_measurements: false
+                capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset)
             num_qubits: 0
             num_results: 0"#]].assert_eq(&program.to_string());
 }
diff --git a/language_service/src/state/tests.rs b/language_service/src/state/tests.rs
index 483617f2..ca000697 100644
--- a/language_service/src/state/tests.rs
+++ b/language_service/src/state/tests.rs
@@ -644,6 +644,68 @@ fn notebook_document_errors() {
     );
 }
 
+#[test]
+fn notebook_document_lints() {
+    let errors = RefCell::new(Vec::new());
+    let mut updater = new_updater(&errors);
+
+    updater.update_notebook_document(
+        "notebook.ipynb",
+        &NotebookMetadata::default(),
+        [
+            ("cell1", 1, "operation Foo() : Unit { let x = 4;;;; }"),
+            ("cell2", 1, "operation Bar() : Unit { let y = 5 / 0; }"),
+        ]
+        .into_iter(),
+    );
+
+    expect_errors(
+        &errors,
+        &expect![[r#"
+            [
+                (
+                    "cell1",
+                    Some(
+                        1,
+                    ),
+                    [
+                        Lint(
+                            Lint {
+                                span: Span {
+                                    lo: 35,
+                                    hi: 38,
+                                },
+                                level: Warn,
+                                message: "redundant semicolons",
+                                help: "remove the redundant semicolons",
+                            },
+                        ),
+                    ],
+                ),
+                (
+                    "cell2",
+                    Some(
+                        1,
+                    ),
+                    [
+                        Lint(
+                            Lint {
+                                span: Span {
+                                    lo: 74,
+                                    hi: 79,
+                                },
+                                level: Warn,
+                                message: "attempt to divide by zero",
+                                help: "division by zero is not allowed",
+                            },
+                        ),
+                    ],
+                ),
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn notebook_update_remove_cell_clears_errors() {
     let errors = RefCell::new(Vec::new());
diff --git a/pip/tests/test_interpreter.py b/pip/tests/test_interpreter.py
index bab27dcf..52cb3351 100644
--- a/pip/tests/test_interpreter.py
+++ b/pip/tests/test_interpreter.py
@@ -315,7 +315,7 @@ def test_adaptive_errors_are_raised_from_entry_expr() -> None:
     assert "Qsc.CapabilitiesCk.UseOfDynamicDouble" in str(excinfo)
 
 
-def test_adaptive_qir_can_be_generated() -> None:
+def test_quantinuum_qir_can_be_generated() -> None:
     adaptive_input = """
         namespace Test {
             open Microsoft.Quantum.Math;
@@ -362,6 +362,87 @@ def test_adaptive_qir_can_be_generated() -> None:
 
         ; module flags
 
+        !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
+
+        !0 = !{i32 1, !"qir_major_version", i32 1}
+        !1 = !{i32 7, !"qir_minor_version", i32 0}
+        !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
+        !3 = !{i32 1, !"dynamic_result_management", i1 false}
+        !4 = !{i32 1, !"classical_ints", i1 true}
+        !5 = !{i32 1, !"qubit_resetting", i1 true}
+        !6 = !{i32 1, !"classical_floats", i1 false}
+        !7 = !{i32 1, !"backwards_branching", i1 false}
+        !8 = !{i32 1, !"classical_fixed_points", i1 false}
+        !9 = !{i32 1, !"user_functions", i1 false}
+        !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        """
+    )
+
+
+def test_base_qir_can_be_generated() -> None:
+    base_input = """
+        namespace Test {
+            open Microsoft.Quantum.Math;
+            open QIR.Intrinsic;
+            @EntryPoint()
+            operation Main() : Result {
+                use q = Qubit();
+                let pi_over_two = 4.0 / 2.0;
+                __quantum__qis__rz__body(pi_over_two, q);
+                mutable some_angle = ArcSin(0.0);
+                __quantum__qis__rz__body(some_angle, q);
+                set some_angle = ArcCos(-1.0) / PI();
+                __quantum__qis__rz__body(some_angle, q);
+                __quantum__qis__mresetz__body(q)
+            }
+        }
+        """
+    e = Interpreter(TargetProfile.Base)
+    e.interpret(base_input)
+    qir = e.qir("Test.Main()")
+    assert qir == dedent(
+        """\
+        %Result = type opaque
+        %Qubit = type opaque
+
+        define void @ENTRYPOINT__main() #0 {
+          call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+          call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+          call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
+          call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1
+          call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+          ret void
+        }
+
+        declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
+        declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
+        declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*)
+        declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
+        declare void @__quantum__qis__rx__body(double, %Qubit*)
+        declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*)
+        declare void @__quantum__qis__ry__body(double, %Qubit*)
+        declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*)
+        declare void @__quantum__qis__rz__body(double, %Qubit*)
+        declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*)
+        declare void @__quantum__qis__h__body(%Qubit*)
+        declare void @__quantum__qis__s__body(%Qubit*)
+        declare void @__quantum__qis__s__adj(%Qubit*)
+        declare void @__quantum__qis__t__body(%Qubit*)
+        declare void @__quantum__qis__t__adj(%Qubit*)
+        declare void @__quantum__qis__x__body(%Qubit*)
+        declare void @__quantum__qis__y__body(%Qubit*)
+        declare void @__quantum__qis__z__body(%Qubit*)
+        declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*)
+        declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1
+        declare void @__quantum__rt__result_record_output(%Result*, i8*)
+        declare void @__quantum__rt__array_record_output(i64, i8*)
+        declare void @__quantum__rt__tuple_record_output(i64, i8*)
+
+        attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
+        attributes #1 = { "irreversible" }
+
+        ; module flags
+
         !llvm.module.flags = !{!0, !1, !2, !3}
 
         !0 = !{i32 1, !"qir_major_version", i32 1}
diff --git a/pip/tests/test_project.py b/pip/tests/test_project.py
index 6f98db70..e248ecfe 100644
--- a/pip/tests/test_project.py
+++ b/pip/tests/test_project.py
@@ -33,7 +33,7 @@ def test_project_compile_error(qsharp) -> None:
 def test_project_bad_qsharp_json(qsharp) -> None:
     with pytest.raises(Exception) as excinfo:
         qsharp.init(project_root="/bad_qsharp_json")
-    assert str(excinfo.value).startswith("Error parsing /bad_qsharp_json/qsharp.json.")
+    assert str(excinfo.value).startswith("Error parsing /bad_qsharp_json/qsharp.json")
 
 
 def test_project_unreadable_qsharp_json(qsharp) -> None: