Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
59f87ae
fix(mlir): :bug: fix one-time-use of variables for the `ctrl` modifier
DRovara Jan 9, 2026
a50d3af
Merge branch 'main' into mlir/fix/ctrl-one-use
DRovara Jan 9, 2026
fd061c7
fix(mlir): :bug: fix error introduced by merge
DRovara Jan 9, 2026
7179197
docs(mlir): :memo: update examples in QCOOps.td
DRovara Jan 9, 2026
ce1ae87
refactor(mlir): :rotating_light: fix some linter issues
DRovara Jan 9, 2026
b88acbd
refactor(mlir): :rotating_light: further linter issue fixes
DRovara Jan 9, 2026
e6a09e1
refactor(mlir): :rotating_light: fix more linter issues
DRovara Jan 9, 2026
becf786
refactor(mlir): :recycle: reuse numTargets in conversion
DRovara Jan 9, 2026
a92d2c4
refactor(mlir): :recycle: implement coderabbit suggestions
DRovara Jan 9, 2026
0059475
feat(mlir): :sparkles: implement lambda-based builder for QCO CtrlOps
DRovara Jan 9, 2026
6c190b5
fix(mlir): :bug: make `qco.ctrl` builder that take a `UnitaryOpInterf…
DRovara Jan 9, 2026
87dd322
chore(mlir): :recycle: add verification for the block argument type i…
DRovara Jan 9, 2026
dedbd64
test(mlir): :white_check_mark: add test for UnitaryOpInterface builde…
DRovara Jan 9, 2026
38a4046
test(mlir): :white_check_mark: add tests for CtrlOp verifier
DRovara Jan 9, 2026
c214261
refactor(mlir): :recycle: apply coderabbit recommendations
DRovara Jan 9, 2026
411b1d5
fix(mlir): :bug: fix datatypes of valueranges in test
DRovara Jan 9, 2026
d055ff1
refactor(mlir): :rotating_light: fix linter issues
DRovara Jan 9, 2026
98ddd5c
test(mlir): :white_check_mark: also test custom parsing
DRovara Jan 9, 2026
3c94d7b
refactor(mlir): :recycle: replace some `EXPECT` with `ASSERT` for cle…
DRovara Jan 9, 2026
2bbb63b
fix linter issues
DRovara Jan 10, 2026
d6c53a5
fix linter issues
DRovara Jan 10, 2026
eeb8b9a
🐛 body lambda function must not return ValueRange
burgholzer Jan 10, 2026
5f330fa
🎨 use `<OP>::create` over `builder.create` for better autocompletion …
burgholzer Jan 10, 2026
012e55f
🎨 micro-optimizations
burgholzer Jan 10, 2026
baff792
♻️ rework tests to rely more on the QCOProgramBuilder for more concis…
burgholzer Jan 10, 2026
d553b39
🐛 fix the QCOProgramBuilder convenience methods
burgholzer Jan 10, 2026
91474b1
🚨👌 Address clang-tidy warnings and CodeRabbit feedback
burgholzer Jan 10, 2026
d513e6b
🚨 fix missing header
burgholzer Jan 10, 2026
4a60744
👌 Address CodeRabbit feedback
burgholzer Jan 10, 2026
f1659bd
🎨 even more idiomatic MLIR code
burgholzer Jan 11, 2026
015e0b5
🚨 clang-tidy warnings
burgholzer Jan 11, 2026
3c2fae2
📝 add to changelog
burgholzer Jan 11, 2026
4fe36f2
👌 more CodeRabbit feedback
burgholzer Jan 11, 2026
20b3a7e
Merge branch 'main' into mlir/fix/ctrl-one-use
burgholzer Jan 11, 2026
631f3b3
👌 more CodeRabbit feedback
burgholzer Jan 11, 2026
a4311b3
👌 more CodeRabbit feedback
burgholzer Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1025,10 +1025,10 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> {

Example:
```mlir
%ctrl_q_out, %tgt_q_out = qco.ctrl(%ctrl_q_in) %tgt_q_in {
%tgt_q_res = qco.h %tgt_q_in : !qco.qubit -> !qco.qubit
qco.yield %tgt_q_res : !qco.qubit
} : ({!qco.qubit}, {!qco.qubit}) -> ({!qco.qubit}, {!qco.qubit})
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit -> !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit}) -> ({!qco.qubit}, {!qco.qubit, !qco.qubit})
```
}];

Expand Down Expand Up @@ -1057,10 +1057,10 @@ def CtrlOp : QCOOp<"ctrl", traits =

Example:
```mlir
%ctrl_q_out, %tgt_q_out = qco.ctrl(%ctrl_q_in) %tgt_q_in {
%tgt_q_res = qco.h %tgt_q_in : !qco.qubit -> !qco.qubit
qco.yield %tgt_q_res : !qco.qubit
} : ({!qco.qubit}, {!qco.qubit}) -> ({!qco.qubit}, {!qco.qubit})
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit -> !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit}) -> ({!qco.qubit}, {!qco.qubit, !qco.qubit})
```
}];

Expand All @@ -1069,8 +1069,10 @@ def CtrlOp : QCOOp<"ctrl", traits =
let results = (outs Variadic<QubitType>:$controls_out, Variadic<QubitType>:$targets_out);
let regions = (region SizedRegion<1>:$region);
let assemblyFormat = [{
`(` $controls_in `)` $targets_in
$region attr-dict `:`
`(` $controls_in `)`
`targets`
custom<TargetAliasing>($region, $targets_in)
attr-dict `:`
`(` `{` type($controls_in) `}` ( `,` `{` type($targets_in)^ `}` )? `)`
`->`
`(` `{` type($controls_out) `}` ( `,` `{` type($targets_out)^ `}` )? `)`
Expand Down
24 changes: 22 additions & 2 deletions mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,32 @@ struct ConvertQCOCtrlOp final : OpConversionPattern<qco::CtrlOp> {
const auto& qcControls = adaptor.getControlsIn();

// Create qc.ctrl operation
auto qcoOp = rewriter.create<qc::CtrlOp>(op.getLoc(), qcControls);
auto qcOp = rewriter.create<qc::CtrlOp>(op.getLoc(), qcControls);

// Clone body region from QCO to QC
auto& dstRegion = qcoOp.getRegion();
auto& dstRegion = qcOp.getRegion();
rewriter.cloneRegionBefore(op.getRegion(), dstRegion, dstRegion.end());

// Remove all block arguments in the cloned region
rewriter.modifyOpInPlace(qcOp, [&] {
auto& entryBlock = dstRegion.front();
auto numArgs = entryBlock.getNumArguments();

// 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]);
}

// 2. Erase all block arguments
// Now that they have no uses, we can safely wipe them.
// We use a bulk erase for efficiency (start index 0, count N).
if (numArgs > 0) {
entryBlock.eraseArguments(0, numArgs);
}
});

// Replace the output qubits with the same QC references
rewriter.replaceOp(op, adaptor.getOperands());

Expand Down
17 changes: 16 additions & 1 deletion mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1124,12 +1124,27 @@ struct ConvertQCCtrlOp final : StatefulOpConversionPattern<qc::CtrlOp> {

// Update modifier information
state.inCtrlOp++;
state.targetsIn.try_emplace(state.inCtrlOp, qcoTargets);

// Clone body region from QC to QCO
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();
SmallVector<Value> qcoTargetAliases;
qcoTargetAliases.reserve(numTargets);

rewriter.modifyOpInPlace(qcoOp, [&] {
for (auto i = 0UL; i < qcoOp.getNumTargets(); i++) {
auto originalTarget = qcoOp.getInputTarget(i);
auto arg =
entryBlock.addArgument(originalTarget.getType(), op.getLoc());
qcoTargetAliases.push_back(arg);
}
});
state.targetsIn.try_emplace(state.inCtrlOp, qcoTargetAliases);

rewriter.eraseOp(op);
return success();
}
Expand Down
84 changes: 84 additions & 0 deletions mlir/lib/Dialect/QCO/IR/QCOOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
*/

#include "mlir/Dialect/QCO/IR/QCODialect.h" // IWYU pragma: associated
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Region.h"
#include "mlir/Support/LogicalResult.h"

#include "llvm/ADT/SmallVector.h"

Check warning on line 16 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:16:1 [misc-include-cleaner]

included header SmallVector.h is not used directly

// The following headers are needed for some template instantiations.
// IWYU pragma: begin_keep
Expand All @@ -21,6 +26,85 @@
using namespace mlir;
using namespace mlir::qco;

//===----------------------------------------------------------------------===//
// Custom Parsers
//===----------------------------------------------------------------------===//

namespace {
ParseResult
parseTargetAliasing(OpAsmParser& parser, Region& region,

Check warning on line 35 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:35:1 [llvm-prefer-static-over-anonymous-namespace]

function 'parseTargetAliasing' is declared in an anonymous namespace; prefer using 'static' for restricting visibility
SmallVectorImpl<OpAsmParser::UnresolvedOperand>& operands) {

Check warning on line 36 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:36:21 [misc-include-cleaner]

no header providing "mlir::SmallVectorImpl" is directly included
// 1. Parse the opening parenthesis
if (parser.parseLParen()) {
return failure();
}

// Temporary storage for block arguments we are about to create
SmallVector<OpAsmParser::Argument> blockArgs;

Check warning on line 43 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:43:3 [misc-include-cleaner]

no header providing "mlir::SmallVector" is directly included

// 2. Prepare to parse the list
if (failed(parser.parseOptionalRParen())) {
do {
OpAsmParser::Argument newArg; // The "new" variable name
OpAsmParser::UnresolvedOperand oldOperand; // The "old" input variable

// Parse "%new"
if (parser.parseArgument(newArg)) {
return failure();
}

// Parse "="
if (parser.parseEqual()) {
return failure();
}

// Parse "%old"
if (parser.parseOperand(oldOperand)) {
return failure();
}
operands.push_back(oldOperand);

Type type = qco::QubitType::get(parser.getBuilder().getContext());

Check warning on line 67 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:67:7 [misc-const-correctness]

variable 'type' of type 'Type' can be declared 'const'
newArg.type = type;
blockArgs.push_back(newArg);

} while (succeeded(parser.parseOptionalComma()));

if (parser.parseRParen()) {
return failure();
}
}

// 4. Parse the Region
// We explicitly pass the blockArgs we just parsed so they become the entry
// block!
if (parser.parseRegion(region, blockArgs)) {
return failure();
}

return success();
}

void printTargetAliasing(OpAsmPrinter& printer, Operation* op, Region& region,

Check warning on line 88 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:88:60 [misc-unused-parameters]

parameter 'op' is unused

Check warning on line 88 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:88:49 [misc-include-cleaner]

no header providing "mlir::Operation" is directly included

Check warning on line 88 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:88:6 [llvm-prefer-static-over-anonymous-namespace]

function 'printTargetAliasing' is declared in an anonymous namespace; prefer using 'static' for restricting visibility
OperandRange targets_in) {

Check warning on line 89 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:89:39 [readability-identifier-naming]

invalid case style for parameter 'targets_in'

Check warning on line 89 in mlir/lib/Dialect/QCO/IR/QCOOps.cpp

View workflow job for this annotation

GitHub Actions / 🇨‌ Lint / 🚨 Lint

mlir/lib/Dialect/QCO/IR/QCOOps.cpp:89:26 [misc-include-cleaner]

no header providing "mlir::OperandRange" is directly included
printer << "(";
Block& entryBlock = region.front();
auto blockArgs = entryBlock.getArguments();

for (unsigned i = 0; i < targets_in.size(); ++i) {
if (i > 0) {
printer << ", ";
}
printer.printOperand(blockArgs[i]);
printer << " = ";
printer.printOperand(targets_in[i]);
}
printer << ") ";

printer.printRegion(region, /*printEntryBlockArgs=*/false);
}
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Dialect
//===----------------------------------------------------------------------===//
Expand Down
Loading