-
-
Notifications
You must be signed in to change notification settings - Fork 48
🐛 Fix one-time-use of variables for the ctrl modifier
#1436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughIntroduce TargetAliasing for CtrlOp/YieldOp: move targets into a Changes
Sequence Diagram(s)sequenceDiagram
participant QCO as QCO CtrlOp (source)
participant Converter as QCO→QC Converter
participant QC as QC CtrlOp (dest)
QCO->>Converter: provide CtrlOp with aliased region (entry block args)
Converter->>QC: qc::CtrlOp::create(rewriter, loc, controls)
Converter->>QC: clone QCO region into qcOp.getRegion()
Converter->>Converter: verify adaptor.targets.size == cloned entry-block-args.size
Converter->>QC: replace cloned entry-block-arg uses with adaptor.getTargetsIn()[i]
Converter->>QC: erase cloned entry-block-arguments
Converter-->>QCO: replace original op with adaptor.getOperands()
sequenceDiagram
participant QC as QC CtrlOp (source)
participant Converter as QC→QCO Converter
participant QCO as QCO CtrlOp (dest)
QC->>Converter: provide CtrlOp with SSA target operands
Converter->>QCO: qco::CtrlOp::create(rewriter, loc, controls, targets)
Converter->>Converter: clone QC body into QCO region
Converter->>QCO: create per-target block-arguments in destination region
Converter->>Converter: store block-arg aliases in state.targetsIn
Converter-->>QC: emit QCO CtrlOp referencing region block-args
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:
- Around line 784-802: The code replaces block-argument uses with
adaptor.getTargetsIn()[i] without checking that adaptor.getTargetsIn() has at
least numArgs elements, risking out-of-bounds access; update the lambda in
rewriter.modifyOpInPlace (the block that manipulates entryBlock and numArgs) to
first get the target container (adaptor.getTargetsIn()), verify target.size() >=
numArgs (or adjust numArgs to min(numArgs, target.size())), and if sizes
mismatch either assert/log and bail out or safely iterate only up to the smaller
size, then perform replaceAllUsesWith using that verified range before calling
entryBlock.eraseArguments(0, originalNumArgs).
In @mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:
- Around line 1132-1146: The loop uses an unsigned long literal for the index
(auto i = 0UL) which can be unclear across platforms; change the loop index to
use size_t (e.g., for (size_t i = 0; i < qcoOp.getNumTargets(); ++i)) so it
matches the return type of getNumTargets()/container sizes and avoids type-width
mismatches when creating block arguments in the rewriter.modifyOpInPlace block
that adds arguments to entryBlock and populates qcoTargetAliases stored in
state.targetsIn.
In @mlir/lib/Dialect/QCO/IR/QCOOps.cpp:
- Around line 77-93: The function printTargetAliasing currently assumes
region.front().getArguments() and targets_in have equal length; add a size
verification before the loop to avoid out-of-bounds: fetch blockArgs
(Block::getArguments()) and if blockArgs.size() != targets_in.size() either emit
a diagnostic via printer or assert/fail early, or iterate only to
std::min(blockArgs.size(), targets_in.size()) and log a warning; update
references in the function (printTargetAliasing, blockArgs, targets_in)
accordingly so all accesses to blockArgs[i] and targets_in[i] are guarded by the
size check.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cpp
🧰 Additional context used
🧠 Learnings (17)
📓 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/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: 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.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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/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.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
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.
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 Learning: 2025-10-09T04:30:29.071Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h:72-140
Timestamp: 2025-10-09T04:30:29.071Z
Learning: In the CrawlScheduler (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h), the persistent `layoutCopy` is updated across all lookahead layers via `remapQubitValue` calls. After each two-qubit gate is scheduled in a layer, its input qubits are remapped to output qubits, ensuring the next layer's crawl starts from those outputs. This progressive remapping through SSA values prevents the same operation from being scheduled in multiple lookahead layers.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 Learning: 2025-10-07T15:30:42.946Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h:219-231
Timestamp: 2025-10-07T15:30:42.946Z
Learning: In the Layout class for MLIR quantum routing (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h), the swap method intentionally does NOT swap the hw fields in QubitInfo. This is correct because SSA values represent quantum states at fixed hardware locations, and only their program index associations change during a SWAP gate. The hw field indicates where an SSA value physically resides and remains constant.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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-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
🧬 Code graph analysis (2)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (2)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (1)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[style] 30-30: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 77-77: The function 'printTargetAliasing' is never used.
(unusedFunction)
🔇 Additional comments (2)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (1)
1071-1079: LGTM! Clean assembly format for target aliasing.The introduction of the
targetskeyword followed bycustom<TargetAliasing>provides a clear and explicit syntax for the target aliasing construct. This aligns well with the goal of fixing one-time-use violations by representing targets as block arguments.mlir/lib/Dialect/QCO/IR/QCOOps.cpp (1)
28-75: LGTM! Correctly implements target aliasing parsing.The
parseTargetAliasingfunction properly parses the(%new = %old, ...)syntax, creates block arguments of QubitType, and passes them to the region parser. The implementation follows standard MLIR parsing patterns.Note: Static analysis tools flag this as unused, but this is a false positive—the function is invoked via the
custom<TargetAliasing>directive in the operation's assembly format.
Signed-off-by: Damian Rovara <[email protected]>
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:
- Around line 783-801: The replacement loop over entryBlock arguments assumes
adaptor.getTargetsIn() has at least numArgs elements; add a defensive bounds
check before the for-loop in the rewriter.modifyOpInPlace block: compute auto
targets = adaptor.getTargetsIn(); verify targets.size() == numArgs (or at least
>= numArgs) and handle mismatch (emit an mlir::emitError on qcOp / return
failure / assert) so you never index out of range when doing
entryBlock.getArgument(i).replaceAllUsesWith(targets[i]); keep the rest of the
eraseArguments logic unchanged.
In @mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:
- Around line 1132-1146: Replace the redundant call to qcoOp.getNumTargets() in
the loop with the already-computed numTargets variable: in the
rewriter.modifyOpInPlace lambda that iterates over targets (where originalTarget
= qcoOp.getInputTarget(i) and arg = entryBlock.addArgument(...)), use numTargets
for the loop bound and keep pushing args into qcoTargetAliases before calling
state.targetsIn.try_emplace. This reuses the existing numTargets and removes the
extra getNumTargets() call.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
🧰 Additional context used
🧠 Learnings (19)
📓 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/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: 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.
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.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
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/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.
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2025-10-07T15:30:42.946Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h:219-231
Timestamp: 2025-10-07T15:30:42.946Z
Learning: In the Layout class for MLIR quantum routing (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h), the swap method intentionally does NOT swap the hw fields in QubitInfo. This is correct because SSA values represent quantum states at fixed hardware locations, and only their program index associations change during a SWAP gate. The hw field indicates where an SSA value physically resides and remains constant.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-10-09T04:30:29.071Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h:72-140
Timestamp: 2025-10-09T04:30:29.071Z
Learning: In the CrawlScheduler (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h), the persistent `layoutCopy` is updated across all lookahead layers via `remapQubitValue` calls. After each two-qubit gate is scheduled in a layer, its input qubits are remapped to output qubits, ensuring the next layer's crawl starts from those outputs. This progressive remapping through SSA values prevents the same operation from being scheduled in multiple lookahead layers.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.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/lib/Conversion/QCToQCO/QCToQCO.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.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
📚 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-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
🧬 Code graph analysis (2)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (16)
op(294-303)op(295-296)op(324-330)op(325-326)op(351-357)op(352-353)op(389-407)op(390-391)op(434-447)op(435-436)op(734-747)op(735-736)op(770-807)op(771-772)op(825-830)op(826-827)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)
⏰ 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). (11)
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
🔇 Additional comments (2)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (1)
1071-1079: LGTM! Target aliasing correctly introduces block arguments.The assemblyFormat now uses
custom<TargetAliasing>($region, $targets_in)to handle target qubits via block arguments in the region body, preventing the double-use issue. This aligns with the PR objectives and follows established MLIR patterns (similar to scf dialect).mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
777-777: Critical bug: Undefined variable reference on line 780.Line 780 references
qcoOp.getRegion()but the variable was renamed toqcOpon line 777. This causes a compilation error. Change line 780 to useqcOpinstead:auto& dstRegion = qcOp.getRegion();⛔ Skipped due to 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: li-mingbao Repo: munich-quantum-toolkit/core PR: 1396 File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085 Timestamp: 2026-01-07T12:29:16.380Z Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.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.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: 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.
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:
- Around line 783-801: Add a defensive bounds check inside the
rewriter.modifyOpInPlace lambda (around qcOp / entryBlock / numArgs) to verify
that numArgs matches the number of elements in adaptor.getTargetsIn() (or at
least that adaptor.getTargetsIn().size() >= numArgs) before the
replaceAllUsesWith loop; if the check fails, assert or emit a clear error
message referencing qcOp and the region so mismatches in QCO control
construction are caught early during development.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
🧰 Additional context used
🧠 Learnings (14)
📓 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/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: 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.
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.
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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.cpp
📚 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/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.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/lib/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.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/Conversion/QCOToQC/QCOToQC.cpp
🧬 Code graph analysis (1)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
🔇 Additional comments (1)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
777-780: LGTM! Variable name accurately reflects the operation type.The rename from
qcoOptoqcOpis correct and consistent with other patterns in this file (e.g., line 397).
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Dialect/QCO/IR/QCOOps.cpp:
- Around line 88-105: The printer function printTargetAliasing assumes
blockArgs.size() equals targets_in.size(), which can lead to OOB access; add a
defensive check at the start of printTargetAliasing that verifies
region.front().getNumArguments() == targets_in.size() (or blockArgs.size() ==
targets_in.size()) and fail fast if not (e.g., use
assert/llvm_unreachable/emitError as appropriate for this codebase), so the
function never indexes blockArgs[i] out of range; keep the rest of the printing
logic unchanged.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.cpp
🧰 Additional context used
🧠 Learnings (20)
📓 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/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: 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.
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.
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: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.td
📚 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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/QCOOps.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
📚 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-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
🧬 Code graph analysis (1)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (2)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 12-12: Include file
(missingInclude)
[information] 13-13: Include file
(missingInclude)
[information] 14-14: Include file
(missingInclude)
[information] 16-16: Include file
(missingInclude)
[style] 35-35: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 88-88: The function 'printTargetAliasing' is never used.
(unusedFunction)
⏰ 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, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
🔇 Additional comments (4)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
777-807: LGTM! Correct handling of target aliasing in QCO→QC conversion.The conversion correctly:
- Creates the
qc.ctrlwith controls only (QC uses reference semantics, no targets needed in the op itself)- Clones the QCO region which has block arguments for targets
- Replaces uses of those block arguments with the adaptor-provided QC target references
- Erases the block arguments since QC doesn't need them
- Replaces the QCO op results with the adaptor operands (which are the same QC references for pass-through)
This properly handles the transition from QCO's block-argument-based target aliasing to QC's reference semantics.
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (1)
33-86: LGTM! Custom parser for target aliasing is well-structured.The parser correctly:
- Handles the empty case
()viaparseOptionalRParen- Parses each
%new = %oldpair, collecting operands and creating typed block arguments- Passes the block arguments to
parseRegionto establish them as entry block argumentsNote: The Cppcheck "unusedFunction" warnings are false positives—these functions are invoked by tablegen-generated code via the
custom<TargetAliasing>directive in the assembly format.mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (2)
1071-1079: LGTM! Assembly format correctly implements target aliasing.The new format:
`(` $controls_in `)` `targets` custom<TargetAliasing>($region, $targets_in) ...This elegantly solves the one-use violation by:
- Consuming target operands at the operation level (
$targets_in)- Introducing block-argument aliases in the region via
custom<TargetAliasing>- Ensuring the original SSA values are used only once (as operands), while the region uses distinct block arguments
The syntax
targets(%alias = %original)is clear and consistent with MLIR patterns likescf.forblock arguments.
1028-1031: Examples clearly demonstrate the new target aliasing syntax.The updated examples show the complete flow:
%a0 = %q0, %a1 = %q1creates aliases that become block arguments- The region body uses the aliases (
%a0,%a1) rather than the original operandsqco.yieldreturns the transformed aliasesThis documentation effectively communicates the new semantics.
Also applies to: 1060-1064
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Dialect/QCO/IR/QCOOps.cpp:
- Around line 33-109: The two free functions parseTargetAliasing and
printTargetAliasing are redundantly placed inside an anonymous namespace while
already declared static; remove the anonymous namespace wrapper (the opening
"namespace {" and the matching closing "}" / comment) and keep both functions
with their existing static linkage at global scope so they remain file-local;
ensure you only delete the namespace lines and preserve the function definitions
and their signatures exactly.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mlir/lib/Dialect/QCO/IR/QCOOps.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/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: 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.
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.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
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.
📚 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/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.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/IR/QCOOps.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/IR/QCOOps.cpp
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[style] 35-35: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 91-91: The function 'printTargetAliasing' is never used.
(unusedFunction)
⏰ 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). (7)
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
🔇 Additional comments (3)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (3)
13-16: LGTM! Necessary includes for the new parsing/printing utilities.The added includes directly support the parseTargetAliasing and printTargetAliasing functions introduced below.
34-89: LGTM! Parsing logic is correct and well-structured.The
parseTargetAliasingfunction correctly implements the target aliasing syntax parsing with appropriate error handling at each step. The hard-codedQubitTypeat line 70 is intentional and well-documented—targets inqco.ctrlare always qubits, avoiding double-binding in the assembly format while keeping the parser simple.Note: Static analysis reports this function as unused, but this is a false positive. The function is called from tablegen-generated code included at line 148.
91-108: LGTM! Printing logic correctly emits the target aliasing syntax.The
printTargetAliasingfunction correctly prints the aliasing mappings followed by the region body. The loop at line 97 assumes the entry block has exactlytargets_in.size()arguments, which is guaranteed by the operation's structure and MLIR's validation. TheprintRegioncall withprintEntryBlockArgs=falseis correct since the block arguments are already printed via the aliasing syntax.Note: Static analysis reports this function as unused, but this is a false positive. The function is called from tablegen-generated code included at line 148.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @mlir/lib/Dialect/QCO/IR/QCOOps.cpp:
- Around line 13-20: Switch the MLIR system-style includes to project-header
style: replace the angle-bracket includes for mlir headers (e.g.,
mlir/IR/Block.h, mlir/IR/OpImplementation.h, mlir/IR/Operation.h,
mlir/IR/Region.h, mlir/IR/ValueRange.h, mlir/Support/LLVM.h,
mlir/Support/LogicalResult.h) to use quoted includes so they match the existing
"mlir/..." dialect headers and avoid IWYU/tooling misclassification; update the
include lines in QCOOps.cpp accordingly, keeping the exact header names but
using double quotes.
- Around line 94-111: The printer function printTargetAliasing assumes
entryBlock.getNumArguments() == targetsIn.size() and can index blockArgs[i]
out-of-bounds; add a precondition check at the top of printTargetAliasing that
validates the sizes (e.g., compare region.front().getNumArguments() with
targetsIn.size()), and if they differ either assert (using assert or
llvm::LLVM_DEBUG or similar) and/or call llvm::report_fatal_error with a clear
message before proceeding; ensure subsequent loop uses the validated size so
blockArgs[i] and targetsIn[i] cannot OOB.
- Around line 36-113: The parseTargetAliasing and printTargetAliasing functions
are redundantly placed inside an anonymous namespace despite already being
declared static; remove the anonymous namespace wrapper and keep each helper as
a file-level static free function (static ParseResult parseTargetAliasing(...),
static void printTargetAliasing(...)) so linkage remains internal but the extra
namespace is eliminated; ensure you retain all parameter names and bodies
unchanged and update any comments or trailing namespace/end comments
accordingly.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
🧰 Additional context used
🧠 Learnings (17)
📓 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/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: 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.
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.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
📚 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/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.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/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cpp
🧬 Code graph analysis (1)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (1)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[style] 38-38: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 94-94: The function 'printTargetAliasing' is never used.
(unusedFunction)
🔇 Additional comments (1)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (1)
36-113: Functions are properly referenced via MLIR'scustom<TargetAliasing>directive.The static analysis report is incorrect.
parseTargetAliasingandprintTargetAliasingare actively used through thecustom<TargetAliasing>($region, $targets_in)directive in QCOOps.td:1074's assemblyFormat block. When MLIR processescustom<TargetAliasing>, it generates code that invokes these functions automatically—this is the standard pattern for custom parsing and printing in MLIR ops. The functions are not textually referenced in the .cpp file because they are implicitly called by the generated code.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp:
- Around line 286-289: The verifier currently only checks block argument count
against getNumTargets(); enhance it to also verify each block argument's type
matches the corresponding target's type by iterating the block.getArguments()
and for each index i comparing arg.getType() with the target type (e.g., from
getTargets()[i] or getTarget(i)). If a mismatch is found, emit an error via
emitOpError with a clear message describing the argument index and the expected
vs actual types. Make this check immediately after the existing count check in
the verifier so manual IR or future build changes cannot create type mismatches.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cpp
🧰 Additional context used
🧠 Learnings (27)
📓 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/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: 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.
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/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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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.
📚 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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 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/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-04T22:30:02.780Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: test/qir/runner/test_qir_runner.cpp:34-34
Timestamp: 2026-01-04T22:30:02.780Z
Learning: In test/qir/runner/test_qir_runner.cpp, when constructing commands for std::system with std::filesystem::path objects, the paths should not be manually quoted. The std::filesystem::path streaming operator already provides appropriate quoting by default, and adding extra quotes around the executable path causes std::system to handle the command incorrectly on Windows.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-12-07T09:10:31.836Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.836Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-08T22:56:03.140Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1435
File: docs/mlir/QC.md:1-11
Timestamp: 2026-01-08T22:56:03.140Z
Learning: In the munich-quantum-toolkit/core repository, MLIR dialect documentation files (e.g., MLIRQCDialect.md, MLIRQCInterfaces.md) are automatically generated during the documentation build via the ReadTheDocs `pre_build` step and do not need to be committed to the repository.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-07T15:30:42.946Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h:219-231
Timestamp: 2025-10-07T15:30:42.946Z
Learning: In the Layout class for MLIR quantum routing (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h), the swap method intentionally does NOT swap the hw fields in QubitInfo. This is correct because SSA values represent quantum states at fixed hardware locations, and only their program index associations change during a SWAP gate. The hw field indicates where an SSA value physically resides and remains constant.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-10-09T04:30:29.071Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1237
File: mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h:72-140
Timestamp: 2025-10-09T04:30:29.071Z
Learning: In the CrawlScheduler (mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Scheduler.h), the persistent `layoutCopy` is updated across all lookahead layers via `remapQubitValue` calls. After each two-qubit gate is scheduled in a layer, its input qubits are remapped to output qubits, ensuring the next layer's crawl starts from those outputs. This progressive remapping through SSA values prevents the same operation from being scheduled in multiple lookahead layers.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
🧬 Code graph analysis (2)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (5)
mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (2)
getNumTargets(117-117)getNumTargets(117-117)mlir/include/mlir/Dialect/QC/IR/QCDialect.h (1)
getNumTargets(70-70)mlir/include/mlir/Dialect/QCO/IR/QCODialect.h (1)
getNumTargets(70-70)mlir/lib/Dialect/QC/IR/Operations/StandardGates/BarrierOp.cpp (2)
getNumTargets(23-23)getNumTargets(23-23)mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp (2)
getNumTargets(81-81)getNumTargets(81-81)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (16)
op(294-303)op(295-296)op(324-330)op(325-326)op(351-357)op(352-353)op(389-407)op(390-391)op(434-447)op(435-436)op(734-747)op(735-736)op(770-807)op(771-772)op(825-830)op(826-827)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[style] 37-37: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 93-93: The function 'printTargetAliasing' is never used.
(unusedFunction)
⏰ 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). (11)
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Coverage / 📈 Coverage
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
🔇 Additional comments (5)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
253-255: LGTM: Block arguments correctly established for target aliasing.The block arguments are created with the correct type derived from each target, establishing the aliasing mechanism. This enables the region body to reference targets via block arguments rather than consuming the operand values directly, restoring one-use semantics.
269-277: LGTM: Lambda-based builder correctly uses block arguments.The implementation correctly:
- Creates block arguments for each target (lines 269-271)
- Collects them into
blockArgs(line 272)- Passes
blockArgsto the user-providedbodyBuilder(line 277) instead of the originaltargetsThis ensures operations constructed inside the region consume the aliased block arguments, maintaining single-use semantics for the original target operands.
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (1)
1132-1147: Block argument aliasing correctly implemented for QC→QCO conversion.The conversion establishes target aliasing by:
- Cloning the QC region into the QCO operation (line 1130)
- Adding block arguments for each target to the entry block (lines 1138-1145)
- Storing these block arguments in
state.targetsIn[state.inCtrlOp](line 1146)When operations inside the cloned region are subsequently converted (as part of the partial conversion pass), they will read from
state.targetsInto obtain the block arguments instead of the original target operands. This correctly implements the aliasing semantics.The ordering is correct:
state.inCtrlOpis incremented at line 1126, so operations inside the region will see the incremented value and read from the correct entry instate.targetsIn.mlir/lib/Dialect/QCO/IR/QCOOps.cpp (2)
36-91: LGTM: Target aliasing parser correctly implements the aliasing syntax.The parser correctly handles the
(%new = %old, ...)syntax:
- Parses each alias pair (new block argument = old operand)
- Creates block arguments with
qco::QubitType(line 72)- Collects old operands for later resolution
- Passes block arguments to
parser.parseRegionso they become the entry block argumentsThe decision to hard-code
qco::QubitType(line 72) is justified by the comment—targets inqco.ctrlare always qubits, and this avoids complexity in the assembly format.
93-110: LGTM: Target aliasing printer correctly emits the aliasing syntax.The printer correctly emits the
(%blockArg = %target, ...)mapping by:
- Iterating over block arguments and corresponding target operands
- Printing each pair with the
%new = %oldsyntax- Printing the region with
printEntryBlockArgs=falseto avoid duplicate argument printingThe implementation correctly complements the parser and maintains round-trip consistency.
… compatible with the new block argumets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (5)
37-61: Fix MergeNestedCtrl: cloningbodyCtrlOp.getBodyUnitary()now risks cross-region SSA uses.
After target-aliasing, the inner unitary’s operands are (or depend on) block args from the inner ctrl region; cloning it into the new ctrl region without anIRMappingcan create invalid IR.Proposed fix (clone inner unitary with IRMapping from inner target block args → new target block args)
@@ // Merge controls SmallVector<Value> newControls(op.getControlsIn()); for (const auto control : bodyCtrlOp.getControlsIn()) { newControls.push_back(control); } - rewriter.replaceOpWithNewOp<CtrlOp>(op, newControls, op.getTargetsIn(), - bodyCtrlOp.getBodyUnitary()); + rewriter.replaceOpWithNewOp<CtrlOp>( + op, newControls, op.getTargetsIn(), + [&](ValueRange newTargetAliases) -> ValueRange { + IRMapping mapping; + auto &innerBody = *bodyCtrlOp.getBody(); + for (auto [oldArg, newArg] : + llvm::zip(innerBody.getArguments(), newTargetAliases)) { + mapping.map(oldArg, newArg); + } + Operation *cloned = + rewriter.clone(*bodyCtrlOp.getBodyUnitary().getOperation(), + mapping); + return cloned->getResults(); + }); return success(); } };
70-83: Fix RemoveTrivialCtrl: cloning body unitary now escapes region block args.
rewriter.clone(*op.getBodyUnitary().getOperation())will copy operands that are ctrl body block arguments (target aliases), which are not valid in the parent block.Proposed fix (map ctrl body target block args → CtrlOp target operands)
@@ const OpBuilder::InsertionGuard guard(rewriter); rewriter.setInsertionPoint(op); - auto* clonedBody = rewriter.clone(*op.getBodyUnitary().getOperation()); + IRMapping mapping; + auto &body = *op.getBody(); + for (auto [arg, target] : + llvm::zip(body.getArguments(), op.getTargetsIn())) { + mapping.map(arg, target); + } + auto *clonedBody = + rewriter.clone(*op.getBodyUnitary().getOperation(), mapping); rewriter.replaceOp(op, clonedBody->getResults()); return success(); } };
164-184: CtrlOp::getInputQubit/getOutputQubit should not return region values (block args/results).
With target-aliasing, delegating target qubits togetBodyUnitary().get{Input,Output}Qubit(...)will return values scoped to the ctrl body region, which can break callers expecting operands/results ofqco.ctrlitself.Proposed fix (use CtrlOp operands/results for targets)
@@ Value CtrlOp::getInputQubit(const size_t i) { const auto numControls = getNumControls(); if (i < numControls) { return getControlsIn()[i]; } if (numControls <= i && i < getNumQubits()) { - return getBodyUnitary().getInputQubit(i - numControls); + return getTargetsIn()[i - numControls]; } llvm::reportFatalUsageError("Invalid qubit index"); } @@ Value CtrlOp::getOutputQubit(const size_t i) { const auto numControls = getNumControls(); if (i < numControls) { return getControlsOut()[i]; } if (numControls <= i && i < getNumQubits()) { - return getBodyUnitary().getOutputQubit(i - numControls); + return getTargetsOut()[i - numControls]; } llvm::reportFatalUsageError("Invalid qubit index"); }
248-267: Builder change is directionally right; prefer IRMapping over post-clone RAUW.
Adding target block arguments and using them in the region matches the intended scf-style aliasing. However,clone + replaceUsesOfWith(targets[i], blockArgs[i])only works when the providedbodyUnitaryactually uses thetargetsvalues; several internal patterns historically pass region-contained ops (see MergeNestedCtrl / RemoveTrivialCtrl), so cloning should generally be done with an explicit mapping.Also applies to: 269-284
286-338: verify(): uniqueness checks no longer detect duplicate target/control operands after aliasing.
You’re insertingbodyUnitary.getInputQubit(i)intouniqueQubitsIn, but those are now block args (distinct SSA values), so duplicate operands ingetTargetsIn()(or overlap between controls and targets) won’t be caught.Proposed fix (uniqueness on CtrlOp operands; optional “don’t use original targets in region” check)
@@ SmallPtrSet<Value, 4> uniqueQubitsIn; for (const auto& control : getControlsIn()) { if (!uniqueQubitsIn.insert(control).second) { return emitOpError("duplicate control qubit found"); } } - auto bodyUnitary = getBodyUnitary(); - const auto numQubits = bodyUnitary.getNumQubits(); - for (size_t i = 0; i < numQubits; i++) { - if (!uniqueQubitsIn.insert(bodyUnitary.getInputQubit(i)).second) { - return emitOpError("duplicate qubit found"); - } - } + for (const auto &target : getTargetsIn()) { + if (!uniqueQubitsIn.insert(target).second) { + return emitOpError("duplicate target qubit found"); + } + } + + // Optional but recommended: enforce aliasing (region should use block args, + // not the original target operands). + auto bodyUnitary = getBodyUnitary(); + for (Value target : getTargetsIn()) { + for (Value operand : bodyUnitary.getOperation()->getOperands()) { + if (operand == target) { + return emitOpError("body must use target block-argument aliases, not " + "the original target operand(s)"); + } + } + } @@ - SmallPtrSet<Value, 4> uniqueQubitsOut; - for (const auto& control : getControlsOut()) { - if (!uniqueQubitsOut.insert(control).second) { - return emitOpError("duplicate control qubit found"); - } - } - for (size_t i = 0; i < numQubits; i++) { - if (!uniqueQubitsOut.insert(bodyUnitary.getOutputQubit(i)).second) { - return emitOpError("duplicate qubit found"); - } - } + // (Optional) If you still want an output-side uniqueness check, it should be + // based on CtrlOp results (controlsOut/targetsOut), not region values.
🤖 Fix all issues with AI agents
In @mlir/unittests/CMakeLists.txt:
- Around line 9-10: The add_subdirectory(Dialect) line should be guarded by the
same CMake option that enables the unit tests to avoid missing-target errors;
wrap the add_subdirectory(Dialect) call in an if(...) / endif() check using the
test-building variable your project uses (e.g., if(BUILD_TESTING) or
if(MLIR_BUILD_TESTS) or the existing option that controls the dialect test
targets) so the Dialect subdir is only added when those tests/GTest are enabled.
- Around line 12-15: Wrap the add_dependencies call for mqt-core-mlir-unittests
with conditional TARGET checks so CMake only adds dependencies if those test
targets exist; specifically, check TARGET mqt-core-mlir-compiler-pipeline-test
and TARGET mqt-core-mlir-dialect-qco-ir-modifiers-test (or separate checks per
dependency) before calling add_dependencies(mqt-core-mlir-unittests ...),
ensuring configure won't fail when optional test targets are absent.
In @mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp:
- Around line 117-130: Add explicit checks that the region block arguments
implement target aliasing: verify block.getNumArguments() == 1 and that
block.getArgument(0).getType() equals qType, then cast block.front() to XOp
(innerXOp) and assert innerXOp.getOperand(0) == block.getArgument(0). Place
these assertions after the existing region/block checks (after
EXPECT_FALSE(region.empty()) and before/after the XOp/YieldOp checks) to
directly validate the target aliasing behavior of ctrlOp.
- Around line 46-90: The test currently checks ctrlOp structure but omits
assertions that the region's entry block contains target block arguments and
that the SWAPOp uses those arguments (aliasing); modify the LambdaBuilder test
for CtrlOp to assert block.getNumArguments() == 2, that
block.getArgument(0).getType() and block.getArgument(1).getType() equal qType,
and that the SWAPOp inside the block (cast to SWAPOp) has operands equal to
block.getArgument(0) and block.getArgument(1) (use
ctrlOp.getRegion()/region.front() to locate the block and block.front() to get
the SWAPOp) so the test verifies target aliasing via block arguments.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (23)
📓 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/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: 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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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.
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.
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.
📚 Learning: 2026-01-08T22:56:03.140Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1435
File: docs/mlir/QC.md:1-11
Timestamp: 2026-01-08T22:56:03.140Z
Learning: In the munich-quantum-toolkit/core repository, MLIR dialect documentation files (e.g., MLIRQCDialect.md, MLIRQCInterfaces.md) are automatically generated during the documentation build via the ReadTheDocs `pre_build` step and do not need to be committed to the repository.
Applied to files:
mlir/unittests/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
📚 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/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
📚 Learning: 2025-11-03T23:09:26.881Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/CMakeLists.txt:9-21
Timestamp: 2025-11-03T23:09:26.881Z
Learning: The CMake functions `generate_device_defs_executable` and `generate_prefixed_qdmi_headers` used in QDMI device test CMakeLists.txt files are provided by the external QDMI library (fetched via FetchContent from https://github.com/Munich-Quantum-Software-Stack/qdmi.git), specifically in the cmake/PrefixHandling.cmake module of the QDMI repository.
Applied to files:
mlir/unittests/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
📚 Learning: 2025-12-14T16:51:52.504Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: .readthedocs.yaml:13-18
Timestamp: 2025-12-14T16:51:52.504Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, LLVM and MLIR toolchains are required for the documentation build because `uv run` includes a full build of the package, which compiles C++/MLIR extensions using scikit-build-core.
Applied to files:
mlir/unittests/CMakeLists.txtmlir/unittests/Dialect/QCO/CMakeLists.txt
📚 Learning: 2025-12-28T17:14:53.890Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: src/qdmi/na/CMakeLists.txt:31-38
Timestamp: 2025-12-28T17:14:53.890Z
Learning: In the munich-quantum-toolkit/core repository, the NA device generator target (mqt-core-qdmi-na-device-gen) is intentionally propagated to MQT_CORE_TARGETS via list(APPEND) because it's publicly linked to the NA device library (the NA device uses a public function from the generator). The SC device generator is not propagated because it has no such dependency with the SC device library.
Applied to files:
mlir/unittests/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
📚 Learning: 2025-10-10T08:09:54.528Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1246
File: bindings/CMakeLists.txt:0-0
Timestamp: 2025-10-10T08:09:54.528Z
Learning: In the Munich Quantum Toolkit (MQT) Core project, scikit-build-core is configured with `wheel.install-dir = "mqt/core"` in pyproject.toml, which automatically prefixes all CMake `DESTINATION` paths with `mqt/core/` during wheel installation. Therefore, CMake install destinations are relative to the `mqt/core` package namespace, not `site-packages`.
Applied to files:
mlir/unittests/CMakeLists.txt
📚 Learning: 2025-10-09T13:14:10.178Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:219-221
Timestamp: 2025-10-09T13:14:10.178Z
Learning: The MQT Core project (munich-quantum-toolkit/core repository) uses the C++20 standard, not C++17. C++20 features such as abbreviated function templates (e.g., `const auto&` parameters) are supported and valid in this codebase.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt
📚 Learning: 2026-01-04T23:54:33.540Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: src/qdmi/na/CMakeLists.txt:179-184
Timestamp: 2026-01-04T23:54:33.540Z
Learning: In the munich-quantum-toolkit/core repository, MSVC link-time code generation (LTCG) must be disabled for the NA device dynamic library target (mqt-core-qdmi-na-device-dyn) on Windows to avoid link errors and missing symbols. This issue affects both release-only builds and debug/release mixed builds, not just debug/release mixing scenarios.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt
📚 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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-05T15:57:39.701Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 3
File: lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt:22-25
Timestamp: 2025-12-05T15:57:39.701Z
Learning: The munich-quantum-toolkit projects (core and core-plugins-catalyst) use `file(GLOB_RECURSE ...)` patterns in CMakeLists.txt files to collect header files, following an established convention in the parent repository for consistency across the codebase.
Applied to files:
mlir/unittests/Dialect/QCO/IR/CMakeLists.txtmlir/unittests/Dialect/CMakeLists.txt
🧬 Code graph analysis (1)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (3)
mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (8)
op(36-53)op(36-37)op(61-74)op(61-62)op(83-106)op(83-84)getNumTargets(117-117)getNumTargets(117-117)mlir/include/mlir/Dialect/QC/IR/QCDialect.h (1)
getNumTargets(70-70)mlir/include/mlir/Dialect/QCO/IR/QCODialect.h (1)
getNumTargets(70-70)
🪛 ast-grep (0.40.4)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
[warning] 295-295: The getTargetsIn function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsIn()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers
(null-library-function-cpp)
🪛 Cppcheck (2.19.0)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
⏰ 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). (14)
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Coverage / 📈 Coverage
- GitHub Check: 🇨 Lint / 🚨 Lint
🔇 Additional comments (7)
mlir/unittests/Dialect/CMakeLists.txt (1)
1-9: Clean wiring for dialect unit tests.
add_subdirectory(QCO)is consistent with the rest of the unit test tree setup.mlir/unittests/Dialect/QCO/IR/CMakeLists.txt (1)
1-9: LGTM: IR → Modifiers unit test wiring is minimal and clear.mlir/unittests/Dialect/QCO/CMakeLists.txt (1)
1-9: LGTM: QCO → IR unit test wiring is minimal and clear.mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (1)
291-300: Nice: block argument count/type validation matches the new semantics.
This is an important verifier addition for keeping the aliasing scheme well-formed.mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt (1)
1-21: LGTM!The CMake configuration correctly sets up the unit test target with all necessary dependencies for testing the QCO CtrlOp functionality.
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (2)
22-44: LGTM!The test fixture setup follows standard MLIR testing patterns with proper context initialization, dialect loading, and module/function scaffolding.
107-115: QCO does not enforce linear type semantics; the pattern is valid.The QCO dialect does not include
LinearTypetraits on eitherQubitTypeorCtrlOp, and the type system uses standard value semantics without single-use enforcement like MQTOpt or Flux. The test correctly shows thatxOpis cloned intoctrlOp's region with block arguments—the originalxOptemplate remains in the main block, and this dual-use pattern is intentional and permitted in QCO. The test correctly expectsmodule->verify()to succeed.
Signed-off-by: burgholzer <[email protected]>
…e code Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
988-1005: Ctrl callback return type change is correct; consider namespacingSmallVectorexplicitly for API consistency.
TheSmallVector<Value>-returning body avoids returningValueRangebacked by temporaries (good), and the updated assembly example matches the new target-aliasing syntax. Consider making the signaturellvm::SmallVector<Value>(ormlir::SmallVector<Value>) to match the rest of this header’s style (llvm::SmallVectoris used elsewhere here).mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
400-531: Two-target wrappers: structured binding from temporary is OK; consider avoidingconst auto&for clarity.
const auto& [q0, q1] = OP_NAME(...)binds to a temporarystd::pairand lifetime-extends it, so it’s correct; usingauto [q0, q1]would be simpler/readable.
555-584: Add a fast-fail check for ctrl body arity (returned targets count).
body(block.getArguments())can return the wrong number of values; the verifier will fail later, but this builder can produce a much clearer error at the source.Proposed fix
std::pair<ValueRange, ValueRange> QCOProgramBuilder::ctrl( ValueRange controls, ValueRange targets, const std::function<SmallVector<Value>(ValueRange)>& body) { checkFinalized(); auto ctrlOp = CtrlOp::create(*this, loc, controls, targets); auto& block = ctrlOp.getBodyRegion().emplaceBlock(); const auto qubitType = QubitType::get(getContext()); for (size_t i = 0; i < targets.size(); ++i) { const auto arg = block.addArgument(qubitType, loc); updateQubitTracking(targets[i], arg); } const InsertionGuard guard(*this); setInsertionPointToStart(&block); const auto innerTargetsOut = body(block.getArguments()); + if (innerTargetsOut.size() != targets.size()) { + llvm::reportFatalUsageError( + "QCOProgramBuilder::ctrl body must return exactly one value per target"); + } YieldOp::create(*this, loc, innerTargetsOut);mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (1)
1107-1146: Guard against pre-existing entry-block arguments before appending target aliases.
If the cloned QC region ever has block arguments (unexpected today, but easy to regress),addArgumentwill silently create a mismatched region signature.Proposed fix
auto& dstRegion = qcoOp.getRegion(); rewriter.cloneRegionBefore(op.getRegion(), dstRegion, dstRegion.end()); // Create block arguments for target qubits and store them in // `state.targetsIn`. auto& entryBlock = dstRegion.front(); + assert(entryBlock.getNumArguments() == 0 && + "QC ctrl region unexpectedly has entry block arguments"); SmallVector<Value> qcoTargetAliases;mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (1)
776-806: Add a size assertion before indexingadaptor.getTargetsIn()during block-arg elimination.
This avoids silent OOB if invariants drift (e.g., malformed IR reaching the converter).Proposed fix
auto& entryBlock = dstRegion.front(); const auto numArgs = entryBlock.getNumArguments(); + assert(adaptor.getTargetsIn().size() == numArgs && + "qco.ctrl: entry block args must match number of target operands"); // 1. Replace uses (Must be done BEFORE erasing) // We iterate 0..N using indices since the block args are still stable // here. for (auto i = 0UL; i < numArgs; ++i) { entryBlock.getArgument(i).replaceAllUsesWith(adaptor.getTargetsIn()[i]); }mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
249-283: CtrlOp::build(UnitaryOpInterface): replacement should not assume body usestargets[i]directly.
With target-aliasing, thebodyUnitaryoften consumes block arguments, not the op’stargetsInoperands;op->replaceUsesOfWith(targets[i], arg)can become a no-op, leaving the cloned unitary with dangling operands. This also makes canonicalizations like “remove trivial ctrl” / “merge nested ctrl” very likely to break unless operands are remapped explicitly.A safer approach is to remap the cloned unitary’s target operands based on the original body unitary inputs (or via an
IRMapping) rather thantargets[i].
285-314: Verifier should enforce “targetsIn are not used inside the body region”.
Current checks validate block-arg count/types and yield arity, but don’t prevent the originaltargetsInValues from being referenced inside the region (which reintroduces the multi-use/linear-semantics violation this PR is fixing). Consider walking the body and rejecting any operand equal to atargetsInvalue.
🤖 Fix all issues with AI agents
In @mlir/lib/Dialect/QCO/IR/QCOOps.cpp:
- Around line 32-110: printTargetAliasing currently calls region.front()
unconditionally which can assert/crash for empty/malformed regions; guard
against this by checking region.empty() before accessing region.front(), and
when non-empty proceed as before, otherwise avoid dereferencing the entry block
and instead print a safe representation (e.g., just the targets or empty
parentheses) without accessing block arguments; also use a loop bound of
min(targetsIn.size(), entryBlock.getNumArguments()) when iterating to avoid
out-of-bounds access, then call printer.printRegion only when the region is
valid.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (9)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (36)
📓 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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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: 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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
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.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
📚 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/Builder/QCOProgramBuilder.hmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-10-09T13:14:10.178Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:219-221
Timestamp: 2025-10-09T13:14:10.178Z
Learning: The MQT Core project (munich-quantum-toolkit/core repository) uses the C++20 standard, not C++17. C++20 features such as abbreviated function templates (e.g., `const auto&` parameters) are supported and valid in this codebase.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt
📚 Learning: 2026-01-04T23:54:33.540Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: src/qdmi/na/CMakeLists.txt:179-184
Timestamp: 2026-01-04T23:54:33.540Z
Learning: In the munich-quantum-toolkit/core repository, MSVC link-time code generation (LTCG) must be disabled for the NA device dynamic library target (mqt-core-qdmi-na-device-dyn) on Windows to avoid link errors and missing symbols. This issue affects both release-only builds and debug/release mixed builds, not just debug/release mixing scenarios.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt
📚 Learning: 2026-01-08T22:56:09.502Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1435
File: docs/mlir/QC.md:1-11
Timestamp: 2026-01-08T22:56:09.502Z
Learning: In the munich-quantum-toolkit/core repository, MLIR dialect documentation files (e.g., MLIRQCDialect.md, MLIRQCInterfaces.md) are automatically generated during the documentation build via the ReadTheDocs `pre_build` step and do not need to be committed to the repository.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/Dialect/QCO/IR/Modifiers/CMakeLists.txtmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass, dynamic legality for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assumes that all involved operands and results are consistently either classical types or qubit types (no mixing). Therefore, during review, verify that the conversion decision only depends on the presence of qc::QubitType in operands or arguments; there is no need to separately check operands and results. This guidance applies specifically to the QCToQCO.cpp implementation, and focuses on ensuring the check uses a unified type-domain detection (presence of qc::QubitType) to determine whether conversion is required.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.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-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/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-04T22:30:02.780Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: test/qir/runner/test_qir_runner.cpp:34-34
Timestamp: 2026-01-04T22:30:02.780Z
Learning: In test/qir/runner/test_qir_runner.cpp, when constructing commands for std::system with std::filesystem::path objects, the paths should not be manually quoted. The std::filesystem::path streaming operator already provides appropriate quoting by default, and adding extra quotes around the executable path causes std::system to handle the command incorrectly on Windows.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-12-07T09:10:31.836Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.836Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, ensure all SCF conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume and enforce that all operands are qubit types (qco.qubit or qc.qubit) and do not handle mixed qubit/classical operands. The conversion should be scoped to all-qubit SCF operations only; add explicit type checks or assertions to reject non-qubit operands and document this constraint in the code comments to prevent future regressions.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧬 Code graph analysis (4)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
ctrl(555-584)ctrl(555-557)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (1)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (4)
mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (8)
build(149-160)build(149-150)build(162-173)build(162-164)verify(175-208)verify(175-175)getNumTargets(117-117)getNumTargets(117-117)mlir/include/mlir/Dialect/QC/IR/QCDialect.h (1)
getNumTargets(70-70)mlir/include/mlir/Dialect/QCO/IR/QCODialect.h (1)
getNumTargets(70-70)mlir/lib/Dialect/QC/IR/Operations/StandardGates/BarrierOp.cpp (2)
getNumTargets(23-23)getNumTargets(23-23)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (4)
ctrl(418-423)ctrl(418-419)checkFinalized(450-454)checkFinalized(450-450)mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (3)
guard(67-67)guard(151-151)guard(165-165)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[style] 37-37: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 92-92: The function 'printTargetAliasing' is never used.
(unusedFunction)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
[information] 28-28: Include file
(missingIncludeSystem)
⏰ 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). (17)
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🐍 Test (macos-15) / 🐍 macos-15
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
🔇 Additional comments (12)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)
203-307: SmallVector-returning ctrl bodies look consistent and fix the ValueRange lifetime hazard.
The updatedctrl(...)->SmallVector<Value>lambdas (including empty-vector yields for zero-target ops) match the new builder contract.mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (1)
106-120: CtrlInlineGPhase update to SmallVector-returning body is correct.
Matches the new CtrlOp builder contract and avoids returning ranges backed by temporaries.mlir/unittests/Dialect/QCO/IR/Modifiers/CMakeLists.txt (1)
1-22: Unit test target wiring looks good; verify the directory is included from the parent CMake.
If not already, ensure the parentmlir/unittests/...CMake adds this subdirectory so the test is built/executed in CI.mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (5)
33-58: LGTM: Clean test fixture setup.The test fixture is well-structured with proper dialect loading and a helpful
testParsemethod that wraps assembly in a function context to provide operands for testing.
60-100: LGTM: Critical test validating one-use semantics fix.Lines 96-97 contain the key assertions that verify operations inside the region use block arguments (
block.getArgument(0),block.getArgument(1)) rather than the original target operands. This directly validates that the PR successfully fixes the one-use violation issue #1427.
102-138: LGTM: UnitaryOpInterface builder test covers template-based construction.The test correctly verifies that when constructing CtrlOp from a template unitary operation, the cloned operation inside the region uses the aliased block argument (line 134) rather than the original operand.
140-213: LGTM: Comprehensive verifier constraint tests.The negative tests thoroughly validate the verifier's enforcement of:
- Exact body size (unitary + yield only)
- Block argument count matching target count
- Qubit type requirements for block arguments
- Unitary-only constraint for the first body operation
215-255: LGTM: Parser error handling is well-tested.The tests cover key syntactic variations of the new
targets(...)aliasing clause, ensuring the custom parser properly rejects malformed input.mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (4)
1028-1031: LGTM: YieldOp example reflects new target aliasing syntax.The example correctly demonstrates that operations inside the region use the aliased block arguments (
%a0,%a1) declared in thetargets(...)clause, rather than the original operands.
1060-1063: LGTM: CtrlOp example consistently shows target aliasing.The example aligns with the YieldOp example and clearly illustrates the scf-style aliasing pattern where
targets(%a0 = %q0, %a1 = %q1)introduces block arguments that operations must use.
1072-1074: LGTM: Assembly format delegates to custom parser.The
custom<TargetAliasing>directive is the correct approach for handling the complex target aliasing syntax. The custom parser/printer (implemented in the .cpp file) can properly manage thetargets(%arg = %operand, ...)syntax and block argument creation.
1104-1104: All call sites correctly updated for breaking builder signature change.The body builder return type changed from
ValueRangetoSmallVector<Value>to fix the dangling reference issue where lambdas returned temporary ranges pointing to local variables. This is a breaking API change, but verification confirms all internal call sites in QCOProgramBuilder, unit tests, and CtrlOp implementations have been properly updated to returnSmallVector<Value>.
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
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/Builder/QCOProgramBuilder.cpp (1)
563-592: Guard against mismatchedinnerTargetsOutsize to prevent tracking leaking region-local values.If the body returns fewer/more values than
targets,validQubitscan end up containing ctrl-region block arguments or inner-region SSA values afterctrl()returns; laterfinalize()may emit deallocs in the main block for those values (invalid IR).Proposed fix
setInsertionPointToStart(&block); const auto innerTargetsOut = body(block.getArguments()); YieldOp::create(*this, loc, innerTargetsOut); + if (innerTargetsOut.size() != targets.size()) { + llvm::reportFatalUsageError( + "Ctrl body must return exactly one output qubit per target"); + } + // Update tracking const auto& controlsOut = ctrlOp.getControlsOut(); for (const auto& [control, controlOut] : llvm::zip(controls, controlsOut)) { updateQubitTracking(control, controlOut); } const auto& targetsOut = ctrlOp.getTargetsOut(); for (const auto& [target, targetOut] : llvm::zip(innerTargetsOut, targetsOut)) { updateQubitTracking(target, targetOut); }mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (1)
285-346: Verifier no longer reliably catches duplicate/overlapping target operands after switching to block-arg aliasing.Because
bodyUnitarynow consumes block arguments, the existing “duplicate qubit” logic won’t catch cases like:
- the same SSA value passed twice in
getTargetsIn(), or- the same SSA value used as both control and target.
Also, the verifier should explicitly enforce that the body unitary’s qubit operands are the entry block args (otherwise malformed IR can reintroduce the original “reuse target operand inside region” bug).
Proposed verifier strengthening
LogicalResult CtrlOp::verify() { auto& block = *getBody(); if (block.getOperations().size() != 2) { return emitOpError("body region must have exactly two operations"); } const auto numTargets = getNumTargets(); if (block.getArguments().size() != numTargets) { return emitOpError( "number of block arguments must match the number of targets"); } const auto qubitType = QubitType::get(getContext()); for (size_t i = 0; i < numTargets; ++i) { if (block.getArgument(i).getType() != qubitType) { return emitOpError("block argument type at index ") << i << " does not match target type"; } } if (!llvm::isa<UnitaryOpInterface>(block.front())) { return emitOpError( "first operation in body region must be a unitary operation"); } if (!llvm::isa<YieldOp>(block.back())) { return emitOpError( "second operation in body region must be a yield operation"); } if (const auto numYieldOperands = block.back().getNumOperands(); numYieldOperands != numTargets) { return emitOpError("yield operation must yield ") << numTargets << " values, but found " << numYieldOperands; } - SmallPtrSet<Value, 4> uniqueQubitsIn; - for (const auto& control : getControlsIn()) { - if (!uniqueQubitsIn.insert(control).second) { - return emitOpError("duplicate control qubit found"); - } - } - auto bodyUnitary = getBodyUnitary(); - const auto numQubits = bodyUnitary.getNumQubits(); - for (size_t i = 0; i < numQubits; i++) { - if (!uniqueQubitsIn.insert(bodyUnitary.getInputQubit(i)).second) { - return emitOpError("duplicate qubit found"); - } - } + // 1) Operand-level uniqueness (controls + targets operands) + SmallPtrSet<Value, 4> uniqueOperandQubits; + for (const auto& control : getControlsIn()) { + if (!uniqueOperandQubits.insert(control).second) { + return emitOpError("duplicate control qubit found"); + } + } + for (const auto& target : getTargetsIn()) { + if (!uniqueOperandQubits.insert(target).second) { + return emitOpError("duplicate or overlapping target qubit found"); + } + } + + // 2) Body must consume the entry block args (aliasing requirement) + auto bodyUnitary = getBodyUnitary(); + if (bodyUnitary.getNumQubits() != numTargets) { + return emitOpError("unitary in body must use exactly ") + << numTargets << " target qubits"; + } + for (size_t i = 0; i < numTargets; ++i) { + if (bodyUnitary.getInputQubit(i) != block.getArgument(i)) { + return emitOpError("unitary operand at index ") + << i << " must be the corresponding entry block argument"; + } + } + + // 3) Yield must forward the unitary outputs 1:1 (prevents yielding aliases) + auto yieldOp = llvm::cast<YieldOp>(block.back()); + for (size_t i = 0; i < numTargets; ++i) { + if (yieldOp.getOperand(i) != bodyUnitary.getOutputQubit(i)) { + return emitOpError("yield operand at index ") + << i << " must be the corresponding unitary result"; + } + } SmallPtrSet<Value, 4> uniqueQubitsOut; for (const auto& control : getControlsOut()) { if (!uniqueQubitsOut.insert(control).second) { return emitOpError("duplicate control qubit found"); } } for (size_t i = 0; i < numQubits; i++) { if (!uniqueQubitsOut.insert(bodyUnitary.getOutputQubit(i)).second) { return emitOpError("duplicate qubit found"); } }mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (1)
1107-1147: Assumption confirmed:qco::CtrlOp::createproduces an operation with an empty region.The ODS definition declares
let regions = (region SizedRegion<1>:$region);with no auto-created blocks. TheSingleBlocktrait ensures only one block exists once populated. This is confirmed by the QCOProgramBuilder pattern, which explicitly callsemplaceBlock()aftercreate(). Your code correctly relies on this:cloneRegionBefore()populates the empty region, sodstRegion.front()correctly accesses the cloned entry block.Consider the optional micro-optimization:
- Replace
state.targetsIn.try_emplace(state.inCtrlOp, qcoTargetAliases);withstate.targetsIn.try_emplace(state.inCtrlOp, std::move(qcoTargetAliases));
🤖 Fix all issues with AI agents
In @mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h:
- Around line 989-1006: The public ctrl API currently takes a std::function for
the body callback which can cause heap allocations on hot paths; change the
signature of QCOProgramBuilder::ctrl to accept
llvm::function_ref<llvm::SmallVector<Value>(ValueRange)> (or a templated
Callable) instead of std::function, update all callers to match the new callable
type, and add the necessary include for llvm/ADT/STLExtras.h (or the header that
defines function_ref); ensure the semantics and return type
(std::pair<ValueRange, ValueRange>) remain unchanged.
In @mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:
- Around line 777-805: The current assert line in the qco->qc clone block uses
invalid unary + syntax and leaves the code vulnerable to out-of-bounds access in
release builds; replace the invalid assert on adaptor.getTargetsIn().size() ==
numArgs with the proper MLIR/C++ form (use && "message") and add a runtime
guard: check adaptor.getTargetsIn().size() at runtime and if it doesn't match
numArgs either emit a match failure/error (e.g., via rewriter.notifyMatchFailure
or report an error) or compute a safe iteration count (size_t targetsSize =
adaptor.getTargetsIn().size(); auto safeCount = std::min(numArgs, targetsSize))
and use safeCount when calling
entryBlock.getArgument(i).replaceAllUsesWith(...), while still erasing the
original entryBlock.eraseArguments(0, numArgs) only after ensuring arguments are
handled safely; reference symbols: adaptor.getTargetsIn(), numArgs, entryBlock,
replaceAllUsesWith, eraseArguments, and
rewriter.modifyOpInPlace/qc::CtrlOp::create.
In @mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp:
- Around line 139-160: The test uses QCOProgramBuilder (builder) after calling
builder.finalize() by calling builder.setInsertionPoint() and
builder.create<XOp>(), which bypasses checkFinalized(); fix by not using builder
after finalize: either perform the extra insertion before calling
builder.finalize(), or obtain a local OpBuilder/OpBuilder::InsertionGuard tied
to the CtrlOp region/block (e.g., create an OpBuilder with builder.getContext()
and set its insertion point to block.back() or to the block's terminator) and
use that OpBuilder to create the XOp; ensure you reference builder.finalize(),
ctrlOp.getRegion(), region.front()/block, and replace builder.create<XOp>() with
opBuilder.create<XOp>() (or move the insert to before finalize).
- Around line 101-137: The test creates a template XOp (xOp) in the main block
and then uses the same qubit as a target for CtrlOp::create, causing q[1] to be
consumed twice; fix by ensuring the template op is not left in the main block —
either create the template XOp in a temporary/isolated region or remove/erase
xOp after using it to build the controlled operation (i.e., call
xOp.getOperation().erase() or construct xOp inside a helper region) so that only
the CtrlOp consumes q[1] in the final IR; update the UnitaryOpBuilder test to
use the isolated template or erase xOp before verifying ctrlOp and calling
mlir::verify.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (29)
📓 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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, ensure all SCF conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume and enforce that all operands are qubit types (qco.qubit or qc.qubit) and do not handle mixed qubit/classical operands. The conversion should be scoped to all-qubit SCF operations only; add explicit type checks or assertions to reject non-qubit operands and document this constraint in the code comments to prevent future regressions.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 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/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Builder/QCOProgramBuilder.hmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass, dynamic legality for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assumes that all involved operands and results are consistently either classical types or qubit types (no mixing). Therefore, during review, verify that the conversion decision only depends on the presence of qc::QubitType in operands or arguments; there is no need to separately check operands and results. This guidance applies specifically to the QCToQCO.cpp implementation, and focuses on ensuring the check uses a unified type-domain detection (presence of qc::QubitType) to determine whether conversion is required.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
🧬 Code graph analysis (5)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (2)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
ctrl(563-592)ctrl(563-565)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (4)
ctrl(418-423)ctrl(418-419)checkFinalized(450-454)checkFinalized(450-450)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (2)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir(30-171)mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
verify(285-346)verify(285-285)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (1)
mlir/include/mlir/Dialect/QCO/IR/QCODialect.h (1)
getNumTargets(70-70)
🪛 Cppcheck (2.19.0)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
⏰ 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). (16)
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🐍 Test (macos-15) / 🐍 macos-15
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
There was a problem hiding this 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 (2)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
239-275: Build changes align with target aliasing (LGTM), but consider asserting return arity in the builder overload. Right now invalid builder callbacks can construct invalid IR until verification.
277-337: Verifier still allows reintroducing the original bug (body can use original target operands). With aliasing, a user can still reference%q1_in(the target operand) inside the body, causing “two uses per value” again. Also, duplicates betweencontrolsInandtargetsIn(or withintargetsIn) are no longer detected because the body now uses block args.Proposed verifier tightening (enforce alias usage + input uniqueness)
LogicalResult CtrlOp::verify() { auto& block = *getBody(); if (block.getOperations().size() != 2) { return emitOpError("body region must have exactly two operations"); } const auto numTargets = getNumTargets(); if (block.getArguments().size() != numTargets) { return emitOpError( "number of block arguments must match the number of targets"); } const auto qubitType = QubitType::get(getContext()); for (size_t i = 0; i < numTargets; ++i) { if (block.getArgument(i).getType() != qubitType) { return emitOpError("block argument type at index ") << i << " does not match target type"; } } if (!llvm::isa<UnitaryOpInterface>(block.front())) { return emitOpError( "first operation in body region must be a unitary operation"); } if (!llvm::isa<YieldOp>(block.back())) { return emitOpError( "second operation in body region must be a yield operation"); } if (const auto numYieldOperands = block.back().getNumOperands(); numYieldOperands != numTargets) { return emitOpError("yield operation must yield ") << numTargets << " values, but found " << numYieldOperands; } SmallPtrSet<Value, 4> uniqueQubitsIn; for (const auto& control : getControlsIn()) { if (!uniqueQubitsIn.insert(control).second) { return emitOpError("duplicate control qubit found"); } } + for (const auto& target : getTargetsIn()) { + if (!uniqueQubitsIn.insert(target).second) { + return emitOpError("duplicate target qubit found"); + } + } auto bodyUnitary = getBodyUnitary(); + if (bodyUnitary.getNumQubits() != numTargets) { + return emitOpError("body unitary must operate on exactly ") + << numTargets << " target qubits, but found " + << bodyUnitary.getNumQubits(); + } const auto numQubits = bodyUnitary.getNumQubits(); for (size_t i = 0; i < numQubits; i++) { - if (!uniqueQubitsIn.insert(bodyUnitary.getInputQubit(i)).second) { - return emitOpError("duplicate qubit found"); - } + if (bodyUnitary.getInputQubit(i) != block.getArgument(i)) { + return emitOpError("body unitary must use target alias block argument ") + << i << " (and not the original target operand)"; + } } + + // Also require yield to forward the unitary's outputs in-order. + for (size_t i = 0; i < numTargets; ++i) { + if (block.back().getOperand(i) != bodyUnitary.getOutputQubit(i)) { + return emitOpError("yield operand ") + << i << " must be the body unitary output qubit " << i; + } + } SmallPtrSet<Value, 4> uniqueQubitsOut; for (const auto& control : getControlsOut()) { if (!uniqueQubitsOut.insert(control).second) { return emitOpError("duplicate control qubit found"); } } for (size_t i = 0; i < numQubits; i++) { if (!uniqueQubitsOut.insert(bodyUnitary.getOutputQubit(i)).second) { return emitOpError("duplicate qubit found"); } } if (llvm::isa<BarrierOp>(bodyUnitary.getOperation())) { return emitOpError("BarrierOp cannot be controlled"); } return success(); }
🤖 Fix all issues with AI agents
In @mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:
- Around line 1132-1147: state.targetsIn.try_emplace can leave stale aliases if
state.inCtrlOp already exists; replace the try_emplace call so the map entry is
always overwritten with the newly built qcoTargetAliases. Specifically, after
building qcoTargetAliases in the rewriter.modifyOpInPlace block, assign or use
insert_or_assign on state.targetsIn with key state.inCtrlOp and value
std::move(qcoTargetAliases) (or use operator[] and std::move), ensuring the map
entry for state.inCtrlOp is replaced rather than left unchanged.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (7)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (31)
📓 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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
📚 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.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/include/mlir/Dialect/QCO/IR/QCOOps.tdmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/QCO/Builder/QCOProgramBuilder.hmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, ensure all SCF conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume and enforce that all operands are qubit types (qco.qubit or qc.qubit) and do not handle mixed qubit/classical operands. The conversion should be scoped to all-qubit SCF operations only; add explicit type checks or assertions to reject non-qubit operands and document this constraint in the code comments to prevent future regressions.
Applied to files:
mlir/lib/Conversion/QCOToQC/QCOToQC.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: 2026-01-04T21:41:30.252Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: cmake/SetupMLIR.cmake:32-33
Timestamp: 2026-01-04T21:41:30.252Z
Learning: In the munich-quantum-toolkit/core repository, the project provides its own pre-built LLVM/MLIR distributions that are compiled with LLVM_ENABLE_RTTI=ON and LLVM_ENABLE_EH=ON. Users are directed to these distributions via the upgrade guide and installation instructions to avoid ABI compatibility issues. The mid-term goal is to eliminate RTTI and exception handling dependencies entirely.
Applied to files:
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass, dynamic legality for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assumes that all involved operands and results are consistently either classical types or qubit types (no mixing). Therefore, during review, verify that the conversion decision only depends on the presence of qc::QubitType in operands or arguments; there is no need to separately check operands and results. This guidance applies specifically to the QCToQCO.cpp implementation, and focuses on ensuring the check uses a unified type-domain detection (presence of qc::QubitType) to determine whether conversion is required.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
🧬 Code graph analysis (6)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (1)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (4)
ctrl(418-423)ctrl(418-419)checkFinalized(450-454)checkFinalized(450-450)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
ctrl(564-598)ctrl(564-566)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (2)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir(31-172)mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
verify(277-338)verify(277-277)
mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (2)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (5)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (16)
op(539-557)op(540-541)op(578-595)op(579-580)op(616-635)op(617-618)op(664-689)op(665-666)op(715-735)op(716-717)op(1031-1057)op(1032-1033)op(1081-1150)op(1082-1083)op(1168-1177)op(1169-1170)mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (6)
build(149-160)build(149-150)build(162-173)build(162-164)getNumTargets(117-117)getNumTargets(117-117)mlir/include/mlir/Dialect/QC/IR/QCDialect.h (1)
getNumTargets(70-70)mlir/include/mlir/Dialect/QCO/IR/QCODialect.h (1)
getNumTargets(70-70)mlir/lib/Dialect/QC/IR/Operations/StandardGates/BarrierOp.cpp (2)
getNumTargets(23-23)getNumTargets(23-23)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)
mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)src/dd/StateGeneration.cpp (1)
state(126-126)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
[information] 19-19: Include file
(missingIncludeSystem)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
⏰ 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). (17)
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🐍 Test (macos-15) / 🐍 macos-15
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🇨 Coverage / 📈 Coverage
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Lint / 🚨 Lint
🔇 Additional comments (13)
mlir/include/mlir/Dialect/QCO/IR/QCOOps.td (4)
1028-1032: LGTM!The updated example clearly demonstrates the new target aliasing semantics where original values (
%q0,%q1) are consumed as operands and aliases (%a0,%a1) are used as block arguments inside the region, correctly enforcing one-use/linear semantics.
1060-1064: LGTM!The CtrlOp example is consistent with the YieldOp example and correctly demonstrates the complete target aliasing syntax including type annotations.
1071-1079: The custom parser/printer implementations correctly handle the zero-target edge case.The
parseTargetAliasingfunction usesparseOptionalRParen()to detect an immediate closing parenthesis (zero targets), which skips the target parsing loop and passes an emptyblockArgsvector toparseRegion(). TheprintTargetAliasingfunction's loop naturally handles zero targets since the conditioni < 0is never true, resulting in empty parentheses followed by the region. Both functions properly support cases likectrlwrappingGPhaseOpwhere no target aliases are present.
1104-1104: All call sites updated for new CtrlOp builder signature.The callback signature change to return
SmallVector<Value>is appropriate—the builder automatically creates theYieldOpwith the returned values. All QCO dialect usages throughoutQCOProgramBuilder.cppand test files have been consistently updated to the new signature expecting callbacks that returnllvm::SmallVector<Value>.mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (2)
15-20: Header/include cleanup forllvm::function_reflooks correct.llvm/ADT/STLFunctionalExtras.hat Line 18 is the right include forllvm::function_ref.
989-1006: Public builder API change is OK, but re-check callsites forfunction_reflifetime expectations.llvm::function_refcan’t outlive the call; any implementation that storesbodywill break.mlir/lib/Conversion/QCOToQC/QCOToQC.cpp (2)
785-789: Good: explicit validation that targets match entry-block arg count. This prevents silent miswiring when the IR is malformed.
776-784: I cannot definitively verify or refute this review comment because the 3-argumentqc::CtrlOp::create(rewriter, loc, controls)method implementation cannot be located in the codebase.The concern hinges on whether this create method initializes an empty region (no blocks) or pre-creates an empty block. From the learnings provided, similar MLIR operations behave differently:
scf::WhileOpcreates empty regions (no blocks), whilescf::IfOpauto-creates an empty block. The code then immediately clones a region and accessesdstRegion.front(), which would only be correct if the region starts empty (no pre-created blocks).The review comment's proposed defensive fix is reasonable, but confirming whether it's necessary requires examining the generated ODS code for
qc::CtrlOp::createor its explicit implementation in the dialect's generated headers.mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (1)
1087-1123: Please verify QC CtrlOp target semantics match the positional mapping assumption. This conversion assumes that within aqc.ctrl, “target #i” is well-defined and stable (and that nested gate conversions can safely usetargetsIn[inCtrlOp][i]regardless of the specificqcoperand value). Ifqc::CtrlOp::getTarget(i)is derived from the controlled unitary operands, this is probably fine—but it needs to be true for correctness.mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (3)
19-20: Include change is appropriate forllvm::function_ref.
204-232: Controlled zero-target ops updated correctly to the new ctrl-body contract. Returning{}for zero targets is consistent with the new “exactly one output per target” rule.
564-598: ctrl() now enforces aliasing + correct boundary tracking (LGTM). Creating block args for targets and requiringinnerTargetsOut.size() == targets.size()fixes the old “reuse the same mlir::Value in region” behavior.mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (1)
59-99: Good coverage for aliasing shape and operand wiring. LambdaBuilder validates the core invariant (unitary consumes block args).
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
# Conflicts: # CHANGELOG.md
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp:
- Around line 138-139: The comment above the
EXPECT_TRUE(mlir::verify(ctrlOp).succeeded()) is incorrect because the template
op xOp was erased earlier (xOp.erase()), so update or remove the comment to
reflect that xOp no longer exists in the main block before ctrlOp; locate the
comment near the EXPECT_TRUE call in test_qco_ctrl.cpp and either delete it or
change its text to describe the actual state (e.g., that xOp was erased and only
ctrlOp remains) so the comment matches the code.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
CHANGELOG.mdmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cppmlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (36)
📓 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/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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
📚 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/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppCHANGELOG.mdmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-04T22:30:02.780Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: test/qir/runner/test_qir_runner.cpp:34-34
Timestamp: 2026-01-04T22:30:02.780Z
Learning: In test/qir/runner/test_qir_runner.cpp, when constructing commands for std::system with std::filesystem::path objects, the paths should not be manually quoted. The std::filesystem::path streaming operator already provides appropriate quoting by default, and adding extra quotes around the executable path causes std::system to handle the command incorrectly on Windows.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-12-07T09:10:31.836Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.836Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2026-01-08T22:56:09.502Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1435
File: docs/mlir/QC.md:1-11
Timestamp: 2026-01-08T22:56:09.502Z
Learning: In the munich-quantum-toolkit/core repository, MLIR dialect documentation files (e.g., MLIRQCDialect.md, MLIRQCInterfaces.md) are automatically generated during the documentation build via the ReadTheDocs `pre_build` step and do not need to be committed to the repository.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppCHANGELOG.md
📚 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/lib/Dialect/QCO/IR/QCOOps.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.cppmlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.
Applied to files:
mlir/lib/Dialect/QCO/IR/QCOOps.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.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/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppCHANGELOG.md
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cppmlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-14T14:37:38.047Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.
Applied to files:
CHANGELOG.md
📚 Learning: 2025-12-01T11:00:40.342Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 1
File: CHANGELOG.md:18-18
Timestamp: 2025-12-01T11:00:40.342Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the CHANGELOG.md intentionally references the parent MQT Core repository's release notes (https://github.com/munich-quantum-toolkit/core/releases) because the plugin repository is based on work performed in the parent repository.
Applied to files:
CHANGELOG.md
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass, dynamic legality for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assumes that all involved operands and results are consistently either classical types or qubit types (no mixing). Therefore, during review, verify that the conversion decision only depends on the presence of qc::QubitType in operands or arguments; there is no need to separately check operands and results. This guidance applies specifically to the QCToQCO.cpp implementation, and focuses on ensuring the check uses a unified type-domain detection (presence of qc::QubitType) to determine whether conversion is required.
Applied to files:
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
🧬 Code graph analysis (3)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (1)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
verify(276-358)verify(276-276)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (2)
mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp (4)
ctrl(418-423)ctrl(418-419)checkFinalized(450-454)checkFinalized(450-450)mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp (3)
guard(67-67)guard(151-151)guard(165-165)
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (8)
op(39-60)op(39-40)op(69-82)op(69-70)op(91-115)op(91-92)op(124-139)op(124-125)mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h (1)
entryBlock(825-825)
🪛 Cppcheck (2.19.0)
mlir/lib/Dialect/QCO/IR/QCOOps.cpp
[information] 13-13: Include file
(missingIncludeSystem)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[style] 37-37: The function 'parseTargetAliasing' is never used.
(unusedFunction)
[style] 92-92: The function 'printTargetAliasing' is never used.
(unusedFunction)
[information] 132-132: Include file
(missingInclude)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
[information] 18-18: Include file
(missingIncludeSystem)
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
⏰ 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). (17)
- GitHub Check: 🐍 Test (macos-15) / 🐍 macos-15
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
- GitHub Check: 🇨 Lint / 🚨 Lint
🔇 Additional comments (17)
CHANGELOG.md (1)
14-14: ✅ Changelog entry correctly formatted and linked.The PR entry is properly consolidated with related MLIR infrastructure PRs, follows the established format, and all PR and contributor links are correctly defined.
Also applies to: 311-311
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp (3)
18-18: LGTM: New include for function_ref support.The addition of
STLFunctionalExtras.his necessary for thellvm::function_reftype used in the updatedctrl()signature.
212-217: LGTM: Zero-target controlled operations.The lambda correctly returns an empty
SmallVector<Value>for zero-target operations likegphase, matching the expected one-output-per-target constraint.
563-597: LGTM: Core ctrl() implementation with target aliasing.The implementation correctly:
- Creates block arguments for each target (lines 571-574)
- Updates qubit tracking from original targets to block arguments
- Passes block arguments to the body callback
- Validates that the body returns exactly one output per target
- Properly chains tracking through inner outputs to final ctrl outputs
This enforces the one-use/linear semantics by ensuring operations inside the region consume the aliased block arguments rather than the original target operands.
mlir/lib/Dialect/QCO/IR/QCOOps.cpp (2)
36-90: LGTM: Custom parser for target aliasing.The parser correctly:
- Handles the
(%new = %old, ...)syntax with comma-separated entries- Creates block arguments with
QubitType(appropriate since ctrl targets are always qubits)- Passes the collected block arguments to
parseRegionto establish them as entry block argumentsThe static analysis warning about unused function is a false positive - these functions are invoked by TableGen-generated assembly format code.
92-114: LGTM: Custom printer for target aliasing.The printer correctly handles the empty region case and prints the target aliasing syntax that matches the parser's expectations. The
falseargument toprintRegionappropriately suppresses the entry block argument list since it's manually printed in the aliasing format.mlir/lib/Conversion/QCToQCO/QCToQCO.cpp (2)
1108-1109: LGTM: Factory method for CtrlOp creation.Using
qco::CtrlOp::create(rewriter, ...)aligns with the factory pattern used consistently throughout the conversion code.
1132-1146: LGTM: Block argument creation for target aliasing.The code correctly:
- Asserts that the cloned QC region has no entry block arguments (as expected)
- Creates block arguments for each target using
modifyOpInPlace- Stores the aliases in
state.targetsInfor use by inner operation conversionsThis ensures that operations inside the converted region use the block arguments (aliases) rather than the original target operands, preserving one-use semantics.
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (4)
32-57: LGTM: Test fixture setup.The fixture correctly initializes the MLIR context with required dialects and provides a helper for parsing test assembly. The
testParsehelper wraps ctrl op assembly in a function context, which is necessary for providing operands.
59-99: LGTM: Lambda builder test validates target aliasing.This test is well-designed to verify the core fix: lines 94-96 assert that the inner SWAP operation uses block arguments (the aliases) rather than the original target operands, which is the essence of the one-use/linear semantics fix.
142-217: LGTM: Comprehensive verifier tests.The negative tests effectively cover the verifier's validation rules:
- Body must have exactly 2 operations
- Block argument count must match target count
- Block arguments must be qubit types
- First body operation must be a unitary
These tests ensure the verifier catches malformed CtrlOps.
219-259: LGTM: Parser error handling tests.Good coverage of syntax error cases for the target aliasing parser, testing various malformed inputs to ensure proper error handling.
mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (5)
54-57: LGTM: MergeNestedCtrl pattern update.Using
llvm::to_vector(llvm::concat<Value>(...))is a cleaner way to merge controls from both operations.
105-116: LGTM: CtrlInlineGPhase pattern with lambda builder.The pattern correctly converts a controlled global phase to a controlled phase operation. Using
controls.drop_back()for controls andcontrols.back()as the target with the new lambda-based builder is clean and correct.
238-256: LGTM: Build with UnitaryOpInterface using IRMapping.The use of
IRMappingto map original targets to block arguments before cloning ensures the cloned unitary operation inside the region uses the aliased block arguments rather than the original operands. This is essential for maintaining one-use semantics.
258-274: LGTM: Build with lambda bodyBuilder.The implementation correctly creates block arguments and passes them to the body builder. The responsibility for using these arguments (rather than capturing external targets) is delegated to the caller, which is the correct design for a callback-based API.
276-358: LGTM: Comprehensive verifier with target aliasing enforcement.The verifier additions are thorough and enforce the key invariant of this PR:
- Lines 281-292: Block argument count and types must match targets
- Lines 327-330: Critical check - body unitary must use block arguments (aliases), not original target operands
- Lines 333-339: Yield must forward the unitary's outputs in order
The error message at line 328-329 clearly communicates when the one-use semantics are violated.
Signed-off-by: burgholzer <[email protected]>
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp:
- Line 71: Several local variables are declared but never used: the `module`
returned by `builder.finalize()` in the LambdaBuilder test, the `qType` declared
in the UnitaryOpBuilder test, and the multiple `module` variables in the
verifier tests; either remove these unused declarations or, if they are
intentionally kept to extend object lifetime, annotate them with
[[maybe_unused]] to silence compiler warnings (e.g., change the `module` and
`qType` declarations to [[maybe_unused]] auto ... or delete the declarations
entirely where not needed).
- Around line 82-104: Add a call to builder.finalize() at the end of the
UnitaryOpBuilder test to match other tests: after creating and erasing xOp and
before/after the VERIFY step in TEST_F(QCOCtrlOpTest, UnitaryOpBuilder) invoke
builder.finalize() so the builder state is finalized consistent with
LambdaBuilder and verifier tests; ensure it is placed such that it does not
change the assertions (e.g., right after xOp.erase() and before the EXPECT_*
checks or immediately after verifying ctrlOp).
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (28)
📓 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/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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🪛 Cppcheck (2.19.0)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
⏰ 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). (17)
- GitHub Check: 🐍 Test (macos-15) / 🐍 macos-15
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Release) / 🏁 windows-2025 msvc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Release) / 🍎 macos-15 clang Release
- GitHub Check: 🇨 Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
- GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
- GitHub Check: 🇨 Test 🏁 (windows-2025, msvc, Debug) / 🏁 windows-2025 msvc Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🍎 (macos-15, clang, Debug) / 🍎 macos-15 clang Debug
- GitHub Check: 🇨 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
Signed-off-by: burgholzer <[email protected]>
There was a problem hiding this 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
🤖 Fix all issues with AI agents
In @mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp:
- Around line 83-104: The test QCOCtrlOpTest::UnitaryOpBuilder is missing the
module finalization step before verification; call builder.finalize() and assign
it to module (e.g., module = builder.finalize()) after creating/erasing xOp and
before EXPECT_TRUE(mlir::verify(ctrlOp).succeeded()) so the module is fully
constructed like the other tests; update the test to finalize the builder prior
to running mlir::verify.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧰 Additional context used
🧠 Learnings (30)
📓 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/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: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
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.
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T18:49:41.066Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1045-1119
Timestamp: 2026-01-10T18:49:41.066Z
Learning: The QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp) does not need defensive mixed-type checks in its func conversion patterns (ConvertQCOFuncFuncOp, ConvertQCOFuncCallOp, ConvertQCOFuncReturnOp) because the conversion workflow always starts from QC to QCO, and the QCToQCO pass already enforces through its dynamic legality checks that func operations contain only qubit types (no mixed classical/quantum). This upstream guarantee justifies the all-qubit assumptions in QCOToQC patterns.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T17:31:13.555Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:816-862
Timestamp: 2026-01-10T17:31:13.555Z
Learning: In mlir/lib/Conversion/QCOToQC/QCOToQC.cpp, when creating scf::IfOp with `rewriter.create<scf::IfOp>(loc, types, condition, false)`, passing `false` for the withElseRegion parameter creates an else region that exists but contains no blocks. Therefore, inlineRegionBefore can be called on the else region without checking if it exists, and there's no need to erase an auto-created empty block from the else region (unlike the then region, which does get an auto-created empty block that must be erased).
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T15:54:51.550Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:891-923
Timestamp: 2026-01-10T15:54:51.550Z
Learning: In MLIR conversion code (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), scf::WhileOp construction only creates empty regions, not blocks. After creating a WhileOp with `rewriter.create<scf::WhileOp>(...)`, the first calls to `rewriter.createBlock(&whileOp.getBefore(), ...)` and `rewriter.createBlock(&whileOp.getAfter(), ...)` create the initial blocks in those empty regions, so no block erasure is needed. This differs from scf::IfOp, which auto-creates an empty block that must be erased when using `inlineRegionBefore()`.
<!-- <review_comment_addressed>
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:28:37.166Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCToQCO/QCToQCO.cpp:1729-1763
Timestamp: 2026-01-10T16:28:37.166Z
Learning: In the QCToQCO conversion pass (mlir/lib/Conversion/QCToQCO/QCToQCO.cpp), the dynamic legality checks for func operations (func::CallOp, func::FuncOp, func::ReturnOp) and scf operations assume that operations use either all classical types or all qubit types, never mixed. Therefore, checking for the presence of qc::QubitType in operands or arguments is sufficient to determine if conversion is needed—there is no need to check both operands and results separately.
<!-- </add_learning]
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:02.062Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1040-1050
Timestamp: 2026-01-07T12:29:02.062Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), ConvertQCOFuncCallOp assumes that if a func::CallOp has qubit results, then all arguments and results are qubits (no mixed classical/quantum types). The conversion is scoped to handle all-qubit function calls only.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-07T12:29:16.380Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:1070-1085
Timestamp: 2026-01-07T12:29:16.380Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the ConvertQCOFuncFuncOp pattern assumes that when a func.func operation is matched for conversion, all of its arguments are qco.qubit types (never mixed qubit/classical). The pattern unconditionally converts all arguments to qc::QubitType based on this assumption.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-09T00:55:11.926Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Operations/StandardGates/BarrierOp.cpp:45-54
Timestamp: 2025-12-09T00:55:11.926Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux), qubits follow single-user/linear-type semantics where each qubit value can only be consumed once, similar to MQTOpt. This invariant makes it safe to dereference getUsers().begin() in canonicalization patterns like MergeSubsequentBarrier in BarrierOp.cpp, as there will be at most one user per qubit output.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:25:36.887Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp:98-100
Timestamp: 2025-10-09T13:25:36.887Z
Learning: In MLIR code, when traversing parent operations to find a common block between two operations where one uses the result of another, explicit bounds checking is not necessary. MLIR's SSA properties and scope locality guarantees ensure that operations using results must be in compatible scopes and will always share a common ancestor in the operation hierarchy.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-10T16:07:49.698Z
Learnt from: li-mingbao
Repo: munich-quantum-toolkit/core PR: 1396
File: mlir/lib/Conversion/QCOToQC/QCOToQC.cpp:988-1024
Timestamp: 2026-01-10T16:07:49.698Z
Learning: In the QCOToQC conversion pass (mlir/lib/Conversion/QCOToQC/QCOToQC.cpp), the SCF operation conversion patterns (ConvertQCOScfYieldOp, ConvertQCOScfConditionOp, ConvertQCOScfIfOp, ConvertQCOScfWhileOp, ConvertQCOScfForOp) assume that all operands are qubit types (qco.qubit or qc.qubit), never mixed qubit/classical types. The conversion is scoped to handle all-qubit SCF operations only.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-28T17:05:10.588Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: test/qir/runner/test_qir_runner.cpp:35-35
Timestamp: 2025-12-28T17:05:10.588Z
Learning: In the munich-quantum-toolkit/core repository, Windows Unicode support (via _wsystem) is not needed for test utilities like test/qir/runner/test_qir_runner.cpp. Using std::system is acceptable for system calls in tests.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2026-01-04T23:01:47.734Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: mlir/CMakeLists.txt:13-15
Timestamp: 2026-01-04T23:01:47.734Z
Learning: In the munich-quantum-toolkit/core repository's MLIR directory (mlir/CMakeLists.txt), the MSVC compile definition `_SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING` is necessary to suppress warnings triggered by MLIR/LLVM internal headers, not because the project code uses non-floating complex types. This suppression is needed to avoid warnings from third-party MLIR/LLVM header includes.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.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/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-10-09T13:28:29.237Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp:42-45
Timestamp: 2025-10-09T13:28:29.237Z
Learning: In the MQTOpt MLIR dialect, linear types enforce single-use semantics where each qubit value can only be consumed once, preventing duplicate deallocations and making RAUW operations safe when replacing output qubits with corresponding input qubits in transformation patterns.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-14T15:23:54.712Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: docs/conf.py:110-130
Timestamp: 2025-12-14T15:23:54.712Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the Ruff configuration has 'ALL' rules enabled with only specific rules disabled. PLR6301 (no-self-use) is active, so `# noqa: PLR6301` directives are necessary for methods that don't use self, even if Ruff reports RUF100 warnings suggesting the directive is unused.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
📚 Learning: 2025-12-15T15:17:25.469Z
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.
Applied to files:
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
🧬 Code graph analysis (1)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (2)
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h (1)
mlir(31-172)mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp (2)
verify(276-358)verify(276-276)
🪛 Cppcheck (2.19.0)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp
[information] 11-11: Include file
(missingInclude)
[information] 12-12: Include file
(missingInclude)
[information] 14-14: Include file
(missingIncludeSystem)
[information] 15-15: Include file
(missingIncludeSystem)
[information] 16-16: Include file
(missingIncludeSystem)
[information] 17-17: Include file
(missingIncludeSystem)
[information] 18-18: Include file
(missingIncludeSystem)
[information] 19-19: Include file
(missingIncludeSystem)
[information] 20-20: Include file
(missingIncludeSystem)
[information] 21-21: Include file
(missingIncludeSystem)
[information] 22-22: Include file
(missingIncludeSystem)
[information] 23-23: Include file
(missingIncludeSystem)
[information] 24-24: Include file
(missingIncludeSystem)
[information] 25-25: Include file
(missingIncludeSystem)
[information] 26-26: Include file
(missingIncludeSystem)
[information] 27-27: Include file
(missingIncludeSystem)
⏰ 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). (8)
- GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
- GitHub Check: 🐍 Test (windows-2025) / 🐍 windows-2025
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
- GitHub Check: 🇨 Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
- GitHub Check: 🐍 Lint / 🚨 Lint
- GitHub Check: 🇨 Coverage / 📈 Coverage
- GitHub Check: 🇨 Lint / 🚨 Lint
🔇 Additional comments (4)
mlir/unittests/Dialect/QCO/IR/Modifiers/test_qco_ctrl.cpp (4)
32-58: LGTM! Test fixture setup is correct.The test fixture properly initializes the MLIRContext, loads necessary dialects, and provides a helper for parsing ctrl op assembly within a function context.
60-81: LGTM! Lambda builder test is well-structured.The test correctly exercises the lambda-based ctrl builder, verifies the operation structure (control/target counts, results), and confirms the operation passes verification.
106-181: LGTM! Verifier tests comprehensively cover error paths.The verifier tests appropriately exercise different failure scenarios:
- Body operation count validation
- Block argument count validation
- Block argument type validation
- Body operation type validation (unitary requirement)
The tests correctly manipulate the CtrlOp's internal structure to trigger each specific verifier error.
183-223: LGTM! Parser error tests provide comprehensive coverage.The test cases thoroughly exercise the parser's error handling for the new target aliasing syntax, covering missing delimiters, missing argument names, and missing operands. All malformed inputs correctly fail parsing.
burgholzer
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DRovara thanks for pushing this.
I made some further changes to this that mainly do two things:
- make the
QCOProgramBuilderwork (including a signature change that was only discovered to be necessary through this PR) - optimize various bits and pieces throughout to use LLVM/MLIR concepts wherever reasonable and to avoid redundant computation.
I am happy with this PR now, and so is the Rabbit.
If you are happy as well, feel free to merge.
Thanks for the changes! I guess the fact that CodeCov is not fully happy can be fixed once we actually finish up the full test system. The main functionalities I introduced here should all be tested, at least. |
Description
With the current design of QCO's
ctrlblocks, targets have to be specified in theqco.ctrlcall and can then be used inside the region's body. However, this violates linear types: the corresponding variables will have two uses, one in theqco.ctrlsignature and another one when actually using them inside the body.To solve this issue, the
qco.ctrloperation now defines aliases that have to be used inside the body, consistent with how, e.g., thescfdialect handles such situations.For example, a
qco.ctrlcall may now look like this:This change not only required an update to the tablegen file for QCO, but also:
Fixes #1427
Checklist: