Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
89f8ee3
feat(mlir): :sparkles: add pass and patterns for measurement lifting
DRovara May 13, 2026
6d4332d
test(mlir/hybrid-opt): :construction: set up tests for measurement li…
DRovara Jun 1, 2026
1a7664c
fix(mlir): :white_check_mark: fix tests
DRovara Jun 2, 2026
770e55c
style(mlir): :rotating_light: fix linter issues
DRovara Jun 2, 2026
64b162c
style(mlir): :recycle: implement coderabbit suggestions
DRovara Jun 2, 2026
137d93c
fix(mlir): :bug: fix all measurement lifting tests
DRovara Jun 2, 2026
559c9ff
style(mlir): :rotating_light: fix linter issues
DRovara Jun 2, 2026
679f454
style(mlir): :recycle: minor code clean-up
DRovara Jun 2, 2026
5510fe8
style(mlir): :rotating_light: fix linter issue regarding unused include
DRovara Jun 2, 2026
64cffab
🎨 pre-commit fixes
pre-commit-ci[bot] Jul 14, 2026
df2301b
chore(mlir): :pencil2: clean up minor typo
DRovara Jul 14, 2026
8ca81d6
fix(mlir): :pencil2: fix some further typos
DRovara Jul 14, 2026
c55987b
Merge branch 'mlir/measurement-lifting' of github.com:munich-quantum-…
DRovara Jul 14, 2026
653a8f4
test(mlir): :white_check_mark: add further special cases to tests
DRovara Jul 14, 2026
1611c1b
chore(mlir): :recycle: add docstrings to new tests
DRovara Jul 14, 2026
4a158fb
test(mlir): :white_check_mark: add test for inverse gates
DRovara Jul 14, 2026
4d98606
chore(mlir): :recycle: slight refactoring in tests
DRovara Jul 14, 2026
2deb2a4
chore(mlir): :recycle: further tests clean up
DRovara Jul 14, 2026
096075c
fix(mlir): :recycle: address coderabbit issues
DRovara Jul 14, 2026
b1a24cd
chore(mlir): :recycle: some further coderabbit suggestions
DRovara Jul 14, 2026
b0d060d
feat(mlir): :sparkles: implement classical control replacement patter…
DRovara Jun 2, 2026
7042e3b
style(mlir): :rotating_light: fix includes
DRovara Jun 2, 2026
58d10b9
style(mlir): :recycle: apply coderabbit suggestions
DRovara Jun 3, 2026
febb216
fix(mlir): :bug: fix issues due to new version
DRovara Jul 14, 2026
6d4ce73
docs(mlir): :memo: add to changelog
DRovara Jul 14, 2026
38bbc15
Merge branch 'mlir/measurement-lifting' into mlir/replace-classical-c…
DRovara Jul 14, 2026
5e482a9
docs(mlir): :memo: add to changelog
DRovara Jul 14, 2026
70de05f
Merge branch 'main' into mlir/measurement-lifting
DRovara Jul 15, 2026
102663b
Merge branch 'main' into mlir/measurement-lifting
DRovara Jul 16, 2026
91fe289
chore(mlir): :recycle: refactor `isDiagonal` check to use `getSoleBod…
DRovara Jul 16, 2026
317ebfd
chore(mlir): :recycle: remove unneeded check
DRovara Jul 17, 2026
d392185
chore(mlir): :recycle: combine lifting of measurements
DRovara Jul 17, 2026
9253715
docs(mlir): :memo: fix up changelog
DRovara Jul 17, 2026
10b0f89
fix(mlir): :rotating_light: fix linter issues
DRovara Jul 17, 2026
2c602ab
Merge branch 'mlir/measurement-lifting' into mlir/replace-classical-c…
DRovara Jul 17, 2026
8955d72
chore(mlir): :recycle: clean up tests
DRovara Jul 17, 2026
d1e452a
refactor(mlir): :recycle: small changes to test style
DRovara Jul 17, 2026
03954f9
Merge branch 'main' into mlir/replace-classical-controls
DRovara Jul 20, 2026
0e9dd2f
style(mlir): :recycle: rewrite `isPhaseGate` check
DRovara Jul 20, 2026
54eb424
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 20, 2026
ce1382a
feat(mlir): :sparkles: starting implementation of qubit reuse
DRovara Jul 21, 2026
d0332ee
chore(mlir): :recycle: remove unneeded "created by damian" headers
DRovara Jul 21, 2026
e54135a
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 21, 2026
3e5d088
chore(mlir): :recycle: remove unneeded "created by damian" header
DRovara Jul 21, 2026
af18628
docs(mlir): :memo: add PR to changelog
DRovara Jul 21, 2026
48d6b07
chore(mlir): :recycle: add docstrings to tests
DRovara Jul 21, 2026
e9dc05e
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 21, 2026
9c68990
feat(mlir): :sparkles: also apply dead gate elimination for ResetOps
DRovara Jul 21, 2026
9059d90
Merge branch 'mlir/dead-gate-elimination-resetop' into mlir/qubit-reuse
DRovara Jul 21, 2026
e434323
docs(mlir): :memo: add documentation
DRovara Jul 21, 2026
84d6288
Merge branch 'mlir/dead-gate-elimination-resetop' into mlir/qubit-reuse
DRovara Jul 21, 2026
8b23850
chore(mlir): :recycle: fix linter issues
DRovara Jul 21, 2026
8679a54
test(mlir): :white_check_mark: add measurements to other unrelated re…
DRovara Jul 21, 2026
86f1e6f
chore(mlir): :recycle: make pass target MeasureOps instead for effici…
DRovara Jul 21, 2026
7fd6d0a
chore(mlir): :recycle: remove check for InvOps
DRovara Jul 21, 2026
65c913e
feat(mlir): :sparkles: cleaner implementation for `trySwapControlsOfD…
DRovara Jul 21, 2026
080d946
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 21, 2026
c6b3b4e
fix(mlir): :white_check_mark: fix tests that were broken by last changes
DRovara Jul 21, 2026
b1a677d
Merge branch 'mlir/dead-gate-elimination-resetop' into mlir/qubit-reuse
DRovara Jul 21, 2026
a2be2c6
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 21, 2026
adced7b
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 21, 2026
83a7141
Merge branch 'main' into mlir/replace-classical-controls
DRovara Jul 22, 2026
52248ad
Merge branch 'main' into mlir/dead-gate-elimination-resetop
DRovara Jul 22, 2026
6f16166
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 22, 2026
ae6e9e0
Merge branch 'mlir/dead-gate-elimination-resetop' into mlir/qubit-reuse
DRovara Jul 22, 2026
eb195d4
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 22, 2026
316adb0
test(mlir): :recycle: add some tests
DRovara Jul 22, 2026
e48b036
chore(mlir): :recycle: apply review comments
DRovara Jul 22, 2026
49d9a41
chore(mlir): :rotating_light: fix linter issue
DRovara Jul 22, 2026
ac46ece
chore(mlir): :recycle: cleaner implementation for main pattern
DRovara Jul 22, 2026
495043b
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 22, 2026
8621ce0
test(mlir): :white_check_mark: remaining tests from old implementation
DRovara Jul 22, 2026
b365d7b
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 22, 2026
aed55b4
Merge branch 'mlir/replace-classical-controls' into mlir/qubit-reuse
DRovara Jul 22, 2026
26d39bd
chore(mlir): :recycle: use `getForwardSlice` method to find reachable…
DRovara Jul 28, 2026
0dd4d63
Merge branch 'main' into mlir/qubit-reuse
DRovara Jul 28, 2026
29b46cd
fix(mlir): :bug: fix error introduced by merge
DRovara Jul 28, 2026
0926619
feat(mlir): :sparkles: add qubit reuse to `mqt-cc` passes
DRovara Jul 28, 2026
86b46a1
chore(mlir): :recycle: clean up code
DRovara Jul 28, 2026
b1a0f81
fix(mlir): :bug: fix tests
DRovara Jul 28, 2026
59f08c4
fix(mlir): :recycle: fix linter issues
DRovara Jul 28, 2026
f120dee
chore(mlir): :recycle: apply coderabbit suggestions
DRovara Jul 28, 2026
93dd0b7
chore(mlir): :recycle: rewrite BFS implementation for more efficient …
DRovara Jul 28, 2026
faf464b
chore(mlir): :rotating_light: fix linter issues
DRovara Jul 28, 2026
d8becff
chore(mlir): :recycle: remove arith from dependent dialects for qubit…
DRovara Jul 28, 2026
ac6bf38
feat(mlir): :sparkles: translate size-1 OpenQASM Qubits into single q…
DRovara Jul 29, 2026
007ac4a
Merge branch 'main' into mlir/qubit-reuse
DRovara Jul 29, 2026
1db4a0f
feat(mlir): :sparkles: update qasm parsing so that the single `qubit`…
DRovara Jul 29, 2026
e42e016
Merge branch 'mlir/qubit-reuse' of github.com:munich-quantum-toolkit/…
DRovara Jul 29, 2026
f4884d6
Merge branch 'main' into mlir/qubit-reuse
DRovara Jul 29, 2026
94ad151
feat(mlir): :recycle: update quantum computation OpenQASM importer
DRovara Jul 29, 2026
edf23d8
Merge branch 'mlir/qubit-reuse' of github.com:munich-quantum-toolkit/…
DRovara Jul 29, 2026
9929601
chore(mlir): :recycle: apply coderabbit suggestions
DRovara Jul 29, 2026
86e0396
Merge branch 'main' into mlir/qubit-reuse
DRovara Jul 29, 2026
fd9b78c
chore(mlir): :recycle: apply coderabbit suggestion
DRovara Jul 29, 2026
facd700
Merge branch 'mlir/qubit-reuse' of github.com:munich-quantum-toolkit/…
DRovara Jul 29, 2026
bcb9c25
chore(mlir): :recycle: use builtin MLIR function to find ancestor
DRovara Jul 30, 2026
d58d93b
Merge branch 'main' into mlir/qubit-reuse
DRovara Jul 30, 2026
f8882f9
feat(mlir): :sparkles: add inliner interface to QCO
DRovara Jul 30, 2026
e558d11
chore(mlir): :recycle: remove `mlir::qco` specifiers
DRovara Jul 30, 2026
4aca1d5
git pushMerge branch 'mlir/qubit-reuse' of github.com:munich-quantum-…
DRovara Jul 30, 2026
21c7b0f
Merge branch 'mlir/qubit-reuse' into mlir/ipo-pass
DRovara Jul 30, 2026
014b2a0
feat(mlir): :sparkles: add IPO pass
DRovara Jul 30, 2026
f74f018
test(mlir): :white_check_mark: add tests for Quantum IPO
DRovara Jul 31, 2026
4c42236
Merge branch 'main' into mlir/ipo-pass
DRovara Jul 31, 2026
8684556
🎨 pre-commit fixes
pre-commit-ci[bot] Jul 31, 2026
8f31dce
chore(mlir): :sparkles: rewrite mlir::tensors into qtensors
DRovara Jul 31, 2026
3d0d086
Merge branch 'mlir/ipo-pass' of github.com:munich-quantum-toolkit/cor…
DRovara Jul 31, 2026
69e3928
Merge branch 'main' into mlir/ipo-pass
DRovara Aug 3, 2026
5aed242
🎨 pre-commit fixes
pre-commit-ci[bot] Aug 3, 2026
cbcbb1f
Merge branch 'mlir/ipo-pass' of github.com:munich-quantum-toolkit/cor…
DRovara Aug 3, 2026
41093d7
fix(mlir): :pencil2: fix typo in passes.td
DRovara Aug 3, 2026
cd2940d
chore(mlir): :rotating_light: fix linter issues
DRovara Aug 3, 2026
785d1f4
fix(mlir): :pencil2: fix incorrect merges
DRovara Aug 3, 2026
cfc98b4
chore(mlir): :rotating_light: fix linter issues
DRovara Aug 3, 2026
eeedb33
test(mlir): :white_check_mark: improve test coverage
DRovara Aug 6, 2026
9233f94
Merge branch 'main' into mlir/ipo-pass
DRovara Aug 6, 2026
0c751f3
🎨 pre-commit fixes
pre-commit-ci[bot] Aug 6, 2026
d6c515b
test(mlir): :white_check_mark: add new tests for QCOProgramBuilder an…
DRovara Aug 7, 2026
7ea2b70
Merge branch 'mlir/ipo-pass' of github.com:munich-quantum-toolkit/cor…
DRovara Aug 7, 2026
306a709
chore(mlir): :rotating_light: fix linter issue
DRovara Aug 7, 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
7 changes: 6 additions & 1 deletion include/mqt-core/qasm3/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ template <typename T> class DesignatedType final : public Type<T> {
std::string designatorToString();
};

enum UnsizedTy : uint8_t { Bool, Duration };
enum UnsizedTy : uint8_t { Bool, Duration, SingleQubit };

template <typename T> class UnsizedType final : public Type<T> {
public:
Expand All @@ -178,6 +178,9 @@ template <typename T> class UnsizedType final : public Type<T> {
static std::shared_ptr<Type<T>> getDurationTy() {
return std::make_shared<UnsizedType>(Duration);
}
static std::shared_ptr<Type<T>> getSingleQubitTy() {
return std::make_shared<UnsizedType>(SingleQubit);
}

T getDesignator() override {
throw std::runtime_error("Unsized types do not have designators");
Expand All @@ -195,6 +198,8 @@ template <typename T> class UnsizedType final : public Type<T> {
return "bool";
case Duration:
return "duration";
case SingleQubit:
return "qubit";
}
throw std::runtime_error("Unhandled type");
}
Expand Down
120 changes: 120 additions & 0 deletions mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@

#pragma once

#include <llvm/ADT/DenseSet.h>
#include <mlir/IR/Builders.h>
#include <mlir/IR/OwningOpRef.h>
#include <mlir/IR/Value.h>
#include <mlir/Support/LLVM.h>

#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <variant>
Expand Down Expand Up @@ -1755,6 +1757,109 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {
const std::variant<int64_t, Value>& index,
ValueRange yieldedValues);

//===--------------------------------------------------------------------===//
// Additional functions
//===--------------------------------------------------------------------===//

/**
* @brief Start building an additional private function in the module
*
* @details
* Creates a new private `func.func` at the end of the module and moves the
* insertion point into its entry block. Qubit- and qubit-tensor-typed
* arguments are added to the linear-type tracking so that operations can be
* applied to them; a tensor argument is treated as a register the callee
* owns for the duration of the call. Arguments of other types (e.g., `f64`)
* are returned as-is without tracking. Must be called after `initialize()`
* and must be paired with a call to `endFunction()`; function definitions
* cannot be nested.
*
* @param name The name of the function
* @param argTypes The argument types of the function
* @param resultTypes The result types of the function
* @return The entry block arguments of the new function
*
* @par Example:
* ```c++
* auto args = builder.startFunction(
* "f", {builder.getQubitType()}, {builder.getQubitType()});
* auto q = builder.h(args[0]);
* builder.endFunction({q});
* ```
* ```mlir
* func.func private @f(%arg0: !qco.qubit) -> !qco.qubit {
* %q = qco.h %arg0 : !qco.qubit -> !qco.qubit
* return %q : !qco.qubit
* }
* ```
*/
SmallVector<Value> startFunction(StringRef name, TypeRange argTypes,
TypeRange resultTypes);

/**
* @brief Finish the function started with `startFunction()`
*
* @details
* Creates the `func.return` with the given values and restores the insertion
* point to where it was before `startFunction()` was called. All qubits and
* tensors that were created within the function (from arguments or
* operations) must either be consumed (e.g., by `sink()` or
* `qtensorDealloc()`) or returned; otherwise a usage error is reported.
*
* @param returnValues The values to return from the function
*/
void endFunction(ValueRange returnValues);

/**
* @brief Call a function previously defined in the module
*
* @details
* Creates a `func.call` to the named function. Qubit- and qubit-tensor-typed
* operands are validated and consumed; results of those types are added to
* the tracking.
*
* The i-th qubit operand is paired with the i-th qubit result, and likewise
* for tensors, so a function that threads its linear values through keeps
* the register association intact. Surplus operands are treated as consumed
* and surplus results as freshly created. This positional pairing is a
* calling convention, not something the IR enforces: a callee that returns
* its qubits in a different order than it takes them will be tracked
* incorrectly.
*
* @param callee The name of the function to call
* @param operands The operands to pass to the call
* @return The results of the call operation
*
* @par Example:
* ```c++
* auto results = builder.call("f", {q0});
* ```
* ```mlir
* %q1 = call @f(%q0) : (!qco.qubit) -> !qco.qubit
* ```
*/
SmallVector<Value> call(StringRef callee, ValueRange operands);

/**
* @brief Get the qubit type
* @return The `!qco.qubit` type
*/
Type getQubitType();

/**
* @brief Get a one-dimensional tensor type holding qubits
* @param size The number of qubits in the tensor
* @return The `tensor<size x !qco.qubit>` type
*/
Type getQubitTensorType(int64_t size);

/**
* @brief Check whether the given type is a tensor of qubits
* @param type The type to check
* @return True if @p type is a ranked tensor with `!qco.qubit` elements
*/
static bool isQubitTensor(Type type);

//===--------------------------------------------------------------------===//
// Finalization
//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -1943,6 +2048,21 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {

/// Ensure static and dynamic qubit allocation modes are not mixed.
void ensureAllocationMode(AllocationMode requestedMode);

/**
* @brief State of an additional function under construction
*/
struct FunctionScope {
/// Insertion point to restore when the function is finished
OpBuilder::InsertPoint savedInsertPoint;
/// Qubit values that were already tracked before the function was started
llvm::DenseSet<Value> outerQubits;
/// Tensor values that were already tracked before the function was started
llvm::DenseSet<Value> outerTensors;
};

/// Active function scope, if a function is currently under construction.
std::optional<FunctionScope> functionScope;
};
} // namespace qco
} // namespace mlir
5 changes: 5 additions & 0 deletions mlir/include/mlir/Dialect/QCO/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#pragma once

#include <mlir/IR/BuiltinOps.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Show the include block of the QCO Transforms Passes.h header.
fd -p 'mlir/include/mlir/Dialect/QCO/Transforms/Passes.h' --exec sed -n '1,45p' {}

Repository: munich-quantum-toolkit/core

Length of output: 1448


Include mlir/IR/SymbolTable.h for SymbolTable.

Passes.h now declares SymbolTable& symbolTable, so the header must provide that declaration.

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

In `@mlir/include/mlir/Dialect/QCO/Transforms/Passes.h` at line 13, Add the
mlir/IR/SymbolTable.h include to Passes.h so the SymbolTable reference
declaration is available from the header.

#include <mlir/Interfaces/FunctionInterfaces.h>
#include <mlir/Pass/Pass.h>
#include <mlir/Pass/PassRegistry.h>
Expand All @@ -33,6 +34,10 @@ namespace mlir::qco {
#define GEN_PASS_REGISTRATION
#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" // IWYU pragma: export

void runQuantumArgumentPromotion(ModuleOp module);
void runAuxiliaryQubitHoisting(ModuleOp module);
void runQuantumFunctionBoundaryCommutation(ModuleOp module,
SymbolTable& symbolTable);
Comment on lines +37 to +40

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

mlir::ModuleOp parameters are named module across the new IPO surface. The coding guidelines forbid module as a C++ variable or parameter name and require moduleOp. The new declarations and their definitions all carry the forbidden name, so the rename must stay consistent between the header and every definition.

  • mlir/include/mlir/Dialect/QCO/Transforms/Passes.h#L37-L40: rename the ModuleOp module parameter to moduleOp in all three declarations.
  • mlir/lib/Dialect/QCO/Transforms/Optimizations/AuxiliaryQubitHoisting.cpp#L262-L262: rename the ModuleOp module parameter of runAuxiliaryQubitHoisting to moduleOp and update its uses at lines 264, 266, 279, and 280.
  • mlir/lib/Dialect/QCO/Transforms/Optimizations/QuantumFunctionBoundaryCommutation.cpp#L102-L102: rename the ModuleOp module parameter of runQuantumFunctionBoundaryCommutation to moduleOp and update its use at line 105.
  • mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_quantum_ipo.cpp#L63-L77: rename the ModuleOp module parameters of runQuantumIPOPass and runCanonicalizerPass to moduleOp, which also removes the shadowing of the module fixture member at line 44.

mlir/lib/Dialect/QCO/Transforms/Optimizations/QuantumArgumentPromotion.cpp at line 315 declares runQuantumArgumentPromotion(ModuleOp module) with the same name; apply the rename there as well.

Based on coding guidelines: "Do not use module as a C++ variable or parameter name; use moduleOp for mlir::ModuleOp values."

📍 Affects 4 files
  • mlir/include/mlir/Dialect/QCO/Transforms/Passes.h#L37-L40 (this comment)
  • mlir/lib/Dialect/QCO/Transforms/Optimizations/AuxiliaryQubitHoisting.cpp#L262-L262
  • mlir/lib/Dialect/QCO/Transforms/Optimizations/QuantumFunctionBoundaryCommutation.cpp#L102-L102
  • mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_quantum_ipo.cpp#L63-L77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/include/mlir/Dialect/QCO/Transforms/Passes.h` around lines 37 - 40,
Rename all ModuleOp parameters named module to moduleOp and update every
corresponding use: the three declarations in
mlir/include/mlir/Dialect/QCO/Transforms/Passes.h#L37-L40;
runAuxiliaryQubitHoisting and its uses in
mlir/lib/Dialect/QCO/Transforms/Optimizations/AuxiliaryQubitHoisting.cpp#L262-L280;
runQuantumFunctionBoundaryCommutation and its use in
mlir/lib/Dialect/QCO/Transforms/Optimizations/QuantumFunctionBoundaryCommutation.cpp#L102-L105;
runQuantumIPOPass and runCanonicalizerPass in
mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_quantum_ipo.cpp#L63-L77;
and runQuantumArgumentPromotion in
mlir/lib/Dialect/QCO/Transforms/Optimizations/QuantumArgumentPromotion.cpp#L315.
Keep declarations, definitions, and call behavior consistent while eliminating
shadowing of the module fixture.

Source: Coding guidelines

/**
* @brief Create target-independent two-qubit gate fusion.
*/
Expand Down
12 changes: 12 additions & 0 deletions mlir/include/mlir/Dialect/QCO/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@ def ReuseQubits : Pass<"reuse-qubits", "mlir::ModuleOp"> {
}];
}

def QuantumIPO : Pass<"quantum-ipo", "mlir::ModuleOp"> {
let summary = "Quantum Interprocedural Optimization Pass";
let description = [{
Performs interprocedural optimizations on quantum functions.
}];
Comment on lines +309 to +312

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Expand the QuantumIPO description to match its actual scope.

The description is one generic sentence. The pass runs four distinct transformations: context-sensitive call specialization, quantum argument promotion, auxiliary-qubit hoisting, and function-boundary commutation. It also has real limitations that a user cannot discover from this text:

  • It skips public functions and declarations.
  • It skips recursive functions.
  • It relies on the QCO calling convention that the i-th qubit result of a call corresponds to the i-th qubit operand.
  • It runs runQuantumFunctionBoundaryCommutation a fixed two times rather than to a fixed point.

Every neighbouring pass in this file documents scope and limitations at this level. Also align the description indentation with the surrounding passes.

As per coding guidelines: "When changing MLIR passes, pipelines, or command-line options, align summaries and descriptions with actual scope, defaults, supported operation shapes, limitations, failure modes, and out-of-scope behavior."

📝 Proposed description skeleton
 def QuantumIPO : Pass<"quantum-ipo", "mlir::ModuleOp"> {
   let summary = "Quantum Interprocedural Optimization Pass";
   let description = [{
-        Performs interprocedural optimizations on quantum functions.
-    }];
+    Performs interprocedural optimizations across `func.func` boundaries by
+    running, in order:
+
+    - Context-sensitive specialization of `func.call` sites for recognized
+      qubit states and constant angles.
+    - Quantum argument promotion, turning eligible qubit-tensor arguments into
+      scalar qubit arguments.
+    - Auxiliary-qubit hoisting, moving callee-internal allocations to the
+      caller.
+    - Quantum function-boundary commutation, removing matching self-inverse
+      gates across a call.
+
+    Limitations: public functions and declarations are not transformed;
+    recursive functions are skipped; the pass relies on the QCO calling
+    convention that the i-th qubit result of a call corresponds to its i-th
+    qubit operand; boundary commutation runs a fixed number of iterations
+    rather than to a fixed point.
+  }];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let summary = "Quantum Interprocedural Optimization Pass";
let description = [{
Performs interprocedural optimizations on quantum functions.
}];
let summary = "Quantum Interprocedural Optimization Pass";
let description = [{
Performs interprocedural optimizations across `func.func` boundaries by
running, in order:
- Context-sensitive specialization of `func.call` sites for recognized
qubit states and constant angles.
- Quantum argument promotion, turning eligible qubit-tensor arguments into
scalar qubit arguments.
- Auxiliary-qubit hoisting, moving callee-internal allocations to the
caller.
- Quantum function-boundary commutation, removing matching self-inverse
gates across a call.
Limitations: public functions and declarations are not transformed;
recursive functions are skipped; the pass relies on the QCO calling
convention that the i-th qubit result of a call corresponds to its i-th
qubit operand; boundary commutation runs a fixed number of iterations
rather than to a fixed point.
}];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/include/mlir/Dialect/QCO/Transforms/Passes.td` around lines 309 - 312,
Expand the `QuantumIPO` `description` to document its four transformations:
context-sensitive call specialization, quantum argument promotion,
auxiliary-qubit hoisting, and function-boundary commutation. Include that it
skips public functions, declarations, and recursive functions; depends on the
QCO convention matching each qubit result to the corresponding operand; and runs
`runQuantumFunctionBoundaryCommutation` exactly two times rather than to a fixed
point. Align the description indentation with neighboring pass definitions.

Source: Coding guidelines


let dependentDialects = ["::mlir::func::FuncDialect",
"::mlir::arith::ArithDialect",
"::mlir::qtensor::QTensorDialect",
"mlir::qco::QCODialect"];
}

//===----------------------------------------------------------------------===//
// Decomposition Passes
//===----------------------------------------------------------------------===//
Expand Down
167 changes: 167 additions & 0 deletions mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
#include <mlir/IR/Location.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/IR/OwningOpRef.h>
#include <mlir/IR/SymbolTable.h>
#include <mlir/IR/Value.h>
#include <mlir/IR/ValueRange.h>
#include <mlir/Support/LLVM.h>

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -1374,6 +1376,171 @@ QCOProgramBuilder::scfCondition(Value reg,
return scfCondition(condition, yieldedValues);
}

//===----------------------------------------------------------------------===//
// Additional Functions
//===----------------------------------------------------------------------===//

Type QCOProgramBuilder::getQubitType() { return QubitType::get(ctx); }

Type QCOProgramBuilder::getQubitTensorType(const int64_t size) {
return RankedTensorType::get({size}, getQubitType());
}
Comment on lines +1385 to +1387

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Reject non-positive sizes in getQubitTensorType.

allocQubitRegister and allocClassicalBitRegister both reject size <= 0 with llvm::reportFatalUsageError. getQubitTensorType accepts any value. A negative size produces an invalid static dimension and triggers an MLIR assertion far away from the call site.

Add the same guard so the failure points at the builder call.

🛡️ Proposed guard
 Type QCOProgramBuilder::getQubitTensorType(const int64_t size) {
+  if (size <= 0) {
+    llvm::reportFatalUsageError("Size must be positive");
+  }
   return RankedTensorType::get({size}, getQubitType());
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Type QCOProgramBuilder::getQubitTensorType(const int64_t size) {
return RankedTensorType::get({size}, getQubitType());
}
Type QCOProgramBuilder::getQubitTensorType(const int64_t size) {
if (size <= 0) {
llvm::reportFatalUsageError("Size must be positive");
}
return RankedTensorType::get({size}, getQubitType());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp` around lines 1385 - 1387,
Update QCOProgramBuilder::getQubitTensorType to reject size <= 0 with
llvm::reportFatalUsageError, matching allocQubitRegister and
allocClassicalBitRegister, before constructing the RankedTensorType.


bool QCOProgramBuilder::isQubitTensor(Type type) {
const auto tensorType = dyn_cast<RankedTensorType>(type);
return tensorType && isa<QubitType>(tensorType.getElementType());
}

SmallVector<Value> QCOProgramBuilder::startFunction(StringRef name,
TypeRange argTypes,
TypeRange resultTypes) {
checkFinalized();

if (functionScope.has_value()) {
llvm::reportFatalUsageError(
"Cannot start a function while another one is being built");
}

FunctionScope scope{.savedInsertPoint = saveInsertionPoint(),
.outerQubits = {},
.outerTensors = {}};
for (const auto& [qubit, info] : validQubits) {
scope.outerQubits.insert(qubit);
}
for (const auto& [tensor, info] : validTensors) {
scope.outerTensors.insert(tensor);
}

setInsertionPointToEnd(cast<ModuleOp>(module).getBody());
auto funcOp =
func::FuncOp::create(*this, name, getFunctionType(argTypes, resultTypes));
// The interprocedural passes only consider functions that are not externally
// visible, so additional functions are private by default.
funcOp.setPrivate();
Comment on lines +1414 to +1419

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect startFunction callers and any existing symbol-uniqueness handling.
set -euo pipefail

# Find all startFunction call sites.
rg -nP -C4 '\bstartFunction\s*\(' --type=cpp --type=cc

# Check whether SymbolTable::insert is used anywhere in the QCO builder.
fd -t f 'QCOProgramBuilder.cpp' | xargs rg -nP -C3 'SymbolTable::(insert|lookupSymbolIn)'

Repository: munich-quantum-toolkit/core

Length of output: 196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== QCOProgramBuilder outline =="
ast-grep outline mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp --match startFunction --view expanded || true

echo "== startFunction definition =="
python3 - <<'PY'
from pathlib import Path
p=Path('mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp')
lines=p.read_text().splitlines()
for i,l in enumerate(lines):
    if 'startFunction' in lines[max(0,i-2):i+1]:
        start=max(0,i-18)
        end=min(len(lines),i+80)
        print(f'-- lines {start+1}-{end} --')
        for n in range(start,end):
            print(f'{n+1:5}: {lines[n]}')
        print()
PY

echo "== all startFunction call sites =="
rg -nP -C4 '\bstartFunction\s*\(' -g '*.{cc,cpp,cxx,h,hpp,hh}'

echo "== SymbolTable usage in QCOProgramBuilder =="
rg -nP -C3 'SymbolTable::(insert|lookupSymbolIn)' mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp

echo "== uses of func::FuncOp::create =="
rg -nP -C2 'func::FuncOp::create|FuncOp::create' -g '*.{cc,cpp,cxx,h,hpp,hh}'

Repository: munich-quantum-toolkit/core

Length of output: 388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== startFunction definition =="
python3 - <<'PY'
from pathlib import Path
p=Path('mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp')
lines=p.read_text().splitlines()
for i,l in enumerate(lines):
    if 'SmallVector<Value> QCOProgramBuilder::startFunction(StringRef name,' in l:
        start=max(0,i-15)
        end=min(len(lines),i+100)
        print(f'-- lines {start+1}-{end} --')
        for n in range(start,end):
            print(f'{n+1:5}: {lines[n]}')
        print()
PY

echo "== all startFunction call sites =="
rg -nP -C4 '\bstartFunction\s*\(' -g '*.cc' -g '*.cpp' -g '*.cxx' -g '*.h' -g '*.hpp' -g '*.hh' .

echo "== SymbolTable usage in QCOProgramBuilder =="
rg -nP -C3 'SymbolTable::(insert|lookupSymbolIn)' mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp

echo "== uses of func::FuncOp::create =="
rg -nP -C2 'func::FuncOp::create|FuncOp::create' -g '*.cc' -g '*.cpp' -g '*.cxx' -g '*.h' -g '*.hpp' -g '*.hh' .

Repository: munich-quantum-toolkit/core

Length of output: 50386


🌐 Web query:

MLIR func FuncOp create SymbolTable insert SymbolTable::lookupSymbolIn

💡 Result:

In MLIR, a SymbolTable manages operations that define symbols (e.g., func.func) [1][2]. To work with these symbols, you use the mlir::SymbolTable class and its associated static utility methods [3][4]. Creating and Managing a SymbolTable Operations that function as containers for symbols must implement the OpTrait::SymbolTable trait [1][2]. You can create an instance of the SymbolTable class by passing the operation that holds the symbols to its constructor [5][6]: mlir::SymbolTable symbolTable(symbolTableOp); Once the object is created, you can insert new symbol operations into the container [6]: symbolTable.insert(newSymbolOp); Looking Up Symbols The mlir::SymbolTable class provides static utility methods to look up symbols within an operation that has the SymbolTable trait [3][4]. These methods do not require you to explicitly instantiate a SymbolTable object [4][7]. The primary method is lookupSymbolIn, which returns the Operation* matching a given symbol name [3][4]. It is overloaded to accept different types of symbol identifiers [4]: Operation *op = mlir::SymbolTable::lookupSymbolIn(symbolTableOp, StringAttr::get(context, "symbol_name")); Operation *op = mlir::SymbolTable::lookupSymbolIn(symbolTableOp, "symbol_name"); Operation *op = mlir::SymbolTable::lookupSymbolIn(symbolTableOp, symbolRefAttr); Key Points: - Container Requirements: The container operation passed to lookupSymbolIn or the SymbolTable constructor must possess the OpTrait::SymbolTable trait [3][4][2]. - Symbol Requirements: The operations being looked up must satisfy the Symbol trait (e.g., having a sym_name attribute) [1][2]. - Efficiency: lookupSymbolIn is designed to efficiently resolve symbols within the region of the specified symbol table operation [1][6]. - SymbolRefAttr: If you need to resolve complex or nested symbol references (e.g., path-like references), use the version of lookupSymbolIn that accepts a SymbolRefAttr [4][8].

Citations:


Reject duplicate function names in startFunction.

func::FuncOp::create creates the function operation at the insertion point and does not enforce module symbol uniqueness. A second startFunction("f", ...) therefore leaves module-level symbol ambiguity for call("f", ...), since SymbolTable::lookupSymbolIn returns one matching symbol. Add an eager check using SymbolTable::lookupSymbolIn(module, name) before creating the funcOp.

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

In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp` around lines 1414 - 1419,
In startFunction, check SymbolTable::lookupSymbolIn(module, name) before
func::FuncOp::create and reject the operation when a function with the same name
already exists. Preserve the existing insertion point, function creation, and
private visibility behavior for unique names.


auto& entryBlock = funcOp.getBody().emplaceBlock();
const SmallVector<Location> locs(argTypes.size(), getLoc());
entryBlock.addArguments(argTypes, locs);
setInsertionPointToStart(&entryBlock);

SmallVector<Value> args;
for (const auto arg : entryBlock.getArguments()) {
if (isa<QubitType>(arg.getType())) {
validQubits.try_emplace(arg, QubitInfo{});
} else if (isQubitTensor(arg.getType())) {
// A tensor argument acts like a register the callee owns for the
// duration of the call, so give it its own register id.
validTensors.try_emplace(arg, TensorInfo{tensorCounter++});
}
args.emplace_back(arg);
}

functionScope = std::move(scope);
return args;
}

void QCOProgramBuilder::endFunction(ValueRange returnValues) {
checkFinalized();

if (!functionScope.has_value()) {
llvm::reportFatalUsageError(
"endFunction() called without a matching startFunction()");
}

for (const auto value : returnValues) {
if (isa<QubitType>(value.getType())) {
validateQubitValue(value);
validQubits.erase(value);
} else if (isQubitTensor(value.getType())) {
validateTensorValue(value);
validTensors.erase(value);
}
}

for (const auto& [qubit, info] : validQubits) {
if (!functionScope->outerQubits.contains(qubit)) {
llvm::reportFatalUsageError(
"Function body has qubit values that are neither returned nor "
"consumed");
}
}
for (const auto& [tensor, info] : validTensors) {
if (!functionScope->outerTensors.contains(tensor)) {
llvm::reportFatalUsageError(
"Function body has tensor values that are neither returned nor "
"deallocated");
}
}

func::ReturnOp::create(*this, returnValues);
Comment on lines +1450 to +1475

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Validate the return values against the declared result types.

endFunction does not compare the types of returnValues with the resultTypes passed to startFunction. On a mismatch, func::ReturnOp is created anyway and the failure only surfaces when the module is verified, after the builder has already restored the insertion point. The reported location then points at the return operation, not at the endFunction call.

The rest of this API reports misuse eagerly with llvm::reportFatalUsageError. Add the same check for arity and types.

🛡️ Proposed check
 void QCOProgramBuilder::endFunction(ValueRange returnValues) {
   checkFinalized();
 
   if (!functionScope.has_value()) {
     llvm::reportFatalUsageError(
         "endFunction() called without a matching startFunction()");
   }
 
+  auto funcOp = cast<func::FuncOp>(getInsertionBlock()->getParentOp());
+  if (!llvm::equal(returnValues.getTypes(), funcOp.getResultTypes())) {
+    llvm::reportFatalUsageError(
+        "Return values do not match the declared function result types");
+  }
+
   for (const auto value : returnValues) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const auto value : returnValues) {
if (isa<QubitType>(value.getType())) {
validateQubitValue(value);
validQubits.erase(value);
} else if (isQubitTensor(value.getType())) {
validateTensorValue(value);
validTensors.erase(value);
}
}
for (const auto& [qubit, info] : validQubits) {
if (!functionScope->outerQubits.contains(qubit)) {
llvm::reportFatalUsageError(
"Function body has qubit values that are neither returned nor "
"consumed");
}
}
for (const auto& [tensor, info] : validTensors) {
if (!functionScope->outerTensors.contains(tensor)) {
llvm::reportFatalUsageError(
"Function body has tensor values that are neither returned nor "
"deallocated");
}
}
func::ReturnOp::create(*this, returnValues);
auto funcOp = cast<func::FuncOp>(getInsertionBlock()->getParentOp());
if (!llvm::equal(returnValues.getTypes(), funcOp.getResultTypes())) {
llvm::reportFatalUsageError(
"Return values do not match the declared function result types");
}
for (const auto value : returnValues) {
if (isa<QubitType>(value.getType())) {
validateQubitValue(value);
validQubits.erase(value);
} else if (isQubitTensor(value.getType())) {
validateTensorValue(value);
validTensors.erase(value);
}
}
for (const auto& [qubit, info] : validQubits) {
if (!functionScope->outerQubits.contains(qubit)) {
llvm::reportFatalUsageError(
"Function body has qubit values that are neither returned nor "
"consumed");
}
}
for (const auto& [tensor, info] : validTensors) {
if (!functionScope->outerTensors.contains(tensor)) {
llvm::reportFatalUsageError(
"Function body has tensor values that are neither returned nor "
"deallocated");
}
}
func::ReturnOp::create(*this, returnValues);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp` around lines 1450 - 1475,
Update endFunction to eagerly validate returnValues against the resultTypes
recorded by startFunction before creating func::ReturnOp: reportFatalUsageError
when the arity differs or any corresponding value type differs from its declared
result type. Keep the existing qubit/tensor validation and cleanup checks, and
perform this validation at the endFunction call site before ReturnOp creation.


restoreInsertionPoint(functionScope->savedInsertPoint);
functionScope.reset();
}

SmallVector<Value> QCOProgramBuilder::call(StringRef callee,
ValueRange operands) {
checkFinalized();

auto funcOp = dyn_cast_or_null<func::FuncOp>(
SymbolTable::lookupSymbolIn(module, getStringAttr(callee)));
if (!funcOp) {
llvm::reportFatalUsageError("Callee not found in module");
}

SmallVector<Value> qubitOperands;
SmallVector<Value> tensorOperands;
for (const auto operand : operands) {
if (isa<QubitType>(operand.getType())) {
validateQubitValue(operand);
qubitOperands.emplace_back(operand);
} else if (isQubitTensor(operand.getType())) {
validateTensorValue(operand);
tensorOperands.emplace_back(operand);
}
}

auto callOp = func::CallOp::create(*this, funcOp, operands);
Comment on lines +1485 to +1503

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The new function APIs defer signature checking to the MLIR verifier. QCOProgramBuilder reports every other misuse eagerly with llvm::reportFatalUsageError, at the call site. call and endFunction break that contract: both build a func operation from unvalidated types, so a mismatch only surfaces later during module verification, pointing at the generated operation instead of the builder call.

  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp#L1485-L1503: after the callee lookup, compare operands.getTypes() with funcOp.getArgumentTypes() and report a fatal usage error on mismatch. Place the check before the linear-tracking updates, which currently consume operands based on a malformed call.
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp#L1450-L1475: compare returnValues.getTypes() with the enclosing function's getResultTypes() and report a fatal usage error on mismatch, before creating func::ReturnOp.
📍 Affects 1 file
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp#L1485-L1503 (this comment)
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp#L1450-L1475
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp` around lines 1485 - 1503,
Validate function signatures eagerly in QCOProgramBuilder.cpp: at lines
1485-1503, update call to compare operands.getTypes() with
funcOp.getArgumentTypes() and reportFatalUsageError before linear-tracking
updates; at lines 1450-1475, update endFunction to compare
returnValues.getTypes() with the enclosing function’s getResultTypes() and
reportFatalUsageError before creating func::ReturnOp.


SmallVector<Value> qubitResults;
SmallVector<Value> tensorResults;
for (const auto result : callOp.getResults()) {
if (isa<QubitType>(result.getType())) {
qubitResults.emplace_back(result);
} else if (isQubitTensor(result.getType())) {
tensorResults.emplace_back(result);
}
}

// Thread the i-th linear operand into the i-th linear result of the same
// kind. Any operand without a matching result is consumed by the call, any
// result without a matching operand is newly created by it.
const auto pairedQubits = std::min(qubitOperands.size(), qubitResults.size());
for (size_t i = 0; i < pairedQubits; ++i) {
updateQubitTracking(qubitOperands[i], qubitResults[i]);
}
for (size_t i = pairedQubits; i < qubitOperands.size(); ++i) {
validQubits.erase(qubitOperands[i]);
}
for (size_t i = pairedQubits; i < qubitResults.size(); ++i) {
validQubits.try_emplace(qubitResults[i], QubitInfo{});
}

const auto pairedTensors =
std::min(tensorOperands.size(), tensorResults.size());
for (size_t i = 0; i < pairedTensors; ++i) {
updateTensorTracking(tensorOperands[i], tensorResults[i]);
}
for (size_t i = pairedTensors; i < tensorOperands.size(); ++i) {
validTensors.erase(tensorOperands[i]);
}
for (size_t i = pairedTensors; i < tensorResults.size(); ++i) {
validTensors.try_emplace(tensorResults[i], TensorInfo{tensorCounter++});
}

return SmallVector<Value>(callOp.getResults());
}

//===----------------------------------------------------------------------===//
// Finalization
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading