Skip to content

Conversation

@denialhaag
Copy link
Member

@denialhaag denialhaag commented Dec 22, 2025

Description

This PR simplifies the signature of the QCProgramBuilder::ctrl and QCOProgramBuilder::ctrl methods.

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.

@denialhaag denialhaag self-assigned this Dec 22, 2025
@denialhaag denialhaag added the MLIR Anything related to MLIR label Dec 22, 2025
@codecov
Copy link

codecov bot commented Dec 22, 2025

Codecov Report

❌ Patch coverage is 58.33333% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp 28.5% 5 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

📝 Walkthrough

Walkthrough

The PR narrows Ctrl callback signatures: QC's ctrl body now takes no arguments (std::function<void()>), and QCO's ctrl body now accepts/returns only ValueRange (std::function<ValueRange(ValueRange)>). Builders, Op builders (.td), CtrlOp implementations, and a unit test plus changelog were updated accordingly.

Changes

Cohort / File(s) Summary
QC Builder & TD
mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h, mlir/include/mlir/Dialect/QC/IR/QCOps.td
Ctrl builder signature changed: body callback from std::function<void(OpBuilder&)>std::function<void()>.
QCO Builder & TD
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h, mlir/include/mlir/Dialect/QCO/IR/QCOps.td
Ctrl builder signature changed: body callback from std::function<ValueRange(OpBuilder&, ValueRange)>std::function<ValueRange(ValueRange)>.
Builder Implementations
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp, mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Implementations adapted to new callback shapes; user callbacks are wrapped/adapted and OP_CLASS::create calls now use the concrete builder (*this) instead of an OpBuilder& parameter.
CtrlOp IR Modifiers
mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp, mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
CtrlOp::build overloads and inline-phase lambdas updated to use the parameterless or ValueRange callbacks; call sites adjusted to invoke new signatures and handle yielded ValueRange.
Tests & Changelog
mlir/unittests/pipeline/test_compiler_pipeline.cpp, CHANGELOG.md
Unit test lambda simplified (removed downcast); changelog updated with an additional PR reference.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus:
    • Consistency between .td builder signatures and public headers.
    • Correct wrapping/adaptation in builder implementations to preserve insertion point and builder semantics.
    • CtrlOp::build overload changes and correct handling/yielding of returned ValueRange.
    • Search for any remaining callers or tests expecting the old OpBuilder& parameter.

Suggested labels

enhancement

Suggested reviewers

  • burgholzer

Poem

🐇 I hopped through headers, trimmed each lambda tight,
No builder passed — the closure stays light,
Targets hum, the ops now call from here,
Small hops, clean code, a carrot cheer!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ⚠️ Warning The PR description lacks key details: no issue reference, no motivation/context, missing CHANGELOG and migration instruction updates despite claiming them as checked, and incomplete checklist with unchecked test and documentation items. Add issue reference (Fixes #...), explain motivation for the signature simplification, verify and document any CHANGELOG/migration changes, and clarify which checklist items are actually completed versus deferred.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Use custom builder classes in ctrl methods' clearly and specifically describes the main change - refactoring ctrl method signatures to use custom builder classes instead of the generic OpBuilder parameter.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch custom-ctrl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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: 1

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aba0f0e and 5eb08dd.

📒 Files selected for processing (5)
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: python/mqt/core/ir/operations.pyi:424-456
Timestamp: 2025-12-15T15:17:25.469Z
Learning: The Control class in munich-quantum-toolkit/core should be immutable (no setters for qubit and type_ properties) because Control objects are used in sets throughout the API (controls property, add_controls, remove_controls methods), and hashable+mutable objects in sets lead to incorrect lookups.
📚 Learning: 2025-12-14T17:02:02.997Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
📚 Learning: 2025-12-08T23:58:09.648Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
📚 Learning: 2025-12-08T12:44:05.883Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
📚 Learning: 2025-12-08T14:55:43.899Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.899Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
📚 Learning: 2025-12-08T23:16:20.680Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:20.680Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
📚 Learning: 2025-12-08T23:44:39.930Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Operations/StandardGates/PhaseOp.cpp:0-0
Timestamp: 2025-12-08T23:44:39.930Z
Learning: In MLIR code under any mlir/ directory, avoid using const qualifiers on core MLIR types in function parameters/signatures (e.g., Value, Type, Attribute, Operation*, Block*, Region*, etc.). This aligns with MLIR's design rationale and should be applied to C++ source files (e.g., .cpp) within mlir/; see https://mlir.llvm.org/docs/Rationale/UsageOfConst/ for details.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
📚 Learning: 2025-12-17T11:32:45.843Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:83-86
Timestamp: 2025-12-17T11:32:45.843Z
Learning: In the mlir portion of munich-quantum-toolkit/core, prefer marking free functions as static (static linkage) over placing them in anonymous namespaces, per the clang-tidy rule llvm-prefer-static-over-anonymous-namespace. Do not apply this to type/class definitions; they may continue to use anonymous namespaces. This guideline should be checked across C++ source files under mlir/ (e.g., any free function in LayeredUnit.cpp) to ensure free functions have static linkage, while types/classes can remain in anonymous namespaces.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
🧬 Code graph analysis (3)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (5)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (2)
  • checkFinalized (454-458)
  • checkFinalized (454-454)
mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp (2)
  • checkFinalized (570-575)
  • checkFinalized (570-570)
mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp (1)
  • builder (608-608)
mlir/lib/Conversion/QCToQIR/QCToQIR.cpp (1)
  • builder (1056-1056)
mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp (1)
  • builder (619-619)
mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h (1)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (1)
  • QCProgramBuilder (34-38)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)
  • QCOProgramBuilder (36-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04-arm
  • GitHub Check: 🐉 Test / 🐧 Coverage
  • GitHub Check: 🐉 Test / 🍎 macos-15-intel
  • GitHub Check: 🐉 Test / 🏁 windows-2025
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04
  • GitHub Check: 🐉 Test / 🏁 windows-11-arm
  • GitHub Check: 🐉 Test / 🍎 macos-15
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: Run
🔇 Additional comments (4)
mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h (1)

844-845: QCProgramBuilder::ctrl API tightening looks good

Exposing QCProgramBuilder& in the callback matches actual usage and keeps existing OpBuilder&/auto& lambdas source‑compatible via inheritance. No issues from the declaration side.

mlir/unittests/pipeline/test_compiler_pipeline.cpp (1)

1584-1588: MCXNested test adapts cleanly to new ctrl signature

Using mlir::qc::QCProgramBuilder& in the lambda matches the updated API and removes the downcast; this is a straightforward and correct adjustment.

mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)

1002-1004: QCOProgramBuilder::ctrl signature change is consistent and useful

Passing QCOProgramBuilder& into the body matches the value‑semantics builder API and aligns with the implementation; existing OpBuilder&-typed lambdas remain usable via inheritance.

mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (1)

420-425: QCProgramBuilder::ctrl wrapper is correct; optional refinement should be disregarded

The current implementation correctly adapts the user-facing callback signature (std::function<void(QCProgramBuilder&)>) to CtrlOp::create's internal callback signature (OpBuilder&). Using *this to pass the QCProgramBuilder to the user's body function is the correct pattern—it provides the high-level builder interface that users expect.

The suggested explicit cast refinement should not be applied. The OpBuilder passed to the lambda is a lower-level MLIR construct, not a QCProgramBuilder, and attempting to cast it would be incorrect. The current implementation is clean and correct as-is.

Copy link
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: 1

♻️ Duplicate comments (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)

579-588: Consider using the region's OpBuilder parameter instead of capturing this.

The wrapper lambda captures this and passes *this to the body while ignoring the OpBuilder& parameter provided by CtrlOp::create. This approach differs from the pattern used in the macros throughout this file (e.g., lines 213-215, 251-253), where the provided OpBuilder& b parameter is used directly to create operations in the control region.

As noted in the past review comment, this pattern obscures which builder instance is being used. Consider casting the region's OpBuilder to the concrete type:

 std::pair<ValueRange, ValueRange> QCOProgramBuilder::ctrl(
     ValueRange controls, ValueRange targets,
     const std::function<ValueRange(QCOProgramBuilder&, ValueRange)>& body) {
   checkFinalized();
 
-  auto ctrlOp =
-      CtrlOp::create(*this, loc, controls, targets,
-                     [this, &body](OpBuilder& /*builder*/, ValueRange targets) {
-                       return body(*this, targets);
-                     });
+  auto ctrlOp = CtrlOp::create(
+      *this, loc, controls, targets,
+      [&body](OpBuilder& builder, ValueRange targets) {
+        auto& qcoBuilder = static_cast<QCOProgramBuilder&>(builder);
+        return body(qcoBuilder, targets);
+      });

This ensures the body operates on the region's builder with the correct insertion point, aligning with the macro patterns and making the builder identity explicit.

Based on learnings from past reviews and consistency with macro patterns in this file.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5eb08dd and 7467e8f.

📒 Files selected for processing (2)
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.
📚 Learning: 2025-12-14T17:02:02.997Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T23:58:09.648Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T12:44:05.883Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T14:55:43.899Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.899Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T23:16:20.680Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:20.680Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-02T07:37:46.860Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp:144-151
Timestamp: 2025-12-02T07:37:46.860Z
Learning: In MLIR transformation code (mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp and similar), when inserting operations before a target operation, prefer `rewriter.setInsertionPoint(op)` over `rewriter.setInsertionPointAfter(op->getPrevNode())`. The former is cleaner, avoids null pointer edge cases (when op is first in block), and is semantically clearer.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-12-08T23:41:55.972Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp:96-117
Timestamp: 2025-12-08T23:41:55.972Z
Learning: In the QIR (Quantum Intermediate Representation) Builder (mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp), the `ptrCache` is intentionally shared between qubit and result pointer creation (in `staticQubit()` and `measure()` methods) because QIR uses opaque pointers and `inttoptr` conversions for both qubits and results. For any given index N, the LLVM IR pointer representation is identical whether it represents a qubit or a result, so the pointer only needs to be created once and can be safely reused across both contexts.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-12-08T23:44:39.930Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Operations/StandardGates/PhaseOp.cpp:0-0
Timestamp: 2025-12-08T23:44:39.930Z
Learning: In MLIR code under any mlir/ directory, avoid using const qualifiers on core MLIR types in function parameters/signatures (e.g., Value, Type, Attribute, Operation*, Block*, Region*, etc.). This aligns with MLIR's design rationale and should be applied to C++ source files (e.g., .cpp) within mlir/; see https://mlir.llvm.org/docs/Rationale/UsageOfConst/ for details.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-17T11:32:45.843Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:83-86
Timestamp: 2025-12-17T11:32:45.843Z
Learning: In the mlir portion of munich-quantum-toolkit/core, prefer marking free functions as static (static linkage) over placing them in anonymous namespaces, per the clang-tidy rule llvm-prefer-static-over-anonymous-namespace. Do not apply this to type/class definitions; they may continue to use anonymous namespaces. This guideline should be checked across C++ source files under mlir/ (e.g., any free function in LayeredUnit.cpp) to ensure free functions have static linkage, while types/classes can remain in anonymous namespaces.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04
  • GitHub Check: 🐉 Test / 🐧 Coverage
  • GitHub Check: 🐉 Test / 🏁 windows-11-arm
  • GitHub Check: 🐉 Test / 🍎 macos-15-intel
  • GitHub Check: 🐉 Test / 🍎 macos-15
  • GitHub Check: 🐉 Test / 🏁 windows-2025
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: Run

@burgholzer
Copy link
Member

@denialhaag I tried something a little different in bf63548 and I think I really like it. Let me know what you think.

Copy link
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: 2

Caution

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

⚠️ Outside diff range comments (1)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (1)

1097-1103: Clarify ValueRange lifetime expectations for CtrlOp bodyBuilder

The new builder overload
OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, "const std::function<ValueRange(ValueRange)>&":$bodyBuilder)>
nicely reflects value semantics (targets in, targets out). However, because ValueRange is a view, callers must ensure the returned range refers to stable values (e.g., operation results or existing ranges), not to temporaries such as stack-local SmallVector<Value> or initializer lists whose storage goes out of scope when the lambda returns. It would be helpful to document this contract at the QCO builder level or wrap this in a small helper that builds the YieldOp from a concrete container to make misuse harder.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7467e8f and bf63548.

📒 Files selected for processing (9)
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.
📚 Learning: 2025-12-08T23:58:09.648Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:09.648Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-14T17:02:02.997Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp:187-210
Timestamp: 2025-12-14T17:02:02.997Z
Learning: In the Flux dialect builder (mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp), the coding style relies on implicit conversion from Value to ValueRange in ctrl() calls (e.g., `ctrl(control, {}, ...)` instead of `ctrl(ValueRange{control}, ValueRange{}, ...)`). This pattern is used consistently throughout all macro-generated methods and should be maintained for consistency.

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T12:44:05.883Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.883Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T14:55:43.899Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.899Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T23:16:20.680Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:20.680Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
  • mlir/include/mlir/Dialect/QC/IR/QCOps.td
  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-17T17:44:21.624Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/include/mlir/Dialect/QCO/IR/QCOOps.td:259-259
Timestamp: 2025-12-17T17:44:21.624Z
Learning: In mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (QCO dialect), ensure GPhaseOp declares MemoryEffects<[MemWrite]> rather than Pure. This op has no results and is a zero-target operation, so using Pure would lead to its removal by dead-code elimination. By marking it with MemWrite, review ensures DCE preserves the operation because it has a meaningful effect on the global quantum state. This guidance applies when reviewing or updating QCO ops in this file (and broadly to similar zero-target ops with side effects in QCO).

Applied to files:

  • mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 Learning: 2025-12-17T17:44:31.349Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/include/mlir/Dialect/QCO/IR/QCOOps.td:259-259
Timestamp: 2025-12-17T17:44:31.349Z
Learning: In the QCO dialect (mlir/include/mlir/Dialect/QCO/IR/QCOOps.td), GPhaseOp intentionally uses `MemoryEffects<[MemWrite]>` instead of `Pure` to prevent the remove-dead-values pass from eliminating it. Since GPhaseOp is a zero-target operation with no result values, it would otherwise be removed by DCE, even though it has a meaningful effect on the global quantum state.

Applied to files:

  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-12-02T07:37:46.860Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp:144-151
Timestamp: 2025-12-02T07:37:46.860Z
Learning: In MLIR transformation code (mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp and similar), when inserting operations before a target operation, prefer `rewriter.setInsertionPoint(op)` over `rewriter.setInsertionPointAfter(op->getPrevNode())`. The former is cleaner, avoids null pointer edge cases (when op is first in block), and is semantically clearer.

Applied to files:

  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T23:44:39.930Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Operations/StandardGates/PhaseOp.cpp:0-0
Timestamp: 2025-12-08T23:44:39.930Z
Learning: In MLIR code under any mlir/ directory, avoid using const qualifiers on core MLIR types in function parameters/signatures (e.g., Value, Type, Attribute, Operation*, Block*, Region*, etc.). This aligns with MLIR's design rationale and should be applied to C++ source files (e.g., .cpp) within mlir/; see https://mlir.llvm.org/docs/Rationale/UsageOfConst/ for details.

Applied to files:

  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-17T11:32:45.843Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:83-86
Timestamp: 2025-12-17T11:32:45.843Z
Learning: In the mlir portion of munich-quantum-toolkit/core, prefer marking free functions as static (static linkage) over placing them in anonymous namespaces, per the clang-tidy rule llvm-prefer-static-over-anonymous-namespace. Do not apply this to type/class definitions; they may continue to use anonymous namespaces. This guideline should be checked across C++ source files under mlir/ (e.g., any free function in LayeredUnit.cpp) to ensure free functions have static linkage, while types/classes can remain in anonymous namespaces.

Applied to files:

  • mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
  • mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp
📚 Learning: 2025-12-08T23:41:55.972Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp:96-117
Timestamp: 2025-12-08T23:41:55.972Z
Learning: In the QIR (Quantum Intermediate Representation) Builder (mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp), the `ptrCache` is intentionally shared between qubit and result pointer creation (in `staticQubit()` and `measure()` methods) because QIR uses opaque pointers and `inttoptr` conversions for both qubits and results. For any given index N, the LLVM IR pointer representation is identical whether it represents a qubit or a result, so the pointer only needs to be created once and can be safely reused across both contexts.

Applied to files:

  • mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-12-05T17:45:37.602Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1360
File: .github/workflows/reusable-mlir-tests.yml:40-43
Timestamp: 2025-12-05T17:45:37.602Z
Learning: In the munich-quantum-toolkit/core repository, patch releases of LLVM dependencies don't require documentation updates, changelog entries, or additional tests beyond what's validated by passing CI checks.

Applied to files:

  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • mlir/unittests/pipeline/test_compiler_pipeline.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04
  • GitHub Check: 🐉 Test / 🍎 macos-15-intel
  • GitHub Check: 🐉 Test / 🐧 Coverage
  • GitHub Check: 🐉 Test / 🐧 ubuntu-24.04-arm
  • GitHub Check: 🐉 Test / 🏁 windows-2025
  • GitHub Check: 🐉 Test / 🍎 macos-15
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🐉 Test / 🏁 windows-11-arm
  • GitHub Check: Run
🔇 Additional comments (12)
mlir/unittests/pipeline/test_compiler_pipeline.cpp (1)

1584-1587: MCXNested test correctly migrated to new ctrl() API

Using b.ctrl(reg[0], [&] { b.cx(reg[1], reg[2]); }); matches the new QCProgramBuilder::ctrl(ValueRange, const std::function<void()>&) signature, relies on the intended implicit ValueValueRange conversion, and keeps the test’s semantics unchanged while simplifying the lambda body.

mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h (1)

827-846: QCProgramBuilder::ctrl signature and example align with QC CtrlOp semantics

The updated ctrl(ValueRange controls, const std::function<void()>& body) plus the example builder.ctrl(q0, [&] { builder.x(q1); }); are consistent with QC’s reference semantics and the underlying qc::CtrlOp builder that now also expects a parameterless body callback. The use of implicit ValueValueRange conversion for single-qubit controls stays in line with the established builder style.

mlir/include/mlir/Dialect/QC/IR/QCOps.td (1)

967-970: QC CtrlOp builder updated consistently to no-arg body callback

Switching the second CtrlOp builder to
OpBuilder<(ins "ValueRange":$controls, "const std::function<void()>&":$bodyBuilder)>
is consistent with the new QCProgramBuilder API and the implementation in CtrlOp::build, where the callback is invoked with the insertion point already set to the body block and qc::YieldOp created afterwards. No functional issues spotted.

mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (1)

97-103: CtrlOp bodyBuilder migration preserves rewrite and build semantics

Adapting both CtrlInlineGPhase and CtrlOp::build to use a std::function<void()> body callback looks correct: the build overload still guards and resets the insertion point, emits the body operations inside the newly created block, and appends a qc::YieldOp afterwards. The updated CtrlInlineGPhase now passes a matching parameterless lambda that creates the POp with the existing PatternRewriter, so the canonicalization behavior for controlled GPhaseOp remains intact.

Also applies to: 162-172

mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)

109-114: LGTM! CtrlInlineGPhase correctly adapted to new signature.

The pattern correctly uses the new lambda signature, taking ValueRange targets and returning the operation result. The implicit conversion from Value to ValueRange is standard practice in MLIR.


261-272: LGTM! Build signature correctly updated.

The CtrlOp::build method signature correctly reflects the new callback type std::function<ValueRange(ValueRange)>, removing the OpBuilder& parameter. The implementation properly invokes the body builder with targets and yields the results.

mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (2)

169-169: LGTM! Macro-generated methods consistently updated.

All controlled operation helpers correctly use the new no-parameter lambda signature [&] { OP_CLASS::create(*this, loc, ...); }. This aligns with QC dialect's reference semantics where the body doesn't return values.

The usage of *this is correct because CtrlOp::build sets the insertion point on the builder before invoking the body lambda.

Based on learnings, QC dialect uses reference semantics and doesn't return values from ctrl operations.

Also applies to: 194-194, 231-232, 265-267, 302-304, 328-329, 359-361, 395-397


418-423: LGTM! ctrl implementation correctly simplified.

The ctrl method signature correctly updated to std::function<void()>, removing the OpBuilder& parameter. The implementation directly passes the body to CtrlOp::create, which is appropriate for the QC dialect's reference semantics.

mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (4)

212-227: LGTM! Zero-target operations correctly adapted.

The macro-generated methods for zero-target operations (like gphase) correctly use the new signature:

  • Lambda takes ValueRange /*targets*/ (empty for zero-target ops)
  • Returns empty ValueRange{}
  • Uses *this to create operations

The pattern is consistent and correct for QCO's value semantics.

Based on learnings, QCO dialect uses value semantics unlike QC's reference semantics.


246-262: LGTM! One-target operations correctly adapted.

All one-target operation helpers (with varying numbers of parameters) consistently use the new lambda signature:

  • Lambda receives ValueRange targets and accesses via targets[0]
  • Returns op->getResults() as ValueRange
  • Properly destructures results with structured bindings

The implementation correctly maintains QCO's value semantics where operations consume input qubits and produce new output values.

Also applies to: 293-310, 336-356, 383-404


426-445: LGTM! Two-target operations correctly adapted.

All two-target operation helpers consistently use the new lambda signature:

  • Lambda receives ValueRange targets and accesses via targets[0] and targets[1]
  • Returns op->getResults() as ValueRange
  • Properly destructures results into control and target pairs

The pattern is uniform across all parameter variations (zero, one, two parameters).

Also applies to: 471-491, 520-541


565-583: LGTM! ctrl implementation correctly updated.

The ctrl method correctly:

  1. Accepts the new signature std::function<ValueRange(ValueRange)>&
  2. Passes the body directly to CtrlOp::create
  3. Updates qubit tracking for both controls and targets
  4. Returns the updated control and target qubits

The implementation properly maintains QCO's value semantics and linear type tracking.

@denialhaag
Copy link
Member Author

denialhaag commented Dec 22, 2025

@li-mingbao and @Ectras, as this is also relevant for your PRs: We have decided to simplify the signatures of the ctrl builder methods. This should also work for your scfFor, scfWhile, etc., and inv builder methods, respectively.

@denialhaag denialhaag enabled auto-merge (squash) December 22, 2025 11:51
@denialhaag denialhaag merged commit 4029d09 into main Dec 22, 2025
29 of 30 checks passed
@denialhaag denialhaag deleted the custom-ctrl branch December 22, 2025 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants