Skip to content

✨ Add SCF conversions between QC and QCO dialects#1638

Merged
burgholzer merged 82 commits into
munich-quantum-toolkit:mainfrom
li-mingbao:scf-conversions
May 11, 2026
Merged

✨ Add SCF conversions between QC and QCO dialects#1638
burgholzer merged 82 commits into
munich-quantum-toolkit:mainfrom
li-mingbao:scf-conversions

Conversation

@li-mingbao
Copy link
Copy Markdown
Contributor

Description

This PR adds support for the conversion of the scf operations scf.while, scf.for, scf.if between the QC and the QCO dialect. This allows the conversion of programs with nonlinear controlflow.

This PR is the revamped version of the #1396 PR to integrate the newest changes.
Changes compared to the PR and new features:

  • Conversions support registers and qubits inside the scf operations.
  • The func conversion is not added since this also requires a qtensor.cast operation first.
  • scf.if is converted into qco.if
  • qco.if also supports tensors of qubits now as input types.
  • qco.yield also supports tensors of qubits now as input types.

Checklist:

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

burgholzer added 5 commits May 6, 2026 00:41
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
@mergify mergify Bot added the conflict label May 6, 2026
@mergify mergify Bot removed the conflict label May 6, 2026
Copy link
Copy Markdown
Member

@burgholzer burgholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @li-mingbao for all the work on this PR! 🙏🏼
I finally found some time to get to this.
I pushed a couple of commits that cleaned up a couple of smaller details and improved small bits and pieces.
I also have a handful of smaller comments left, which you will find inline. These should be quick to address and I believe this should be ready to go in afterwards.

I have the feeling that the conversions between QC and QCO have accumulated quite some tracking code and data structures that feel a bit much. And I am wondering whether some of the respective code could be simplified. However, these are all concerns for a potential follow-up PR.
Let's get this one in sooner rather than later 😄

Comment thread mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
Comment thread mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h Outdated
Comment thread mlir/lib/Dialect/QCO/IR/QCOOps.cpp Outdated
Comment thread mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Comment thread mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
@burgholzer burgholzer dismissed denialhaag’s stale review May 6, 2026 00:07

Dismissing Daniel's review as stale for now.

@burgholzer burgholzer mentioned this pull request May 6, 2026
11 tasks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp (1)

246-281: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reinstate branch-yield checks in IfOp::verify().

verify() now only ties block arguments and op results back to the input list. It no longer checks that thenYield() and elseYield() have the same arity and types as the qco.if results, so malformed IR can pass verification and later break the constant-folding path on Lines 133-141 or the QCO→QC lowering, which assumes the carried values line up.

Suggested fix
 LogicalResult IfOp::verify() {
   const auto& inputQubits = getQubits();
   const auto numInputQubits = inputQubits.size();
   const auto& outputQubits = getResults();
   const auto numOutputQubits = outputQubits.size();
@@
   if (numInputQubits != numOutputQubits) {
     return emitOpError("Operation must return the same number of qubits as the "
                        "number of input qubits.");
   }
+  if (thenYield().getOperands().size() != numOutputQubits ||
+      elseYield().getOperands().size() != numOutputQubits) {
+    return emitOpError("Both regions must yield the same number of values as "
+                       "the operation returns.");
+  }
   for (auto [inputQubitType, outputQubitType] :
        llvm::zip_equal(inputQubits.getTypes(), outputQubits.getTypes())) {
     if (inputQubitType != outputQubitType) {
       return emitOpError("Operation must return the same qubit types as its "
                          "input qubit types.");
     }
   }
+  for (auto [yieldType, resultType] :
+       llvm::zip_equal(thenYield().getOperands().getTypes(),
+                       outputQubits.getTypes())) {
+    if (yieldType != resultType) {
+      return emitOpError(
+          "Then-region yield types must match the operation result types.");
+    }
+  }
+  for (auto [yieldType, resultType] :
+       llvm::zip_equal(elseYield().getOperands().getTypes(),
+                       outputQubits.getTypes())) {
+    if (yieldType != resultType) {
+      return emitOpError(
+          "Else-region yield types must match the operation result types.");
+    }
+  }
   SmallPtrSet<Value, 4> uniqueQubitsIn;
   for (auto qubit : inputQubits) {
     if (!uniqueQubitsIn.insert(qubit).second) {
       return emitOpError("Input qubits must be unique.");
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp` around lines 246 - 281, IfOp::verify()
removed checks that thenYield() and elseYield() match the op results and input
qubits; restore verification so that thenYield() and elseYield() both exist,
have the same arity as getResults() (and as
getQubits()/thenBlock()->getNumArguments()), and that each corresponding yielded
type equals the corresponding getResults() type (and input qubit type). Also
ensure thenYield() and elseYield() have identical types to each other (same
arity and per-index type equality) and emitOpError() with a clear message when
any of these conditions fail so malformed IR cannot pass verification.
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)

245-259: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't hard-assert on unsupported qubit-memref boundaries.

This helper still aborts when a qubit-bearing memref has no seeded tensor mapping. That is reachable for function/block arguments or call edges carrying memref<...x!qc.qubit>: func-boundary conversion is still out of scope here, so ConvertMemRefLoadOp can hit lookupMappedTensor() before any mapping exists and crash instead of producing a clean conversion failure.

Based on learnings, tensor containers are supported for scf.if/scf.while in this PR, but func operations remain restricted to scalar qubit types only.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Conversion/QCToQCO/QCToQCO.cpp` around lines 245 - 259, The helper
lookupMappedTensor currently aborts via assert when findRegionLocalMap returns
no mapping, which crashes during func/block boundary cases (e.g.,
memref<...x!qc.qubit>) before conversion can fail cleanly; change
lookupMappedTensor to not hard-assert—have it return a null/empty Value (e.g.,
Value()) when tensorMap/tensorValue are missing so callers can detect absence,
then update callers such as ConvertMemRefLoadOp (and any users relying on
lookupMappedTensor/currentModifierFrame) to check the returned Value and emit a
proper conversion failure or diagnostic instead of relying on an assertion; keep
use of findRegionLocalMap and currentModifierFrame but remove the assert and
propagate the empty result.

1116-1127: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rescan cloned modifier regions to populate SCF-use maps.

collectQubitValuesInsideSCFOps() is called once at the start and keys regionQubitMap / regionRegisterMap by Operation*, but cloneRegionBefore() creates new operation instances for every nested scf.* op inside the cloned region. Those cloned SCF ops therefore have no entries in the pre-computed maps and appear empty to the legality predicate, remaining legal and unconverted. Nested SCF operations carrying qubits or registers inside qc.ctrl / qc.inv bodies will not be rewritten.

♻️ Localized fix

Add a rescan after cloning in both ConvertQCCtrlOp::matchAndRewrite (line 1118) and ConvertQCInvOp::matchAndRewrite (line 1168):

     auto& dstRegion = qcoOp.getRegion();
     rewriter.cloneRegionBefore(op.getRegion(), dstRegion, dstRegion.end());
+    collectQubitValuesInsideSCFOps(qcoOp.getOperation(), &state);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Conversion/QCToQCO/QCToQCO.cpp` around lines 1116 - 1127, The
precomputed maps (regionQubitMap/regionRegisterMap populated by
collectQubitValuesInsideSCFOps) reference original SCF ops, but
rewriter.cloneRegionBefore(...) creates new SCF op instances so the cloned SCF
bodies are missing from those maps; after cloning the region in
ConvertQCCtrlOp::matchAndRewrite and ConvertQCInvOp::matchAndRewrite
(immediately after rewriter.cloneRegionBefore(op.getRegion(), dstRegion,
dstRegion.end())), re-run collectQubitValuesInsideSCFOps on the cloned region to
repopulate regionQubitMap/regionRegisterMap for the new operations so nested
scf.* ops carrying qubits/registers are recognized by the legality predicate
before proceeding (i.e., call the same scan helper with the newly-cloned
ops/region).
♻️ Duplicate comments (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)

967-984: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Drive scf.while bookkeeping from the emitted scf.condition.

createBody() threads the beforeBody return value into the after region, but the real loop-carried state is whatever scfCondition() put on the terminator. If those ever differ, the builder updates validQubits/validTensors for the wrong values and diverges from the IR.

🔧 Proposed fix
   auto createBody =
       [&](Block* block, function_ref<SmallVector<Value>(ValueRange)> body,
           ValueRange innerInitArgs, bool createYield) -> SmallVector<Value> {
     auto blockArgs = block->getArguments();
     // Update the qubit values to the block args
     updateQubitValueTracking(innerInitArgs, blockArgs);
     // Construct the body
     const auto& results = body(blockArgs);

     if (results.size() != innerInitArgs.size()) {
       llvm::reportFatalUsageError(
           "scf.while body must return exactly one value per iter arg");
     }
     if (createYield) {
       scf::YieldOp::create(*this, results);
-    }
-    return results;
+      return SmallVector<Value>(results.begin(), results.end());
+    }
+
+    auto condOp = dyn_cast<scf::ConditionOp>(block->getTerminator());
+    if (!condOp) {
+      llvm::reportFatalUsageError(
+          "scf.while beforeBody must terminate with scf.condition");
+    }
+    if (condOp.getArgs().size() != innerInitArgs.size()) {
+      llvm::reportFatalUsageError(
+          "scf.condition must yield exactly one value per iter arg");
+    }
+    return SmallVector<Value>(condOp.getArgs().begin(), condOp.getArgs().end());
   };

Also applies to: 1048-1064

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp` around lines 967 - 984,
The loop body builder createBody currently uses the body() return (the
`results`/`beforeBody`) to update qubit/tensor tracking and to emit the
scf::YieldOp, but the true loop-carried values come from the terminator produced
by scfCondition(); update createBody to accept and use the values returned by
scfCondition (the condition terminator results) when calling
updateQubitValueTracking and when creating the scf::YieldOp (i.e., thread the
scfCondition terminator values into the `after` region instead of using the
body() results), so validQubits/validTensors are updated from the scfCondition
outputs rather than from `results`/`beforeBody`. Ensure you preserve the size
checks (results.size() vs innerInitArgs.size()) but base bookkeeping and the
yielded values on the scfCondition outputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Conversion/QCOToQC/QCOToQC.cpp`:
- Around line 851-852: In ConvertQCOIfOp and ConvertQCOSCFConditionOp replace
uses of op.getCondition() with the remapped operand adaptor.getCondition() when
constructing the new SCF ops (e.g. the scf::IfOp::create call in ConvertQCOIfOp
and the scf::condition creation in ConvertQCOSCFConditionOp), and
restore/un-comment the adaptor parameter in ConvertQCOSCFConditionOp so you can
call adaptor.getCondition(); this ensures the recreated scf ops use the adapted
(converted) SSA value produced by the OpAdaptor rather than the original
op.getCondition().

In `@mlir/unittests/programs/qco_programs.cpp`:
- Around line 2077-2088: Extend the ifOneQubitOneTensor test to include a
non-trivial else branch for b.qcoIf that returns the same mixed qubit+tensor
result types as the then branch (use qtensorExtract/qtensorInsert on the tensor
operand in the else path and perform an operation on the qubit operand in that
branch), and add assertions after lowering to verify the physical qubit identity
is preserved across then/else (i.e., the qubit Value produced by both branches
refers to the same underlying resource); update the usage sites in
ifOneQubitOneTensor (references: qcoIf, qtensorExtract, qtensorInsert,
allocQubitRegister, measure) so both branches return matching SmallVector shapes
and include checks that compare the qubit Values for identity after the
transformation.

---

Outside diff comments:
In `@mlir/lib/Conversion/QCToQCO/QCToQCO.cpp`:
- Around line 245-259: The helper lookupMappedTensor currently aborts via assert
when findRegionLocalMap returns no mapping, which crashes during func/block
boundary cases (e.g., memref<...x!qc.qubit>) before conversion can fail cleanly;
change lookupMappedTensor to not hard-assert—have it return a null/empty Value
(e.g., Value()) when tensorMap/tensorValue are missing so callers can detect
absence, then update callers such as ConvertMemRefLoadOp (and any users relying
on lookupMappedTensor/currentModifierFrame) to check the returned Value and emit
a proper conversion failure or diagnostic instead of relying on an assertion;
keep use of findRegionLocalMap and currentModifierFrame but remove the assert
and propagate the empty result.
- Around line 1116-1127: The precomputed maps (regionQubitMap/regionRegisterMap
populated by collectQubitValuesInsideSCFOps) reference original SCF ops, but
rewriter.cloneRegionBefore(...) creates new SCF op instances so the cloned SCF
bodies are missing from those maps; after cloning the region in
ConvertQCCtrlOp::matchAndRewrite and ConvertQCInvOp::matchAndRewrite
(immediately after rewriter.cloneRegionBefore(op.getRegion(), dstRegion,
dstRegion.end())), re-run collectQubitValuesInsideSCFOps on the cloned region to
repopulate regionQubitMap/regionRegisterMap for the new operations so nested
scf.* ops carrying qubits/registers are recognized by the legality predicate
before proceeding (i.e., call the same scan helper with the newly-cloned
ops/region).

In `@mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp`:
- Around line 246-281: IfOp::verify() removed checks that thenYield() and
elseYield() match the op results and input qubits; restore verification so that
thenYield() and elseYield() both exist, have the same arity as getResults() (and
as getQubits()/thenBlock()->getNumArguments()), and that each corresponding
yielded type equals the corresponding getResults() type (and input qubit type).
Also ensure thenYield() and elseYield() have identical types to each other (same
arity and per-index type equality) and emitOpError() with a clear message when
any of these conditions fail so malformed IR cannot pass verification.

---

Duplicate comments:
In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp`:
- Around line 967-984: The loop body builder createBody currently uses the
body() return (the `results`/`beforeBody`) to update qubit/tensor tracking and
to emit the scf::YieldOp, but the true loop-carried values come from the
terminator produced by scfCondition(); update createBody to accept and use the
values returned by scfCondition (the condition terminator results) when calling
updateQubitValueTracking and when creating the scf::YieldOp (i.e., thread the
scfCondition terminator values into the `after` region instead of using the
body() results), so validQubits/validTensors are updated from the scfCondition
outputs rather than from `results`/`beforeBody`. Ensure you preserve the size
checks (results.size() vs innerInitArgs.size()) but base bookkeeping and the
yielded values on the scfCondition outputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 385c3c14-c66e-4b6c-bc5a-6d696f32d1bc

📥 Commits

Reviewing files that changed from the base of the PR and between b0b6bb1 and aa77eed.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp
  • mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
  • mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/IR/QCOOps.cpp
  • mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp
  • mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp
  • mlir/unittests/programs/qco_programs.cpp
  • mlir/unittests/programs/qco_programs.h

Comment thread mlir/lib/Conversion/QCOToQC/QCOToQC.cpp Outdated
Comment thread mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
Comment thread mlir/unittests/programs/qco_programs.cpp
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (1)

569-571: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject scfCondition() outside scfWhile's before-region.

This helper can still emit scf.condition in the main block, in an after region, or inside scf.if, which lets the public builder API create malformed IR before scfWhile() ever validates it. The parent/region check discussed earlier still needs to live here, and the same guard should be mirrored in QCOProgramBuilder::scfCondition.

Suggested fix
 QCProgramBuilder& QCProgramBuilder::scfCondition(Value condition) {
   checkFinalized();
+  auto* block = getInsertionBlock();
+  auto* parentOp = block ? block->getParentOp() : nullptr;
+  if (!parentOp || !isa<scf::WhileOp>(parentOp) ||
+      &cast<scf::WhileOp>(parentOp).getBefore().front() != block) {
+    llvm::reportFatalUsageError(
+        "scfCondition can only be emitted in the before region of scfWhile");
+  }
   scf::ConditionOp::create(*this, condition, ValueRange{});
   return *this;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp` around lines 569 - 571,
Ensure QCProgramBuilder::scfCondition validates the insertion region is the
before-region of an scf::WhileOp before emitting scf::ConditionOp: obtain the
current insertion block (or parent op), find the nearest scf::WhileOp (e.g.,
getParentOfType<scf::WhileOp>()), check that the insertion block/region equals
the while op's before-region (or whileOp.getBefore().front()), and bail/emit an
error/assert if not; only then call scf::ConditionOp::create(...) — apply the
same guard and logic to QCOProgramBuilder::scfCondition so both builders prevent
emitting scf.condition outside the while before-region.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp`:
- Around line 145-156: The guard currently keys loadedQubits by the SSA Value
(index), which misses semantically-equal constant indices; change the logic to
canonicalize the slot index to a semantic integer before checking/inserting:
resolve the index Value to an integer slot id (e.g., detect arith.constant Index
ops and extract the IntegerAttr/getZExtValue, else fall back to a stable
non-constant key) and use that semantic id when checking
loadedQubits[curr][memref].contains(...) and when inserting into
loadedQubits[region][memref].insert(...); update the checks around regionStack,
the duplicate-load check, and the code that creates the load
(memref::LoadOp::create) so all use the same resolved slot id (also ensure
allocQubitRegister seeding uses the same canonicalization).

In `@mlir/unittests/programs/qco_programs.cpp`:
- Around line 2231-2237: Replace the non-obvious SmallVector construction that
takes a ValueRange in the second lambda by explicitly converting the ValueRange
to a vector; specifically, in the lambda capturing ValueRange iterArgs (the one
that currently returns SmallVector<Value>{iterArgs}), return
llvm::to_vector(iterArgs) (or construct SmallVector from iterArgs.begin()/end())
so the conversion from ValueRange to a SmallVector<Value> is explicit and uses
LLVM helpers.

---

Duplicate comments:
In `@mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp`:
- Around line 569-571: Ensure QCProgramBuilder::scfCondition validates the
insertion region is the before-region of an scf::WhileOp before emitting
scf::ConditionOp: obtain the current insertion block (or parent op), find the
nearest scf::WhileOp (e.g., getParentOfType<scf::WhileOp>()), check that the
insertion block/region equals the while op's before-region (or
whileOp.getBefore().front()), and bail/emit an error/assert if not; only then
call scf::ConditionOp::create(...) — apply the same guard and logic to
QCOProgramBuilder::scfCondition so both builders prevent emitting scf.condition
outside the while before-region.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8e27c37f-9dd3-482a-8ae1-987e07fc6f3f

📥 Commits

Reviewing files that changed from the base of the PR and between aa77eed and c45b2fd.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/programs/qc_programs.cpp
  • mlir/unittests/programs/qco_programs.cpp

Comment thread mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
Comment thread mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Comment thread mlir/unittests/programs/qco_programs.cpp Outdated
@li-mingbao li-mingbao requested a review from burgholzer May 11, 2026 15:05
Copy link
Copy Markdown
Member

@burgholzer burgholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the hard work here @li-mingbao 🙏🏼
This LGTM now 🎉

I'll get this in once #1694 is in. 🚀
Edit: Screw that. CodeRabbit requested further changes there. Getting this in first.

@burgholzer burgholzer merged commit 398ccd2 into munich-quantum-toolkit:main May 11, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants