From 1b603749afc9879ec1c3a5c9429a6bcf3ba1ae58 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Mon, 15 Jun 2026 15:37:23 +0200 Subject: [PATCH 01/32] Add pass foundation --- .../mlir/Dialect/QIR/Transforms/Passes.td | 9 ++ .../Transforms/AttachEntryPointAttributes.cpp | 99 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index efb7fea4b1..b38dccdfb0 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -11,6 +11,15 @@ include "mlir/Pass/PassBase.td" +def AttachEntryPointAttributes : Pass<"attach-qir-entry-point-attributes", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::LLVM::LLVMDialect"]; + let summary = "Attaches the required attributes to the function marked as entry_point, compliant with QIR 2.1"; + let description = [{ + TODO + }]; + let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */"\"base_profile\"", "Specifies the profile.">]; +} + def QIRCleanupPass : Pass<"qir-cleanup", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; let summary = "Remove redundant QIR runtime bookkeeping."; diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp new file mode 100644 index 0000000000..921fd635cd --- /dev/null +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -0,0 +1,99 @@ +#include "mlir/Dialect/QIR/Transforms/Passes.h" +#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" +#include "mlir/Dialect/QIR/Utils/QIRUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::qir { + +#define GEN_PASS_DEF_ATTACHENTRYPOINTATTRIBUTES +#include "mlir/Dialect/QIR/Transforms/Passes.h.inc" + +/** + * @brief Attaches the required attributes to the function marked as + * entry_point. + */ +struct AttachEntryPointAttributes final + : impl::AttachEntryPointAttributesBase { +protected: + void runOnOperation() override { + auto main = getMainFunction(getOperation()); + setQIRAttributes(main, collectMetadata()); + } + +private: + QIRMetadata collectMetadata(LLVM::LLVMFuncOp& main) { + QIRMetadata metadata; + + if (!useAdaptive) { + metadata.useAdaptive = false; + metadata.useDynamicQubit = false; + metadata.useDynamicResult = false; + metadata.backwardsBranching = 0; + metadata.useArrays = false; + metadata.numQubits = getNumConstantQubits(main); + } + + return metadata; + } + + static size_t getNumConstantQubits(LLVM::LLVMFuncOp& main) { + static constexpr StringRef PREFIX_LABEL = "@__quantum__qis"; + + size_t numQubits{0}; + DenseSet seen; // A set of seen indices. + + std::ignore = main->walk([&](LLVM::ConstantOp& constant) { + // Must be used and an integer const. + if (constant.use_empty() || !isa(constant.getValue())) { + return WalkResult::advance(); + } + + auto intAttr = cast(constant.getValue()); + + // Must be not be an index integer. + if (!intAttr.getType().isInteger()) { + return WalkResult::advance(); + } + + // We assume that static qubits are constant integers, that are converted + // to an integer pointer, and then used in one quantum instruction. + + auto userIt = llvm::find_if(constant->getUsers(), [](Operation* user) { + return isa(user); + }); + if (userIt == constant->user_end()) { + return WalkResult::advance(); + } + + auto toPtrOp = cast(*userIt); + auto callIt = llvm::find_if(toPtrOp->getUsers(), [](Operation* user) { + auto callOp = dyn_cast(user); + if (!callOp) { + return false; + } + + const auto funcName = callOp->getName().getStringRef(); + return funcName.starts_with(PREFIX_LABEL); + }); + + if (callIt == toPtrOp->user_end() || seen.contains(intAttr.getValue())) { + return WalkResult::advance(); + } + + ++numQubits; + }); + } +}; + +} // namespace mlir::qir \ No newline at end of file From 80636f74b152a8fe74eb0a515db7e97bd190e536 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 08:54:54 +0200 Subject: [PATCH 02/32] Finalize pass implementation --- .../Transforms/AttachEntryPointAttributes.cpp | 214 ++++++++++++++---- 1 file changed, 168 insertions(+), 46 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 921fd635cd..bca5c67576 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -25,74 +25,196 @@ namespace mlir::qir { */ struct AttachEntryPointAttributes final : impl::AttachEntryPointAttributesBase { + using AttachEntryPointAttributesBase::AttachEntryPointAttributesBase; + protected: void runOnOperation() override { auto main = getMainFunction(getOperation()); - setQIRAttributes(main, collectMetadata()); + setQIRAttributes(main, useAdaptive ? getBase(main) : getAdaptive(main)); } private: - QIRMetadata collectMetadata(LLVM::LLVMFuncOp& main) { - QIRMetadata metadata; - - if (!useAdaptive) { - metadata.useAdaptive = false; - metadata.useDynamicQubit = false; - metadata.useDynamicResult = false; - metadata.backwardsBranching = 0; - metadata.useArrays = false; - metadata.numQubits = getNumConstantQubits(main); - } - - return metadata; - } - - static size_t getNumConstantQubits(LLVM::LLVMFuncOp& main) { + /// Count the number of uniquely indexed qubit pointers. + /// Assumes that qubits are constant integers that are converted to + /// an integer pointer and then used in (at least) one quantum instruction. + static size_t getNumQubits(LLVM::LLVMFuncOp& main) { static constexpr StringRef PREFIX_LABEL = "@__quantum__qis"; - size_t numQubits{0}; - DenseSet seen; // A set of seen indices. + DenseSet seen; + main->walk([&](LLVM::ConstantOp& constOp) { + if (constOp.use_empty()) { + return; + } + + const auto intAttr = dyn_cast(constOp.getValue()); + if (!intAttr) { + return; + } - std::ignore = main->walk([&](LLVM::ConstantOp& constant) { - // Must be used and an integer const. - if (constant.use_empty() || !isa(constant.getValue())) { - return WalkResult::advance(); + if (!intAttr.getType().isInteger()) { // Not a ": index". + return; } - auto intAttr = cast(constant.getValue()); + const auto userIt = + llvm::find_if(constOp->getUsers(), [](Operation* user) { + return isa(user); + }); + if (userIt == constOp->user_end()) { + return; + } + + const auto toPtrOp = cast(*userIt); + const auto callIt = + llvm::find_if(toPtrOp->getUsers(), [](Operation* user) { + auto callOp = dyn_cast(user); + if (!callOp) { + return false; + } + + const auto funcName = callOp->getName().getStringRef(); + return funcName.starts_with(PREFIX_LABEL); + }); - // Must be not be an index integer. - if (!intAttr.getType().isInteger()) { - return WalkResult::advance(); + if (callIt == toPtrOp->user_end()) { + return; } - // We assume that static qubits are constant integers, that are converted - // to an integer pointer, and then used in one quantum instruction. + // The set ensures that we don't insert the same index multiple times. + seen.insert(intAttr.getValue()); + }); + + return seen.size(); + } - auto userIt = llvm::find_if(constant->getUsers(), [](Operation* user) { - return isa(user); - }); - if (userIt == constant->user_end()) { - return WalkResult::advance(); + /// Count the number of uniquely indexed result_record_output statements. + static size_t getNumResults(LLVM::LLVMFuncOp& main) { + static constexpr StringRef REC_FN = "@__quantum__rt__result_record_output"; + + DenseSet seen; + main->walk([&](LLVM::CallOp& callOp) { + if (callOp->getName().getStringRef() != REC_FN) { + return; } - auto toPtrOp = cast(*userIt); - auto callIt = llvm::find_if(toPtrOp->getUsers(), [](Operation* user) { - auto callOp = dyn_cast(user); - if (!callOp) { - return false; - } + const auto operand = callOp->getOperand(0); + auto toPtrOp = dyn_cast(operand.getDefiningOp()); + if (!toPtrOp) { + return; + } - const auto funcName = callOp->getName().getStringRef(); - return funcName.starts_with(PREFIX_LABEL); - }); + const auto arg = toPtrOp.getArg(); + auto constOp = dyn_cast(arg.getDefiningOp()); + if (!constOp) { + return; + } - if (callIt == toPtrOp->user_end() || seen.contains(intAttr.getValue())) { - return WalkResult::advance(); + const auto intAttr = dyn_cast(constOp.getValue()); + if (!intAttr) { + return; } - ++numQubits; + // The set ensures that we don't insert the same index multiple times. + seen.insert(intAttr.getValue()); + }); + + return seen.size(); + } + + /// Return true, if the entry point contains an `LLVM::CondBrOp`. + static bool hasConditionalBranching(LLVM::LLVMFuncOp& main) { + bool hasConditional{false}; + main->walk([&](LLVM::CondBrOp&) { + hasConditional = true; + return WalkResult::interrupt(); }); + return hasConditional; + } + + /// Return true, if the entry point contains an `LLVM::BrOp` for which the + /// destination dominates the block it terminates. + bool hasBackwardBranching(LLVM::LLVMFuncOp& main) { + bool hasBackward{false}; + const auto& domInfo = getAnalysis(); + main->walk([&](LLVM::BrOp& brOp) { + if (domInfo.dominates(brOp.getDest(), brOp->getBlock())) { + hasBackward = true; + return WalkResult::interrupt(); + } + }); + return hasBackward; + } + + /// + static std::tuple + hasDynamicQubitAllocation(LLVM::LLVMFuncOp& main) { + static constexpr StringRef QUBIT_ALLOC = "@__quantum__rt__qubit_allocate"; + static constexpr StringRef RESULT_ALLOC = "@__quantum__rt__result_allocate"; + static constexpr StringRef QUBIT_ARR_ALLOC = + "@__quantum__rt__qubit_array_allocate"; + static constexpr StringRef RESULT_ARR_ALLOC = + "@__quantum__rt__result_array_allocate"; + + bool useDynamicQubit{false}; + bool useDynamicResult{false}; + bool useArrays{false}; + + main->walk([&](LLVM::CallOp& callOp) { + const auto name = callOp->getName().getStringRef(); + if (name == QUBIT_ALLOC) { + useDynamicQubit = true; + } else if (name == RESULT_ALLOC) { + useDynamicResult = true; + } else if (name == QUBIT_ARR_ALLOC) { + useDynamicQubit = true; + useArrays = true; + } else if (name == RESULT_ARR_ALLOC) { + useDynamicResult = true; + useArrays = true; + } + }); + + return std::make_tuple(useDynamicQubit, useDynamicResult, useArrays); + } + + /// Return the metadata for a QIR base profile compliant program. + static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { + return {.useAdaptive = false, + .useDynamicQubit = false, + .useDynamicResult = false, + .backwardsBranching = 0, + .useArrays = false, + .numQubits = getNumQubits(main), + .numResults = getNumResults(main)}; + } + + /// Return the metadata for a QIR base profile compliant program. + QIRMetadata getAdaptive(LLVM::LLVMFuncOp& main) { + const auto hasConditional = hasConditionalBranching(main); + const auto hasBackward = hasBackwardBranching(main); + const auto [useDynamicQubit, useDynamicResult, useArrays] = + hasDynamicQubitAllocation(main); + + QIRMetadata md; + md.useAdaptive = true; + md.useDynamicQubit = useDynamicQubit; + md.useDynamicResult = useDynamicResult; + md.useArrays = useArrays; + + if (!useDynamicQubit) { + md.numQubits = getNumQubits(main); + } + + if (!useDynamicResult) { + md.numResults = getNumResults(main); + } + + if (hasConditional) { + md.backwardsBranching = hasBackward ? 3 : 2; + } else if (hasBackward) { + md.backwardsBranching = 1; + } + + return md; } }; From 4b69f8c1b446f9ac8d3195a29ddd026c06854637 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 09:59:19 +0200 Subject: [PATCH 03/32] Add populate function --- .../Conversion/QCToQIR/QIRCommon/QIRCommon.h | 5 ++- .../mlir/Dialect/QIR/Transforms/Passes.td | 5 +-- mlir/include/mlir/Support/Passes.h | 5 +++ mlir/lib/Compiler/CompilerPipeline.cpp | 13 +----- .../QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp | 43 ++----------------- .../QCToQIR/QIRBase/QCToQIRBase.cpp | 29 +++++-------- .../QCToQIR/QIRCommon/QIRCommon.cpp | 5 --- .../Transforms/AttachEntryPointAttributes.cpp | 14 +++--- mlir/lib/Support/Passes.cpp | 12 ++++++ .../QCToQIR/QCToQIRAdaptive/CMakeLists.txt | 1 + .../test_qc_to_qir_adaptive.cpp | 3 +- .../QCToQIR/QCToQIRBase/CMakeLists.txt | 3 +- .../QCToQIRBase/test_qc_to_qir_base.cpp | 3 +- 13 files changed, 52 insertions(+), 89 deletions(-) diff --git a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h index c92bf5f9d2..38b0148cde 100644 --- a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h +++ b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h @@ -38,7 +38,7 @@ enum class AllocationMode : std::uint8_t { /** * @brief State object for tracking lowering information during QIR conversion */ -struct LoweringState : QIRMetadata { +struct LoweringState { /// Cache static qubit pointers for reuse DenseMap staticQubits; @@ -70,6 +70,9 @@ struct LoweringState : QIRMetadata { Block* measurementsBlock{}; Block* outputBlock{}; + /// The number of recorded results in the computation. + size_t numResults = 0; + /// The qubit allocation mode used in the module AllocationMode allocationMode = AllocationMode::Unset; diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index b38dccdfb0..36b56034d4 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -14,10 +14,7 @@ include "mlir/Pass/PassBase.td" def AttachEntryPointAttributes : Pass<"attach-qir-entry-point-attributes", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; let summary = "Attaches the required attributes to the function marked as entry_point, compliant with QIR 2.1"; - let description = [{ - TODO - }]; - let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */"\"base_profile\"", "Specifies the profile.">]; + let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */"true", "Specifies the profile.">]; } def QIRCleanupPass : Pass<"qir-cleanup", "mlir::ModuleOp"> { diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index 9a7ec7a2e8..ce24b51fe0 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -35,6 +35,11 @@ void populateQCOCleanupPipeline(mlir::PassManager& pm); */ void populateQIRCleanupPipeline(mlir::PassManager& pm); +/** + * @brief Populate the QIR conversion pipeline on the given pass manager. + */ +void populateQIRConversionPipeline(mlir::PassManager& pm, bool useAdaptive = false); + /** * @brief Run the QC-oriented cleanup pipeline on a module. */ diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index b3f6b9daef..c65695d828 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -12,8 +12,6 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" -#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" -#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" @@ -199,15 +197,8 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 9: QC-to-QIR conversion (optional) if (convertToQIR) { - auto addConversionPass = [&](PassManager& pm) { - if (config_.convertToQIRBase) { - pm.addPass(createQCToQIRBase()); - } else { - pm.addPass(createQCToQIRAdaptive()); - } - }; - - if (failed(runStage(addConversionPass))) { + if (failed(runStage( + [&](PassManager& pm) { populateQIRConversionPipeline(pm, config_.convertToQIRAdaptive); }))) { return failure(); } diff --git a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp index bdd7ead97c..e8561c2bff 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp @@ -84,9 +84,6 @@ struct ConvertMemRefAllocOp final } auto& state = getState(); - state.useDynamicQubit = true; - state.useArrays = true; - auto* ctx = getContext(); auto ptrType = LLVM::LLVMPointerType::get(ctx); @@ -235,7 +232,6 @@ struct ConvertQCAllocOp final : StatefulOpConversionPattern { op.getOperation()))) { return failure(); } - state.useDynamicQubit = true; auto* ctx = getContext(); auto ptrType = LLVM::LLVMPointerType::get(ctx); @@ -363,8 +359,6 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { matchAndRewrite(MeasureOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { auto& state = getState(); - state.useDynamicResult = true; - auto& resultArrays = state.resultArrays; auto& loadedResults = state.loadedResults; auto& resultPtrs = state.resultPtrs; @@ -381,7 +375,6 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { // Get result pointer Value result; if (op.getRegisterName() && op.getRegisterSize() && op.getRegisterIndex()) { - state.useArrays = true; const auto registerName = op.getRegisterName().value(); const auto registerSize = static_cast(op.getRegisterSize().value()); @@ -425,7 +418,6 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { rewriter.setInsertionPoint(state.entryBlock->getTerminator()); result = createPointerFromIndex(rewriter, op.getLoc(), resultPtrs.size()); resultPtrs.try_emplace(resultPtrs.size(), result); - state.numResults++; } rewriter.restoreInsertionPoint(savedInsertionPoint); @@ -573,22 +565,6 @@ struct QCToQIRAdaptive final : impl::QCToQIRAdaptiveBase { } } - /** - * @brief Iterates through the module to find any scf.while or scf.for - * operation to set the backward branching flag before they are converted to - * cf operations. - */ - static void setSCFFlags(Operation* op, LoweringState* state) { - op->walk([&](scf::ForOp) { - state->backwardsBranching += 1; - return WalkResult::interrupt(); - }); - op->walk([&](scf::WhileOp) { - state->backwardsBranching += 2; - return WalkResult::interrupt(); - }); - } - protected: /** * @brief Executes the QC to QIR conversion pass @@ -614,15 +590,11 @@ struct QCToQIRAdaptive final : impl::QCToQIRAdaptiveBase { * Convert QC dialect operations and memref operations to QIR calls and add * output recording to the output block. * - * **Stage 6: QIR Attributes** - * Add QIR Profile metadata to the main function, including qubit/result - * counts and version information. - * - * **Stage 7: Standard dialects to LLVM** + * **Stage 6: Standard dialects to LLVM** * Convert arith and control flow dialects to LLVM (for index arithmetic and * function control flow). * - * **Stage 8: Reconcile casts** + * **Stage 7: Reconcile casts** * Clean up any unrealized cast operations introduced during type * conversion. */ @@ -632,15 +604,11 @@ struct QCToQIRAdaptive final : impl::QCToQIRAdaptiveBase { ConversionTarget target(*ctx); QCToQIRTypeConverter typeConverter(ctx); LoweringState state; - state.useAdaptive = true; target.addLegalDialect(); // Stage 1: Convert scf dialect to cf { - // Find the required flags before the scf operations are converted - setSCFFlags(moduleOp, &state); - RewritePatternSet scfPatterns(ctx); target.addIllegalDialect(); target.addLegalDialect(); @@ -696,10 +664,7 @@ struct QCToQIRAdaptive final : impl::QCToQIRAdaptiveBase { releaseResults(main, ctx, &state); } - // Stage 6: Set QIR metadata attributes - setQIRAttributes(main, state); - - // Stage 7: Convert standard dialects to LLVM + // Stage 6: Convert standard dialects to LLVM { RewritePatternSet stdPatterns(ctx); target.addIllegalDialect(); @@ -716,7 +681,7 @@ struct QCToQIRAdaptive final : impl::QCToQIRAdaptiveBase { } } - // Stage 8: Reconcile unrealized casts + // Stage 7: Reconcile unrealized casts PassManager passManager(ctx); passManager.addPass(createReconcileUnrealizedCastsPass()); if (passManager.run(moduleOp).failed()) { diff --git a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp index 74638efaae..ae1df22cc0 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp @@ -102,10 +102,10 @@ struct ConvertMemRefLoadOp final : StatefulOpConversionPattern { // Switch to entry block rewriter.setInsertionPoint(state.entryBlock->getTerminator()); + auto nqubits = state.staticQubits.size(); auto qubit = createPointerFromIndex(rewriter, op.getLoc(), - static_cast(state.numQubits)); - state.staticQubits.try_emplace(static_cast(state.numQubits++), - qubit); + static_cast(nqubits)); + state.staticQubits.try_emplace(static_cast(nqubits), qubit); rewriter.replaceOp(op, qubit); return success(); @@ -157,10 +157,10 @@ struct ConvertQCAllocOp final : StatefulOpConversionPattern { rewriter.setInsertionPoint(state.entryBlock->getTerminator()); + const auto nqubits = state.staticQubits.size(); auto qubit = createPointerFromIndex(rewriter, op.getLoc(), - static_cast(state.numQubits)); - state.staticQubits.try_emplace(static_cast(state.numQubits++), - qubit); + static_cast(nqubits)); + state.staticQubits.try_emplace(static_cast(nqubits + 1), qubit); rewriter.replaceOp(op, qubit); return success(); @@ -380,7 +380,7 @@ struct QCToQIRBase final : impl::QCToQIRBaseBase { * @brief Executes the QC to QIR conversion pass * * @details - * Performs the conversion in seven stages: + * Performs the conversion in six stages: * * **Stage 1: Func to LLVM** * Convert func dialect operations (main function) to LLVM dialect @@ -397,15 +397,11 @@ struct QCToQIRBase final : impl::QCToQIRBaseBase { * Convert QC dialect operations to QIR calls and add output recording to the * output block. * - * **Stage 5: QIR attributes** - * Add QIR base profile metadata to the main function, including qubit/result - * counts and version information. - * - * **Stage 6: Standard dialects to LLVM** + * **Stage 5: Standard dialects to LLVM** * Convert arith and control flow dialects to LLVM (for index arithmetic and * function control flow). * - * **Stage 7: Reconcile casts** + * **Stage 6: Reconcile casts** * Clean up any unrealized cast operations introduced during type conversion. */ void runOnOperation() override { @@ -460,10 +456,7 @@ struct QCToQIRBase final : impl::QCToQIRBaseBase { addOutputRecording(main, ctx, state); } - // Stage 5: Set QIR metadata attributes - setQIRAttributes(main, state); - - // Stage 6: Convert standard dialects to LLVM + // Stage 5: Convert standard dialects to LLVM { RewritePatternSet stdPatterns(ctx); target.addIllegalDialect(); @@ -480,7 +473,7 @@ struct QCToQIRBase final : impl::QCToQIRBaseBase { } } - // Stage 7: Reconcile unrealized casts + // Stage 6: Reconcile unrealized casts PassManager passManager(ctx); passManager.addPass(createReconcileUnrealizedCastsPass()); if (passManager.run(moduleOp).failed()) { diff --git a/mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp b/mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp index f197002ddd..aea4ae0a02 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp @@ -297,11 +297,6 @@ struct ConvertQCStaticOp final : StatefulOpConversionPattern { } rewriter.replaceOp(op, qubit); - // Track maximum qubit index - if (std::cmp_greater_equal(index, state.numQubits)) { - state.numQubits = index + 1; - } - return success(); } }; diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index bca5c67576..5ca2086281 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -41,7 +41,7 @@ struct AttachEntryPointAttributes final static constexpr StringRef PREFIX_LABEL = "@__quantum__qis"; DenseSet seen; - main->walk([&](LLVM::ConstantOp& constOp) { + main->walk([&](LLVM::ConstantOp constOp) { if (constOp.use_empty()) { return; } @@ -91,7 +91,7 @@ struct AttachEntryPointAttributes final static constexpr StringRef REC_FN = "@__quantum__rt__result_record_output"; DenseSet seen; - main->walk([&](LLVM::CallOp& callOp) { + main->walk([&](LLVM::CallOp callOp) { if (callOp->getName().getStringRef() != REC_FN) { return; } @@ -123,7 +123,7 @@ struct AttachEntryPointAttributes final /// Return true, if the entry point contains an `LLVM::CondBrOp`. static bool hasConditionalBranching(LLVM::LLVMFuncOp& main) { bool hasConditional{false}; - main->walk([&](LLVM::CondBrOp&) { + main->walk([&](LLVM::CondBrOp) { hasConditional = true; return WalkResult::interrupt(); }); @@ -135,16 +135,18 @@ struct AttachEntryPointAttributes final bool hasBackwardBranching(LLVM::LLVMFuncOp& main) { bool hasBackward{false}; const auto& domInfo = getAnalysis(); - main->walk([&](LLVM::BrOp& brOp) { + std::ignore = main->walk([&](LLVM::BrOp brOp) { if (domInfo.dominates(brOp.getDest(), brOp->getBlock())) { hasBackward = true; return WalkResult::interrupt(); } + return WalkResult::advance(); }); return hasBackward; } - /// + /// Return triple of booleans, indicating whether the entry point uses + /// dynamic qubits = [0], dynamic results = [1], or dynamic arrays = [2]. static std::tuple hasDynamicQubitAllocation(LLVM::LLVMFuncOp& main) { static constexpr StringRef QUBIT_ALLOC = "@__quantum__rt__qubit_allocate"; @@ -158,7 +160,7 @@ struct AttachEntryPointAttributes final bool useDynamicResult{false}; bool useArrays{false}; - main->walk([&](LLVM::CallOp& callOp) { + main->walk([&](LLVM::CallOp callOp) { const auto name = callOp->getName().getStringRef(); if (name == QUBIT_ALLOC) { useDynamicQubit = true; diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 70a5a499d1..e94f7a155f 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -10,6 +10,8 @@ #include "mlir/Support/Passes.h" +#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" +#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QC/Transforms/Passes.h" #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" @@ -59,6 +61,16 @@ void populateQIRCleanupPipeline(PassManager& pm) { pm.addPass(createRemoveDeadValuesPass()); } +void populateQIRConversionPipeline(mlir::PassManager& pm, bool useAdaptive) { + if (useAdaptive) { + pm.addPass(createQCToQIRAdaptive()); + } else { + pm.addPass(createQCToQIRBase()); + } + pm.addPass(createAttachEntryPointAttributes( + qir::AttachEntryPointAttributesOptions{useAdaptive})); +} + [[nodiscard]] LogicalResult runQCCleanupPipeline(ModuleOp module) { return runWithPassManager(module, populateQCCleanupPipeline, "Failed to run QC cleanup pipeline."); diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt index 1b991762b4..cf97d720dd 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms + MLIRQCToQIRBase MLIRQCToQIRAdaptive) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp index 1a1f5a3639..d6610730bb 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp @@ -9,7 +9,6 @@ */ #include "TestCaseUtils.h" -#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" @@ -78,7 +77,7 @@ class QCToQIRAdaptiveTest static LogicalResult runQCToQIRAdaptiveConversion(ModuleOp module) { PassManager pm(module.getContext()); - pm.addPass(createQCToQIRAdaptive()); + populateQIRConversionPipeline(pm, true); return pm.run(module); } diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt index e49b73d5d4..407b3ff560 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt @@ -18,7 +18,8 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms - MLIRQCToQIRBase) + MLIRQCToQIRBase + MLIRQCToQIRAdaptive) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp index 409d1d70de..f6adda0a43 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp @@ -9,7 +9,6 @@ */ #include "TestCaseUtils.h" -#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" @@ -76,7 +75,7 @@ class QCToQIRBaseTest : public testing::TestWithParam { static LogicalResult runQCToQIRBaseConversion(ModuleOp module) { PassManager pm(module.getContext()); - pm.addPass(createQCToQIRBase()); + populateQIRConversionPipeline(pm, false); return pm.run(module); } From b42ba92ad3c1133b151b48ffe655f66cdb0f0b87 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:00:52 +0000 Subject: [PATCH 04/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QIR/Transforms/Passes.td | 9 ++++++--- mlir/include/mlir/Support/Passes.h | 3 ++- mlir/lib/Compiler/CompilerPipeline.cpp | 5 +++-- .../QIR/Transforms/AttachEntryPointAttributes.cpp | 12 +++++++++++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index 36b56034d4..f1aefb07a1 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -11,10 +11,13 @@ include "mlir/Pass/PassBase.td" -def AttachEntryPointAttributes : Pass<"attach-qir-entry-point-attributes", "mlir::ModuleOp"> { +def AttachEntryPointAttributes + : Pass<"attach-qir-entry-point-attributes", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; - let summary = "Attaches the required attributes to the function marked as entry_point, compliant with QIR 2.1"; - let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */"true", "Specifies the profile.">]; + let summary = "Attaches the required attributes to the function marked as " + "entry_point, compliant with QIR 2.1"; + let options = [Option<"useAdaptive", "use-adaptive", "bool", + /*default= */ "true", "Specifies the profile.">]; } def QIRCleanupPass : Pass<"qir-cleanup", "mlir::ModuleOp"> { diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index ce24b51fe0..8afb12d865 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -38,7 +38,8 @@ void populateQIRCleanupPipeline(mlir::PassManager& pm); /** * @brief Populate the QIR conversion pipeline on the given pass manager. */ -void populateQIRConversionPipeline(mlir::PassManager& pm, bool useAdaptive = false); +void populateQIRConversionPipeline(mlir::PassManager& pm, + bool useAdaptive = false); /** * @brief Run the QC-oriented cleanup pipeline on a module. diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index c65695d828..b7a4617e15 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -197,8 +197,9 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 9: QC-to-QIR conversion (optional) if (convertToQIR) { - if (failed(runStage( - [&](PassManager& pm) { populateQIRConversionPipeline(pm, config_.convertToQIRAdaptive); }))) { + if (failed(runStage([&](PassManager& pm) { + populateQIRConversionPipeline(pm, config_.convertToQIRAdaptive); + }))) { return failure(); } diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 5ca2086281..164d7c6f20 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" @@ -220,4 +230,4 @@ struct AttachEntryPointAttributes final } }; -} // namespace mlir::qir \ No newline at end of file +} // namespace mlir::qir From 9bbdf2484183ce1f86d30aefabcacdef447b0b46 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:01:52 +0200 Subject: [PATCH 05/32] Fix off by one error --- mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp index ae1df22cc0..d0344c135c 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp @@ -160,7 +160,7 @@ struct ConvertQCAllocOp final : StatefulOpConversionPattern { const auto nqubits = state.staticQubits.size(); auto qubit = createPointerFromIndex(rewriter, op.getLoc(), static_cast(nqubits)); - state.staticQubits.try_emplace(static_cast(nqubits + 1), qubit); + state.staticQubits.try_emplace(static_cast(nqubits), qubit); rewriter.replaceOp(op, qubit); return success(); From 6af1739379cc2727d865686693389051a2a68c16 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:10:42 +0200 Subject: [PATCH 06/32] Fix initialization order --- .../Transforms/AttachEntryPointAttributes.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 164d7c6f20..332b7fd1a3 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -190,13 +190,15 @@ struct AttachEntryPointAttributes final /// Return the metadata for a QIR base profile compliant program. static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { - return {.useAdaptive = false, - .useDynamicQubit = false, - .useDynamicResult = false, - .backwardsBranching = 0, - .useArrays = false, - .numQubits = getNumQubits(main), - .numResults = getNumResults(main)}; + return { + .numQubits = getNumQubits(main), + .numResults = getNumResults(main), + .useDynamicQubit = false, + .useDynamicResult = false, + .useArrays = false, + .backwardsBranching = 0, + .useAdaptive = false, + }; } /// Return the metadata for a QIR base profile compliant program. From d0ef4e74e37053f37287feffe96502c6bf5ae8bb Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:21:10 +0200 Subject: [PATCH 07/32] Add MLIRQCToQIR library --- .../include/mlir/Conversion/QCToQIR/QCToQIR.h | 12 ++++++++++++ mlir/include/mlir/Support/Passes.h | 6 ------ mlir/lib/Compiler/CMakeLists.txt | 3 +-- mlir/lib/Compiler/CompilerPipeline.cpp | 1 + mlir/lib/Conversion/QCToQIR/CMakeLists.txt | 19 +++++++++++++++++++ mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 18 ++++++++++++++++++ mlir/lib/Support/Passes.cpp | 12 ------------ .../QCToQIR/QCToQIRAdaptive/CMakeLists.txt | 3 +-- .../test_qc_to_qir_adaptive.cpp | 1 + .../QCToQIR/QCToQIRBase/CMakeLists.txt | 3 +-- .../QCToQIRBase/test_qc_to_qir_base.cpp | 1 + 11 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h create mode 100644 mlir/lib/Conversion/QCToQIR/QCToQIR.cpp diff --git a/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h b/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h new file mode 100644 index 0000000000..ad9e95a353 --- /dev/null +++ b/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h @@ -0,0 +1,12 @@ +#pragma once + +namespace mlir { +class ModuleOp; +class PassManager; + +/** + * @brief Populate the QIR conversion pipeline on the given pass manager. + */ +void populateQIRConversionPipeline(mlir::PassManager& pm, + bool useAdaptive = false); +} // namespace mlir \ No newline at end of file diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index 8afb12d865..9a7ec7a2e8 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -35,12 +35,6 @@ void populateQCOCleanupPipeline(mlir::PassManager& pm); */ void populateQIRCleanupPipeline(mlir::PassManager& pm); -/** - * @brief Populate the QIR conversion pipeline on the given pass manager. - */ -void populateQIRConversionPipeline(mlir::PassManager& pm, - bool useAdaptive = false); - /** * @brief Run the QC-oriented cleanup pipeline on a module. */ diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index b36ef2ff3b..1dc45532fa 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -19,8 +19,7 @@ add_mlir_library( MLIRTransformUtils MLIRQCToQCO MLIRQCOToQC - MLIRQCToQIRAdaptive - MLIRQCToQIRBase + MLIRQCToQIR MLIRQCOTransforms MQT::MLIRSupport) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index b7a4617e15..9a8ddb4dec 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -12,6 +12,7 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" +#include "mlir/Conversion/QCToQIR/QCToQIR.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" diff --git a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt index 5dac824d4f..a96810ae00 100644 --- a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt +++ b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt @@ -9,3 +9,22 @@ add_subdirectory(QIRCommon) add_subdirectory(QIRBase) add_subdirectory(QIRAdaptive) + +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +file(GLOB CONVERSION_SOURCES *.cpp) + +add_mlir_conversion_library( + MLIRQCToQIR + ${CONVERSION_SOURCES} + LINK_LIBS + MLIRQCToQIRBase + MLIRQCToQIRAdaptive) + +mqt_mlir_target_use_project_options(MLIRQCToQCO) diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp new file mode 100644 index 0000000000..1a46cc4b46 --- /dev/null +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -0,0 +1,18 @@ +#include "mlir/Conversion/QCToQIR/QCToQIR.h" + +#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" +#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" +#include "mlir/Dialect/QIR/Transforms/Passes.h" + +#include + +void mlir::populateQIRConversionPipeline(mlir::PassManager& pm, + bool useAdaptive) { + if (useAdaptive) { + pm.addPass(createQCToQIRAdaptive()); + } else { + pm.addPass(createQCToQIRBase()); + } + pm.addPass(createAttachEntryPointAttributes( + qir::AttachEntryPointAttributesOptions{useAdaptive})); +} \ No newline at end of file diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index e94f7a155f..70a5a499d1 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -10,8 +10,6 @@ #include "mlir/Support/Passes.h" -#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" -#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QC/Transforms/Passes.h" #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" @@ -61,16 +59,6 @@ void populateQIRCleanupPipeline(PassManager& pm) { pm.addPass(createRemoveDeadValuesPass()); } -void populateQIRConversionPipeline(mlir::PassManager& pm, bool useAdaptive) { - if (useAdaptive) { - pm.addPass(createQCToQIRAdaptive()); - } else { - pm.addPass(createQCToQIRBase()); - } - pm.addPass(createAttachEntryPointAttributes( - qir::AttachEntryPointAttributesOptions{useAdaptive})); -} - [[nodiscard]] LogicalResult runQCCleanupPipeline(ModuleOp module) { return runWithPassManager(module, populateQCCleanupPipeline, "Failed to run QC cleanup pipeline."); diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt index cf97d720dd..2e5639472d 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt @@ -18,8 +18,7 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms - MLIRQCToQIRBase - MLIRQCToQIRAdaptive) + MLIRQCToQIR) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp index d6610730bb..a27053223e 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp @@ -9,6 +9,7 @@ */ #include "TestCaseUtils.h" +#include "mlir/Conversion/QCToQIR/QCToQIR.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt index 407b3ff560..cc2aecf0a3 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt @@ -18,8 +18,7 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms - MLIRQCToQIRBase - MLIRQCToQIRAdaptive) + MLIRQCToQIR) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp index f6adda0a43..cbf6e062bb 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp @@ -9,6 +9,7 @@ */ #include "TestCaseUtils.h" +#include "mlir/Conversion/QCToQIR/QCToQIR.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" From 34e54befd8db8f97ebc7103741bae066bc0e8835 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:21:47 +0000 Subject: [PATCH 08/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h | 12 +++++++++++- mlir/lib/Conversion/QCToQIR/CMakeLists.txt | 13 ++++--------- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 12 +++++++++++- .../Transforms/AttachEntryPointAttributes.cpp | 16 ++++++++-------- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h b/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h index ad9e95a353..44084c9ebc 100644 --- a/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h +++ b/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + #pragma once namespace mlir { @@ -9,4 +19,4 @@ class PassManager; */ void populateQIRConversionPipeline(mlir::PassManager& pm, bool useAdaptive = false); -} // namespace mlir \ No newline at end of file +} // namespace mlir diff --git a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt index a96810ae00..45676710c0 100644 --- a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt +++ b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt @@ -10,9 +10,8 @@ add_subdirectory(QIRCommon) add_subdirectory(QIRBase) add_subdirectory(QIRAdaptive) -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM Copyright (c) 2025 - 2026 Munich +# Quantum Software Company GmbH All rights reserved. # # SPDX-License-Identifier: MIT # @@ -20,11 +19,7 @@ add_subdirectory(QIRAdaptive) file(GLOB CONVERSION_SOURCES *.cpp) -add_mlir_conversion_library( - MLIRQCToQIR - ${CONVERSION_SOURCES} - LINK_LIBS - MLIRQCToQIRBase - MLIRQCToQIRAdaptive) +add_mlir_conversion_library(MLIRQCToQIR ${CONVERSION_SOURCES} LINK_LIBS MLIRQCToQIRBase + MLIRQCToQIRAdaptive) mqt_mlir_target_use_project_options(MLIRQCToQCO) diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index 1a46cc4b46..b10fb63bfe 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + #include "mlir/Conversion/QCToQIR/QCToQIR.h" #include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" @@ -15,4 +25,4 @@ void mlir::populateQIRConversionPipeline(mlir::PassManager& pm, } pm.addPass(createAttachEntryPointAttributes( qir::AttachEntryPointAttributesOptions{useAdaptive})); -} \ No newline at end of file +} diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 332b7fd1a3..b5914b5475 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -190,14 +190,14 @@ struct AttachEntryPointAttributes final /// Return the metadata for a QIR base profile compliant program. static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { - return { - .numQubits = getNumQubits(main), - .numResults = getNumResults(main), - .useDynamicQubit = false, - .useDynamicResult = false, - .useArrays = false, - .backwardsBranching = 0, - .useAdaptive = false, + return { + .numQubits = getNumQubits(main), + .numResults = getNumResults(main), + .useDynamicQubit = false, + .useDynamicResult = false, + .useArrays = false, + .backwardsBranching = 0, + .useAdaptive = false, }; } From d6272b26add930c0b5f461875963b4647bd60033 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:28:15 +0200 Subject: [PATCH 09/32] Apply linting suggestions --- mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp | 1 - .../Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp index e8561c2bff..9b03e4d722 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index b5914b5475..daa40bcba9 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -25,10 +25,10 @@ #include namespace mlir::qir { - #define GEN_PASS_DEF_ATTACHENTRYPOINTATTRIBUTES #include "mlir/Dialect/QIR/Transforms/Passes.h.inc" +namespace { /** * @brief Attaches the required attributes to the function marked as * entry_point. @@ -231,5 +231,5 @@ struct AttachEntryPointAttributes final return md; } }; - +} // namespace } // namespace mlir::qir From b5104eac1b5c63b4830aa8023b107b84d68ae631 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:29:48 +0200 Subject: [PATCH 10/32] Fix symbol names --- .../QIR/Transforms/AttachEntryPointAttributes.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index daa40bcba9..522a4b9e79 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -98,7 +98,7 @@ struct AttachEntryPointAttributes final /// Count the number of uniquely indexed result_record_output statements. static size_t getNumResults(LLVM::LLVMFuncOp& main) { - static constexpr StringRef REC_FN = "@__quantum__rt__result_record_output"; + static constexpr StringRef REC_FN = "__quantum__rt__result_record_output"; DenseSet seen; main->walk([&](LLVM::CallOp callOp) { @@ -159,12 +159,12 @@ struct AttachEntryPointAttributes final /// dynamic qubits = [0], dynamic results = [1], or dynamic arrays = [2]. static std::tuple hasDynamicQubitAllocation(LLVM::LLVMFuncOp& main) { - static constexpr StringRef QUBIT_ALLOC = "@__quantum__rt__qubit_allocate"; - static constexpr StringRef RESULT_ALLOC = "@__quantum__rt__result_allocate"; + static constexpr StringRef QUBIT_ALLOC = "__quantum__rt__qubit_allocate"; + static constexpr StringRef RESULT_ALLOC = "__quantum__rt__result_allocate"; static constexpr StringRef QUBIT_ARR_ALLOC = - "@__quantum__rt__qubit_array_allocate"; + "__quantum__rt__qubit_array_allocate"; static constexpr StringRef RESULT_ARR_ALLOC = - "@__quantum__rt__result_array_allocate"; + "__quantum__rt__result_array_allocate"; bool useDynamicQubit{false}; bool useDynamicResult{false}; From a49a1b6107a4ebbda9102eb504c9af9f096277bd Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:44:51 +0200 Subject: [PATCH 11/32] Fix invalid use of call attrs --- .../QIR/Transforms/AttachEntryPointAttributes.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 522a4b9e79..25f7b0955e 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ struct AttachEntryPointAttributes final protected: void runOnOperation() override { auto main = getMainFunction(getOperation()); - setQIRAttributes(main, useAdaptive ? getBase(main) : getAdaptive(main)); + setQIRAttributes(main, useAdaptive ? getAdaptive(main) : getBase(main)); } private: @@ -102,7 +103,11 @@ struct AttachEntryPointAttributes final DenseSet seen; main->walk([&](LLVM::CallOp callOp) { - if (callOp->getName().getStringRef() != REC_FN) { + if (!callOp.getCallee()) { + return; + } + + if (*callOp.getCallee() != REC_FN) { return; } @@ -171,7 +176,11 @@ struct AttachEntryPointAttributes final bool useArrays{false}; main->walk([&](LLVM::CallOp callOp) { - const auto name = callOp->getName().getStringRef(); + if (!callOp.getCallee()) { + return; + } + + const auto name = *callOp.getCallee(); if (name == QUBIT_ALLOC) { useDynamicQubit = true; } else if (name == RESULT_ALLOC) { From 205c7939c1ae876c9ac14660836a6d19293d3f88 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 10:50:27 +0200 Subject: [PATCH 12/32] Remove unused include --- .../QIR/Transforms/AttachEntryPointAttributes.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp index 25f7b0955e..e1cdf254bf 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -49,7 +48,7 @@ struct AttachEntryPointAttributes final /// Assumes that qubits are constant integers that are converted to /// an integer pointer and then used in (at least) one quantum instruction. static size_t getNumQubits(LLVM::LLVMFuncOp& main) { - static constexpr StringRef PREFIX_LABEL = "@__quantum__qis"; + static constexpr StringRef PREFIX_LABEL = "__quantum__qis"; DenseSet seen; main->walk([&](LLVM::ConstantOp constOp) { @@ -82,8 +81,11 @@ struct AttachEntryPointAttributes final return false; } - const auto funcName = callOp->getName().getStringRef(); - return funcName.starts_with(PREFIX_LABEL); + if (!callOp.getCallee()) { + false; + } + + return callOp.getCallee()->starts_with(PREFIX_LABEL); }); if (callIt == toPtrOp->user_end()) { From 18d47b20945820ff139647147cfa5ce631fa78dc Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 16 Jun 2026 13:03:32 +0200 Subject: [PATCH 13/32] Rename pass --- mlir/include/mlir/Dialect/QIR/Transforms/Passes.td | 3 +-- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 4 ++-- ...AttachEntryPointAttributes.cpp => AttachAttributes.cpp} | 7 +++---- 3 files changed, 6 insertions(+), 8 deletions(-) rename mlir/lib/Dialect/QIR/Transforms/{AttachEntryPointAttributes.cpp => AttachAttributes.cpp} (96%) diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index f1aefb07a1..86bc12b944 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -11,8 +11,7 @@ include "mlir/Pass/PassBase.td" -def AttachEntryPointAttributes - : Pass<"attach-qir-entry-point-attributes", "mlir::ModuleOp"> { +def AttachQIRAttributes : Pass<"attach-qir-attributes", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; let summary = "Attaches the required attributes to the function marked as " "entry_point, compliant with QIR 2.1"; diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index b10fb63bfe..51f2d4b197 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -23,6 +23,6 @@ void mlir::populateQIRConversionPipeline(mlir::PassManager& pm, } else { pm.addPass(createQCToQIRBase()); } - pm.addPass(createAttachEntryPointAttributes( - qir::AttachEntryPointAttributesOptions{useAdaptive})); + pm.addPass( + qir::createAttachQIRAttributes(qir::AttachQIRAttributesOptions{useAdaptive})); } diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp similarity index 96% rename from mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp rename to mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp index e1cdf254bf..010e653e40 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachEntryPointAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp @@ -25,7 +25,7 @@ #include namespace mlir::qir { -#define GEN_PASS_DEF_ATTACHENTRYPOINTATTRIBUTES +#define GEN_PASS_DEF_ATTACHQIRATTRIBUTES #include "mlir/Dialect/QIR/Transforms/Passes.h.inc" namespace { @@ -33,9 +33,8 @@ namespace { * @brief Attaches the required attributes to the function marked as * entry_point. */ -struct AttachEntryPointAttributes final - : impl::AttachEntryPointAttributesBase { - using AttachEntryPointAttributesBase::AttachEntryPointAttributesBase; +struct AttachQIRAttributes final : impl::AttachQIRAttributesBase { + using AttachQIRAttributesBase::AttachQIRAttributesBase; protected: void runOnOperation() override { From ac46f40317f6df09be7bd1bb0ff6b3c80387d226 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:04:05 +0000 Subject: [PATCH 14/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 4 ++-- mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index 51f2d4b197..24611ad606 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -23,6 +23,6 @@ void mlir::populateQIRConversionPipeline(mlir::PassManager& pm, } else { pm.addPass(createQCToQIRBase()); } - pm.addPass( - qir::createAttachQIRAttributes(qir::AttachQIRAttributesOptions{useAdaptive})); + pm.addPass(qir::createAttachQIRAttributes( + qir::AttachQIRAttributesOptions{useAdaptive})); } diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp index 010e653e40..db50557574 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp @@ -33,7 +33,8 @@ namespace { * @brief Attaches the required attributes to the function marked as * entry_point. */ -struct AttachQIRAttributes final : impl::AttachQIRAttributesBase { +struct AttachQIRAttributes final + : impl::AttachQIRAttributesBase { using AttachQIRAttributesBase::AttachQIRAttributesBase; protected: From 2feade8959feba330b52d0676466724a245d93e9 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 11:00:50 +0200 Subject: [PATCH 15/32] Integrate into QIRProgramBuilder --- .../include/mlir/Conversion/QCToQIR/QCToQIR.h | 22 - .../Conversion/QCToQIR/QIRCommon/QIRCommon.h | 3 - .../Dialect/QIR/Builder/QIRProgramBuilder.h | 11 +- .../mlir/Dialect/QIR/Utils/QIRMetadata.h | 43 -- .../include/mlir/Dialect/QIR/Utils/QIRUtils.h | 25 -- mlir/include/mlir/Support/Passes.h | 16 +- mlir/lib/Compiler/CMakeLists.txt | 3 +- mlir/lib/Compiler/CompilerPipeline.cpp | 18 +- mlir/lib/Conversion/QCToQIR/CMakeLists.txt | 16 +- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 28 -- .../Dialect/QIR/Builder/QIRProgramBuilder.cpp | 55 +-- .../QIR/Transforms/AttachAttributes.cpp | 246 ----------- .../QIR/Transforms/AttachQIRAttributes.cpp | 403 ++++++++++++++++++ mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp | 61 --- mlir/lib/Support/IRVerification.cpp | 61 ++- mlir/lib/Support/Passes.cpp | 16 +- .../Compiler/test_compiler_pipeline.cpp | 2 +- .../QCToQIR/QCToQIRAdaptive/CMakeLists.txt | 2 +- .../test_qc_to_qir_adaptive.cpp | 9 +- .../QCToQIR/QCToQIRBase/CMakeLists.txt | 2 +- .../QCToQIRBase/test_qc_to_qir_base.cpp | 8 +- 21 files changed, 540 insertions(+), 510 deletions(-) delete mode 100644 mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h delete mode 100644 mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h delete mode 100644 mlir/lib/Conversion/QCToQIR/QCToQIR.cpp delete mode 100644 mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp create mode 100644 mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp diff --git a/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h b/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h deleted file mode 100644 index 44084c9ebc..0000000000 --- a/mlir/include/mlir/Conversion/QCToQIR/QCToQIR.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -namespace mlir { -class ModuleOp; -class PassManager; - -/** - * @brief Populate the QIR conversion pipeline on the given pass manager. - */ -void populateQIRConversionPipeline(mlir::PassManager& pm, - bool useAdaptive = false); -} // namespace mlir diff --git a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h index 38b0148cde..9c8a0753d7 100644 --- a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h +++ b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" - #include #include #include @@ -26,7 +24,6 @@ #include namespace mlir { -using namespace qir; /** @brief Qubit allocation mode */ enum class AllocationMode : std::uint8_t { diff --git a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h index 30802171eb..3ad99aa526 100644 --- a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" - #include #include #include @@ -1086,15 +1084,18 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { /// Map from register to their loaded indices DenseMap> loadedQubits; - /// Track qubit and result counts for QIR metadata - QIRMetadata metadata_; - /// Helper variable for storing the LLVM pointer type Type ptrType; /// Helper variable for storing the LLVM void type Type voidType; + /// The number of used qubits. + size_t numQubits{0}; + + /// The number of result values. + size_t numResults{0}; + /** * @brief Helper to create a LLVM CallOp * diff --git a/mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h b/mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h deleted file mode 100644 index d089b1b78b..0000000000 --- a/mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include - -namespace mlir::qir { - -/** - * @brief State object for tracking QIR metadata during conversion - * - * @details - * This struct maintains metadata about the QIR program being built: - * - Qubit and result counts for QIR metadata - * - Whether dynamic memory management is needed - */ -struct QIRMetadata { - /// Number of qubits used in the module - size_t numQubits{0}; - /// Number of measurement results stored in the module - size_t numResults{0}; - /// Whether the module uses dynamic qubit management - bool useDynamicQubit{false}; - /// Whether the module uses dynamic result management - bool useDynamicResult{false}; - /// Whether the module uses arrays - bool useArrays{false}; - /// Whether the module uses backward branching (0 = none, 1 = iteration based, - /// 2 = condition based, 3 = both) - int backwardsBranching{0}; - /// Whether the module uses Adaptive Profile - bool useAdaptive{false}; -}; - -} // namespace mlir::qir diff --git a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h index 81a880192e..de6d055ec6 100644 --- a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h +++ b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" - #include #include #include @@ -169,29 +167,6 @@ DEFINE_GETTER(XXMINUSYY) */ LLVM::LLVMFuncOp getMainFunction(Operation* op); -/** - * @brief Set QIR base profile metadata attributes on the main function - * - * @details - * Adds the required metadata attributes for QIR base profile compliance: - * - `entry_point`: Marks the main entry point function - * - `output_labeling_schema`: labeled - * - `qir_profiles`: base_profile - * - `required_num_qubits`: Number of qubits used - * - `required_num_results`: Number of measurement results - * - `qir_major_version`: 2 - * - `qir_minor_version`: 1 - * - `dynamic_qubit_management`: true/false - * - `dynamic_result_management`: true/false - * - * These attributes are required by the QIR specification and inform QIR - * consumers about the module's resource requirements and capabilities. - * - * @param main The main LLVM function to annotate - * @param metadata The QIR metadata containing qubit/result counts - */ -void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata); - /** * @brief Get or create a QIR function declaration * diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index 9a7ec7a2e8..0f10e92d44 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -17,6 +17,14 @@ class ModuleOp; class PassManager; } // namespace mlir +/** + * @brief Populate the pass manager and run it on the module. + */ +mlir::LogicalResult +runWithPassManager(mlir::ModuleOp module, + mlir::function_ref populatePasses, + mlir::StringRef errorMessage); + /** * @brief Populate a QC-oriented cleanup pipeline on the given pass manager. * @details Adds generic cleanup and QC qubit-register shrinking. @@ -31,9 +39,10 @@ void populateQCOCleanupPipeline(mlir::PassManager& pm); /** * @brief Populate a QIR-oriented cleanup pipeline on the given pass manager. - * @details Adds generic cleanup and QIR-specific simplifications. + * @details Adds generic cleanup and QIR-specific simplifications. Updates the + * meta data accordingly. */ -void populateQIRCleanupPipeline(mlir::PassManager& pm); +void populateQIRCleanupPipeline(mlir::PassManager& pm, bool useAdaptive); /** * @brief Run the QC-oriented cleanup pipeline on a module. @@ -48,4 +57,5 @@ void populateQIRCleanupPipeline(mlir::PassManager& pm); /** * @brief Run the QIR-oriented cleanup pipeline on a module. */ -[[nodiscard]] mlir::LogicalResult runQIRCleanupPipeline(mlir::ModuleOp module); +[[nodiscard]] mlir::LogicalResult runQIRCleanupPipeline(mlir::ModuleOp module, + bool useAdaptive); diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index 1dc45532fa..735c12d6fd 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -19,7 +19,8 @@ add_mlir_library( MLIRTransformUtils MLIRQCToQCO MLIRQCOToQC - MLIRQCToQIR + MLIRQCToQIRBase + MLIRQCToQIRAdaptive MLIRQCOTransforms MQT::MLIRSupport) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 9a8ddb4dec..4418991a55 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -12,8 +12,10 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" -#include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" +#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" @@ -97,7 +99,7 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // 10. QIR cleanup (optional) auto totalStages = 8; if (convertToQIR) { - totalStages += 2; + totalStages += 3; } auto currentStage = 0; @@ -199,11 +201,14 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // Stage 9: QC-to-QIR conversion (optional) if (convertToQIR) { if (failed(runStage([&](PassManager& pm) { - populateQIRConversionPipeline(pm, config_.convertToQIRAdaptive); + if (config_.convertToQIRAdaptive) { + pm.addPass(createQCToQIRAdaptive()); + } else { + pm.addPass(createQCToQIRBase()); + } }))) { return failure(); } - if (record != nullptr && config_.recordIntermediates) { record->afterQIRConversion = captureIR(module); if (config_.printIRAfterAllStages) { @@ -212,8 +217,9 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } } // Stage 10: QIR cleanup (optional) - if (failed(runStage( - [&](PassManager& pm) { populateQIRCleanupPipeline(pm); }))) { + if (failed(runStage([&](PassManager& pm) { + populateQIRCleanupPipeline(pm, config_.convertToQIRAdaptive); + }))) { return failure(); } if (record != nullptr && config_.recordIntermediates) { diff --git a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt index 45676710c0..a9bd0c58fb 100644 --- a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt +++ b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt @@ -8,18 +8,4 @@ add_subdirectory(QIRCommon) add_subdirectory(QIRBase) -add_subdirectory(QIRAdaptive) - -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM Copyright (c) 2025 - 2026 Munich -# Quantum Software Company GmbH All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -file(GLOB CONVERSION_SOURCES *.cpp) - -add_mlir_conversion_library(MLIRQCToQIR ${CONVERSION_SOURCES} LINK_LIBS MLIRQCToQIRBase - MLIRQCToQIRAdaptive) - -mqt_mlir_target_use_project_options(MLIRQCToQCO) +add_subdirectory(QIRAdaptive) \ No newline at end of file diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp deleted file mode 100644 index 24611ad606..0000000000 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Conversion/QCToQIR/QCToQIR.h" - -#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" -#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" -#include "mlir/Dialect/QIR/Transforms/Passes.h" - -#include - -void mlir::populateQIRConversionPipeline(mlir::PassManager& pm, - bool useAdaptive) { - if (useAdaptive) { - pm.addPass(createQCToQIRAdaptive()); - } else { - pm.addPass(createQCToQIRBase()); - } - pm.addPass(qir::createAttachQIRAttributes( - qir::AttachQIRAttributesOptions{useAdaptive})); -} diff --git a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp index 771e62b926..1c2a17fdc3 100644 --- a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp +++ b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp @@ -10,7 +10,9 @@ #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" +#include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" +#include "mlir/Support/Passes.h" #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include @@ -130,7 +133,6 @@ Value QIRProgramBuilder::allocQubit() { Value qubit; if (profile == Profile::Adaptive) { ensureAllocationMode(AllocationMode::Dynamic); - metadata_.useDynamicQubit = true; auto fnSig = LLVM::LLVMFunctionType::get(ptrType, {ptrType}); auto fnDec = @@ -139,7 +141,7 @@ Value QIRProgramBuilder::allocQubit() { auto zero = LLVM::ZeroOp::create(*this, ptrType); qubit = LLVM::CallOp::create(*this, fnDec, zero.getResult()).getResult(); } else { - qubit = staticQubit(static_cast(metadata_.numQubits)); + qubit = staticQubit(static_cast(numQubits)); } qubits.insert(qubit); @@ -169,8 +171,8 @@ Value QIRProgramBuilder::staticQubit(const int64_t index) { } // Update qubit count - if (std::cmp_greater_equal(index, metadata_.numQubits)) { - metadata_.numQubits = static_cast(index) + 1; + if (std::cmp_greater_equal(index, numQubits)) { + numQubits = static_cast(index) + 1; } return qubit; @@ -198,8 +200,8 @@ Value QIRProgramBuilder::staticResult(const int64_t index) { } // Update result count - if (std::cmp_greater_equal(index, metadata_.numResults)) { - metadata_.numResults = static_cast(index) + 1; + if (std::cmp_greater_equal(index, numResults)) { + numResults = static_cast(index) + 1; } return result; @@ -228,8 +230,6 @@ QIRProgramBuilder::allocQubitRegister(const int64_t size) { if (profile == Profile::Adaptive) { // Create a dynamic qubit array and load the qubits in the Adaptive Profile ensureAllocationMode(AllocationMode::Dynamic); - metadata_.useArrays = true; - metadata_.useDynamicQubit = true; auto allocFnSignature = LLVM::LLVMFunctionType::get(LLVM::LLVMVoidType::get(getContext()), @@ -257,7 +257,7 @@ QIRProgramBuilder::allocQubitRegister(const int64_t size) { } else { // Create static qubits in the Base Profile for (int64_t i = 0; i < size; ++i) { - auto qubit = staticQubit(static_cast(metadata_.numQubits)); + auto qubit = staticQubit(static_cast(numQubits)); qubits.push_back(qubit); } } @@ -317,8 +317,6 @@ QIRProgramBuilder::allocClassicalBitRegister(const int64_t size, if (profile == Profile::Adaptive) { // Create a dynamic result array for the Adaptive Profile - metadata_.useDynamicResult = true; - metadata_.useArrays = true; auto fnSig = LLVM::LLVMFunctionType::get(voidType, {getI64Type(), ptrType, ptrType}); @@ -343,7 +341,7 @@ QIRProgramBuilder::allocClassicalBitRegister(const int64_t size, } else { // Use static results in the Base Profile for (int64_t i = 0; i < size; ++i) { - auto result = staticResult(static_cast(metadata_.numResults)); + auto result = staticResult(static_cast(numResults)); loadedResults.try_emplace({stringSaver.save(name), i}, result); } } @@ -392,9 +390,7 @@ Value QIRProgramBuilder::measure(Value qubit, const Bit& bit) { llvm::reportFatalUsageError("Bit does not belong to a result pointer"); } auto result = it->second; - if (profile == Profile::Adaptive) { - metadata_.useDynamicResult = true; - } else { + if (profile != Profile::Adaptive) { setInsertionPoint(measurementsBlock->getTerminator()); } @@ -726,11 +722,6 @@ QIRProgramBuilder::scfFor(const std::variant& lowerbound, "Adaptive Profile is selected."); } - int& backwardsBranchingFlag = metadata_.backwardsBranching; - if (backwardsBranchingFlag != 1 && backwardsBranchingFlag != 3) { - backwardsBranchingFlag += 1; - } - auto loc = getLoc(); auto lb = resolveIntVariant(lowerbound); auto ub = resolveIntVariant(upperbound); @@ -853,11 +844,6 @@ QIRProgramBuilder::scfWhile(const function_ref& beforeBody, "Adaptive Profile is selected."); } - int& backwardsBranchingFlag = metadata_.backwardsBranching; - if (backwardsBranchingFlag != 2 && backwardsBranchingFlag != 3) { - backwardsBranchingFlag += 2; - } - auto* currentBlock = getInsertionBlock(); // Build the blocks auto* beforeBlock = createBlock(currentBlock->getParent(), @@ -985,11 +971,12 @@ OwningOpRef QIRProgramBuilder::finalize() { // Save current insertion point const InsertionGuard guard(*this); + const bool isAdaptive = (profile == Profile::Adaptive); // Release resources in output block setInsertionPoint(outputBlock->getTerminator()); - if (profile == Profile::Adaptive) { + if (isAdaptive) { for (auto qubit : qubits) { auto sig = LLVM::LLVMFunctionType::get(voidType, {ptrType}); auto dec = @@ -1009,7 +996,7 @@ OwningOpRef QIRProgramBuilder::finalize() { // Generate output recording in output block generateOutputRecording(); - if (profile == Profile::Adaptive) { + if (isAdaptive) { for (auto& [_, ptr] : resultPtrs) { auto sig = LLVM::LLVMFunctionType::get(voidType, {ptrType}); auto dec = getOrCreateFunctionDeclaration(*this, module, @@ -1026,13 +1013,17 @@ OwningOpRef QIRProgramBuilder::finalize() { } } - auto mainFuncOp = cast(mainFunc); - metadata_.useAdaptive = profile == Profile::Adaptive; - setQIRAttributes(mainFuncOp, metadata_); - + // Attach attributes + auto m = cast(module); + std::ignore = runWithPassManager( + m, + [&](PassManager& pm) { + pm.addPass(qir::createAttachQIRAttributes({isAdaptive})); + }, + "Failed to attach attributes"); isFinalized = true; - return cast(module); + return m; } OwningOpRef QIRProgramBuilder::build( diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp deleted file mode 100644 index db50557574..0000000000 --- a/mlir/lib/Dialect/QIR/Transforms/AttachAttributes.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/QIR/Transforms/Passes.h" -#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" -#include "mlir/Dialect/QIR/Utils/QIRUtils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace mlir::qir { -#define GEN_PASS_DEF_ATTACHQIRATTRIBUTES -#include "mlir/Dialect/QIR/Transforms/Passes.h.inc" - -namespace { -/** - * @brief Attaches the required attributes to the function marked as - * entry_point. - */ -struct AttachQIRAttributes final - : impl::AttachQIRAttributesBase { - using AttachQIRAttributesBase::AttachQIRAttributesBase; - -protected: - void runOnOperation() override { - auto main = getMainFunction(getOperation()); - setQIRAttributes(main, useAdaptive ? getAdaptive(main) : getBase(main)); - } - -private: - /// Count the number of uniquely indexed qubit pointers. - /// Assumes that qubits are constant integers that are converted to - /// an integer pointer and then used in (at least) one quantum instruction. - static size_t getNumQubits(LLVM::LLVMFuncOp& main) { - static constexpr StringRef PREFIX_LABEL = "__quantum__qis"; - - DenseSet seen; - main->walk([&](LLVM::ConstantOp constOp) { - if (constOp.use_empty()) { - return; - } - - const auto intAttr = dyn_cast(constOp.getValue()); - if (!intAttr) { - return; - } - - if (!intAttr.getType().isInteger()) { // Not a ": index". - return; - } - - const auto userIt = - llvm::find_if(constOp->getUsers(), [](Operation* user) { - return isa(user); - }); - if (userIt == constOp->user_end()) { - return; - } - - const auto toPtrOp = cast(*userIt); - const auto callIt = - llvm::find_if(toPtrOp->getUsers(), [](Operation* user) { - auto callOp = dyn_cast(user); - if (!callOp) { - return false; - } - - if (!callOp.getCallee()) { - false; - } - - return callOp.getCallee()->starts_with(PREFIX_LABEL); - }); - - if (callIt == toPtrOp->user_end()) { - return; - } - - // The set ensures that we don't insert the same index multiple times. - seen.insert(intAttr.getValue()); - }); - - return seen.size(); - } - - /// Count the number of uniquely indexed result_record_output statements. - static size_t getNumResults(LLVM::LLVMFuncOp& main) { - static constexpr StringRef REC_FN = "__quantum__rt__result_record_output"; - - DenseSet seen; - main->walk([&](LLVM::CallOp callOp) { - if (!callOp.getCallee()) { - return; - } - - if (*callOp.getCallee() != REC_FN) { - return; - } - - const auto operand = callOp->getOperand(0); - auto toPtrOp = dyn_cast(operand.getDefiningOp()); - if (!toPtrOp) { - return; - } - - const auto arg = toPtrOp.getArg(); - auto constOp = dyn_cast(arg.getDefiningOp()); - if (!constOp) { - return; - } - - const auto intAttr = dyn_cast(constOp.getValue()); - if (!intAttr) { - return; - } - - // The set ensures that we don't insert the same index multiple times. - seen.insert(intAttr.getValue()); - }); - - return seen.size(); - } - - /// Return true, if the entry point contains an `LLVM::CondBrOp`. - static bool hasConditionalBranching(LLVM::LLVMFuncOp& main) { - bool hasConditional{false}; - main->walk([&](LLVM::CondBrOp) { - hasConditional = true; - return WalkResult::interrupt(); - }); - return hasConditional; - } - - /// Return true, if the entry point contains an `LLVM::BrOp` for which the - /// destination dominates the block it terminates. - bool hasBackwardBranching(LLVM::LLVMFuncOp& main) { - bool hasBackward{false}; - const auto& domInfo = getAnalysis(); - std::ignore = main->walk([&](LLVM::BrOp brOp) { - if (domInfo.dominates(brOp.getDest(), brOp->getBlock())) { - hasBackward = true; - return WalkResult::interrupt(); - } - return WalkResult::advance(); - }); - return hasBackward; - } - - /// Return triple of booleans, indicating whether the entry point uses - /// dynamic qubits = [0], dynamic results = [1], or dynamic arrays = [2]. - static std::tuple - hasDynamicQubitAllocation(LLVM::LLVMFuncOp& main) { - static constexpr StringRef QUBIT_ALLOC = "__quantum__rt__qubit_allocate"; - static constexpr StringRef RESULT_ALLOC = "__quantum__rt__result_allocate"; - static constexpr StringRef QUBIT_ARR_ALLOC = - "__quantum__rt__qubit_array_allocate"; - static constexpr StringRef RESULT_ARR_ALLOC = - "__quantum__rt__result_array_allocate"; - - bool useDynamicQubit{false}; - bool useDynamicResult{false}; - bool useArrays{false}; - - main->walk([&](LLVM::CallOp callOp) { - if (!callOp.getCallee()) { - return; - } - - const auto name = *callOp.getCallee(); - if (name == QUBIT_ALLOC) { - useDynamicQubit = true; - } else if (name == RESULT_ALLOC) { - useDynamicResult = true; - } else if (name == QUBIT_ARR_ALLOC) { - useDynamicQubit = true; - useArrays = true; - } else if (name == RESULT_ARR_ALLOC) { - useDynamicResult = true; - useArrays = true; - } - }); - - return std::make_tuple(useDynamicQubit, useDynamicResult, useArrays); - } - - /// Return the metadata for a QIR base profile compliant program. - static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { - return { - .numQubits = getNumQubits(main), - .numResults = getNumResults(main), - .useDynamicQubit = false, - .useDynamicResult = false, - .useArrays = false, - .backwardsBranching = 0, - .useAdaptive = false, - }; - } - - /// Return the metadata for a QIR base profile compliant program. - QIRMetadata getAdaptive(LLVM::LLVMFuncOp& main) { - const auto hasConditional = hasConditionalBranching(main); - const auto hasBackward = hasBackwardBranching(main); - const auto [useDynamicQubit, useDynamicResult, useArrays] = - hasDynamicQubitAllocation(main); - - QIRMetadata md; - md.useAdaptive = true; - md.useDynamicQubit = useDynamicQubit; - md.useDynamicResult = useDynamicResult; - md.useArrays = useArrays; - - if (!useDynamicQubit) { - md.numQubits = getNumQubits(main); - } - - if (!useDynamicResult) { - md.numResults = getNumResults(main); - } - - if (hasConditional) { - md.backwardsBranching = hasBackward ? 3 : 2; - } else if (hasBackward) { - md.backwardsBranching = 1; - } - - return md; - } -}; -} // namespace -} // namespace mlir::qir diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp new file mode 100644 index 0000000000..983f495dac --- /dev/null +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QIR/Transforms/Passes.h" +#include "mlir/Dialect/QIR/Utils/QIRUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mlir::qir { +#define GEN_PASS_DEF_ATTACHQIRATTRIBUTES +#include "mlir/Dialect/QIR/Transforms/Passes.h.inc" + +namespace { + +/// State object for tracking QIR metadata during conversion +struct QIRMetadata { + /// Number of qubits used in the module + size_t numQubits{0}; + /// Number of measurement results stored in the module + size_t numResults{0}; + /// Whether the module uses dynamic qubit management + bool useDynamicQubit{false}; + /// Whether the module uses dynamic result management + bool useDynamicResult{false}; + /// Whether the module uses arrays + bool useArrays{false}; + /// Whether the module uses backward branching (0 = none, 1 = iteration based, + /// 2 = condition based, 3 = both) + int backwardsBranching{0}; +}; + +/** + * @brief Attaches the required attributes to the function marked as + * entry_point. + */ +struct AttachQIRAttributes final + : impl::AttachQIRAttributesBase { + using AttachQIRAttributesBase::AttachQIRAttributesBase; + +protected: + void runOnOperation() override { + IRRewriter rewriter(&getContext()); + auto main = getMainFunction(getOperation()); + setQIRAttributes(main, useAdaptive ? getAdaptive(main) : getBase(main), + rewriter); + } + +private: + /// Clear and set QIR base profile metadata. + /// + /// Adds the required metadata attributes for QIR base profile compliance: + /// - `entry_point`: Marks the main entry point function + /// - `output_labeling_schema`: labeled + /// - `qir_profiles`: base_profile + /// - `required_num_qubits`: Number of qubits used + /// - `required_num_results`: Number of measurement results + /// - `qir_major_version`: 2 + /// - `qir_minor_version`: 1 + /// - `dynamic_qubit_management`: true/false + /// - `dynamic_result_management`: true/false + /// + /// These attributes are required by the QIR specification and inform QIR + /// consumers about the module's resource requirements and capabilities. + void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata, + IRRewriter& rewriter) { + auto m = getOperation(); + const auto createFlag = [&](LLVM::ModFlagBehavior behavior, StringRef name, + int32_t val) { + return LLVM::ModuleFlagAttr::get(m->getContext(), behavior, + rewriter.getStringAttr(name), + rewriter.getI32IntegerAttr(val)); + }; + + const SmallVector attributes{ + rewriter.getStringAttr("entry_point"), + rewriter.getStrArrayAttr({"output_labeling_schema", "labeled"}), + rewriter.getStrArrayAttr({"qir_profiles", useAdaptive + ? "adaptive_profile" + : "base_profile"}), + rewriter.getStrArrayAttr( + {"required_num_qubits", std::to_string(metadata.numQubits)}), + rewriter.getStrArrayAttr( + {"required_num_results", std::to_string(metadata.numResults)})}; + + main->setAttr("passthrough", rewriter.getArrayAttr(attributes)); + + rewriter.setInsertionPointToEnd(m.getBody()); + + SmallVector flags{ + createFlag(LLVM::ModFlagBehavior::Error, "qir_major_version", 2), + createFlag(LLVM::ModFlagBehavior::Max, "qir_minor_version", 1), + createFlag(LLVM::ModFlagBehavior::Error, "dynamic_qubit_management", + static_cast(metadata.useDynamicQubit)), + createFlag(LLVM::ModFlagBehavior::Error, "dynamic_result_management", + static_cast(metadata.useDynamicResult))}; + + if (useAdaptive) { + flags.emplace_back(createFlag(LLVM::ModFlagBehavior::Error, + "backwards_branching", + metadata.backwardsBranching)); + flags.emplace_back(createFlag(LLVM::ModFlagBehavior::Error, "arrays", + static_cast(metadata.useArrays))); + } + + removeExistingModuleFlags(m, rewriter); + LLVM::ModuleFlagsOp::create(rewriter, m.getLoc(), + rewriter.getArrayAttr(flags)); + } + + /// Remove existing module flag operations from module. + /// Note that this might also erase non-QIR module flag operations, but for + /// now, we assume that there are no others. + static void removeExistingModuleFlags(ModuleOp m, IRRewriter& rewriter) { + SmallVector flagOps; + m->walk([&](LLVM::ModuleFlagsOp op) { flagOps.emplace_back(op); }); + for (Operation* op : llvm::make_early_inc_range(flagOps)) { + rewriter.eraseOp(op); + } + } + + /// Count the number of uniquely indexed qubit pointers. + /// Assumes that qubits are constant integers that are converted to + /// an integer pointer and then used in (at least) one quantum instruction. + static size_t getNumQubits(LLVM::LLVMFuncOp& main) { + static constexpr StringRef QIS_PREFIX = "__quantum__qis"; + + DenseSet seen; + main->walk([&](LLVM::ConstantOp constOp) { + if (constOp.use_empty()) { + return; + } + + const auto intAttr = dyn_cast(constOp.getValue()); + if (!intAttr) { + return; + } + + if (!intAttr.getType().isInteger()) { // Not a ": index". + return; + } + + const auto userIt = + llvm::find_if(constOp->getUsers(), [](Operation* user) { + return isa(user); + }); + if (userIt == constOp->user_end()) { + return; + } + + const auto toPtrOp = cast(*userIt); + const auto callIt = + llvm::find_if(toPtrOp->getUses(), [](OpOperand& operand) { + auto callOp = dyn_cast(operand.getOwner()); + if (!callOp) { + return false; + } + + auto callee = callOp.getCallee(); + if (!callee.has_value()) { + return false; + } + + if (*callee == QIR_MEASURE) { + + // The following assumes that the first argument of a + // measurement call is the qubit. This may (or may not) hold in + // the future. + + return operand.getOperandNumber() == 0; + } + + return callee->starts_with(QIS_PREFIX); + }); + if (callIt == toPtrOp->use_end()) { + return; + } + + // The set ensures that we don't insert the same index multiple times. + seen.insert(intAttr.getValue()); + }); + + return seen.size(); + } + + /// Count the number of uniquely indexed result_record_output statements. + static size_t getNumResults(LLVM::LLVMFuncOp& main) { + DenseSet seen; + main->walk([&](LLVM::CallOp callOp) { + if (!callOp.getCallee()) { + return; + } + + if (*callOp.getCallee() != QIR_RECORD_OUTPUT) { + return; + } + + const auto operand = callOp->getOperand(0); + auto toPtrOp = dyn_cast(operand.getDefiningOp()); + if (!toPtrOp) { + return; + } + + const auto arg = toPtrOp.getArg(); + auto constOp = dyn_cast(arg.getDefiningOp()); + if (!constOp) { + return; + } + + const auto intAttr = dyn_cast(constOp.getValue()); + if (!intAttr) { + return; + } + + // The set ensures that we don't insert the same index multiple times. + seen.insert(intAttr.getValue()); + }); + + return seen.size(); + } + + /// Determine whether an `LLVM::CondBrOp` belongs to an iterative loop (true) + /// or a conditionally terminated loop (false). + static bool classifyCondBrOp(LLVM::CondBrOp& condBrOp) { + auto condition = condBrOp.getCondition(); + auto callOp = dyn_cast(condition.getDefiningOp()); + if (!callOp) { + + // If the condition is not produced by a measurement call, we + // consider it a basic loop. + return true; + } + + if (!callOp.getCallee()) { + return true; + } + + if (*callOp.getCallee() == QIR_READ_RESULT) { + + // If the condition has been produced by a measurement call + // (e.g. a until-zero-measurement loop), we found a + // "conditionally terminating loop". + return false; + } + + // Unseen edge case (so far): The condition of the terminator + // operation is produced by a function call, which isn't a + // measurement. + return true; + } + + /// Return pair of booleans, indicating whether the entry point uses + /// iterations = [0] or conditionally terminated loops = [1]. + static std::pair + usesBackwardsBranching(LLVM::LLVMFuncOp& main, const DominanceInfo& domInfo) { + bool useIteration{false}; + bool useCondTerm{false}; + + DenseSet visited; + SmallVector worklist; + + for (Block& block : main.getBlocks()) { + for (Block* successor : block.getSuccessors()) { + if (domInfo.dominates(successor, &block)) { // Find back edge. + Block* header = successor; + Block* tail = █ + + visited.insert(header); + + if (header == tail) { + Operation* terminator = header->getTerminator(); + if (auto condBrOp = dyn_cast(terminator)) { + const auto res = classifyCondBrOp(condBrOp); + if (res) { + useIteration |= true; + } else { + useCondTerm |= true; + } + } + continue; + } + + worklist.push_back(tail); + while (!worklist.empty()) { + Block* curr = worklist.pop_back_val(); + Operation* terminator = curr->getTerminator(); + + if (auto condBrOp = dyn_cast(terminator)) { + const auto res = classifyCondBrOp(condBrOp); + if (res) { + useIteration |= true; + } else { + useCondTerm |= true; + } + } + + for (Block* pred : curr->getPredecessors()) { + if (visited.insert(pred).second) { + worklist.push_back(pred); + } + } + } + + visited.clear(); + } + } + } + + return std::make_pair(useIteration, useCondTerm); + } + + /// Return triple of booleans, indicating whether the entry point uses + /// dynamic qubits = [0], dynamic results = [1], or dynamic arrays = [2]. + static std::tuple usesDynamic(LLVM::LLVMFuncOp& main) { + bool useDynamicQubit{false}; + bool useDynamicResult{false}; + bool useArrays{false}; + + main->walk([&](LLVM::CallOp callOp) { + if (!callOp.getCallee()) { + return; + } + + const auto name = *callOp.getCallee(); + if (name == QIR_QUBIT_ALLOC) { + useDynamicQubit = true; + } else if (name == QIR_RESULT_ALLOC) { + useDynamicResult = true; + } else if (name == QIR_QUBIT_ARRAY_ALLOC) { + useDynamicQubit = true; + useArrays = true; + } else if (name == QIR_RESULT_ARRAY_ALLOC) { + useDynamicResult = true; + useArrays = true; + } + }); + + return std::make_tuple(useDynamicQubit, useDynamicResult, useArrays); + } + + /// Return the metadata for a QIR base profile compliant program. + static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { + return {.numQubits = getNumQubits(main), + .numResults = getNumResults(main), + .useDynamicQubit = false, + .useDynamicResult = false, + .useArrays = false, + .backwardsBranching = 0}; + } + + /// Return the metadata for a QIR base profile compliant program. + QIRMetadata getAdaptive(LLVM::LLVMFuncOp& main) { + const auto& domInfo = getAnalysis(); + const auto [useIteration, useCondTerm] = + usesBackwardsBranching(main, domInfo); + const auto [useDynamicQubit, useDynamicResult, useArrays] = + usesDynamic(main); + + QIRMetadata md; + md.useDynamicQubit = useDynamicQubit; + md.useDynamicResult = useDynamicResult; + md.useArrays = useArrays; + + if (!useDynamicQubit) { + md.numQubits = getNumQubits(main); + } + + if (!useDynamicResult) { + md.numResults = getNumResults(main); + } + + if (useIteration) { + md.backwardsBranching = useCondTerm ? 3 : 1; + } else if (useCondTerm) { + md.backwardsBranching = 2; + } + + return md; + } +}; +} // namespace +} // namespace mlir::qir diff --git a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp index 309a5e9652..821d09beaa 100644 --- a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp +++ b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp @@ -10,8 +10,6 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Dialect/QIR/Utils/QIRMetadata.h" - #include #include #include @@ -55,65 +53,6 @@ LLVM::LLVMFuncOp getMainFunction(Operation* op) { return nullptr; } -void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata) { - auto module = main->getParentOfType(); - if (metadata.useDynamicQubit && metadata.numQubits != 0) { - llvm::reportFatalUsageError( - "Cannot use dynamic qubit allocation if static qubits are allocated"); - } - - OpBuilder builder(main.getBody()); - SmallVector attributes; - - // Core QIR attributes - attributes.emplace_back(builder.getStringAttr("entry_point")); - attributes.emplace_back( - builder.getStrArrayAttr({"output_labeling_schema", "labeled"})); - attributes.emplace_back(builder.getStrArrayAttr( - {"qir_profiles", - metadata.useAdaptive ? "adaptive_profile" : "base_profile"})); - - // Resource requirements - attributes.emplace_back(builder.getStrArrayAttr( - {"required_num_qubits", std::to_string(metadata.numQubits)})); - attributes.emplace_back(builder.getStrArrayAttr( - {"required_num_results", std::to_string(metadata.numResults)})); - - main->setAttr("passthrough", builder.getArrayAttr(attributes)); - - builder.setInsertionPointToEnd(module.getBody()); - - auto createFlag = [&](LLVM::ModFlagBehavior behavior, StringRef name, - int32_t val) { - return LLVM::ModuleFlagAttr::get(module->getContext(), behavior, - builder.getStringAttr(name), - builder.getI32IntegerAttr(val)); - }; - - SmallVector flags; - - flags.push_back( - createFlag(LLVM::ModFlagBehavior::Error, "qir_major_version", 2)); - flags.push_back( - createFlag(LLVM::ModFlagBehavior::Max, "qir_minor_version", 1)); - flags.push_back(createFlag(LLVM::ModFlagBehavior::Error, - "dynamic_qubit_management", - static_cast(metadata.useDynamicQubit))); - flags.push_back(createFlag(LLVM::ModFlagBehavior::Error, - "dynamic_result_management", - static_cast(metadata.useDynamicResult))); - if (metadata.useAdaptive) { - flags.push_back(createFlag(LLVM::ModFlagBehavior::Error, - "backwards_branching", - metadata.backwardsBranching)); - flags.push_back(createFlag(LLVM::ModFlagBehavior::Error, "arrays", - static_cast(metadata.useArrays))); - } - - LLVM::ModuleFlagsOp::create(builder, module.getLoc(), - builder.getArrayAttr(flags)); -} - LLVM::LLVMFuncOp getOrCreateFunctionDeclaration(OpBuilder& builder, Operation* op, StringRef fnName, Type fnType) { diff --git a/mlir/lib/Support/IRVerification.cpp b/mlir/lib/Support/IRVerification.cpp index a1700fa319..61d9312e5c 100644 --- a/mlir/lib/Support/IRVerification.cpp +++ b/mlir/lib/Support/IRVerification.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -308,9 +310,24 @@ static bool compareAttributes(Attribute lhs, Attribute rhs) { !strAttrB || strAttrA.getValue() != strAttrB.getValue()) { return false; } - } else if (auto symbolRefAttrA = - llvm::dyn_cast(lhs)) { - auto symbolRefAttrB = llvm::dyn_cast(rhs); + } else if (auto arrayAttrA = llvm::dyn_cast(lhs)) { + auto arrayAttrB = llvm::dyn_cast(rhs); + if (!arrayAttrB) { + return false; + } + if (arrayAttrA.size() != arrayAttrB.size()) { + return false; + } + + for (const auto& [subAttrA, subAttrB] : + llvm::zip_equal(arrayAttrA, arrayAttrB)) { + if (!compareAttributes(subAttrA, subAttrB)) { + return false; + } + } + + } else if (auto symbolRefAttrA = dyn_cast(lhs)) { + auto symbolRefAttrB = dyn_cast(rhs); if (!symbolRefAttrB) { return false; } @@ -318,6 +335,44 @@ static bool compareAttributes(Attribute lhs, Attribute rhs) { if (symbolRefAttrA.getValue() != symbolRefAttrB.getValue()) { return false; } + } else if (auto tailCallAttrA = dyn_cast(lhs)) { + auto tailCallAttrB = dyn_cast(rhs); + if (!tailCallAttrB) { + return false; + } + + if (tailCallAttrA.getTailCallKind() != tailCallAttrB.getTailCallKind()) { + return false; + } + } else if (auto fastMathAttrA = dyn_cast(lhs)) { + auto fastMathAttrB = dyn_cast(rhs); + if (!fastMathAttrB) { + return false; + } + + if (fastMathAttrA.getValue() != fastMathAttrB.getValue()) { + return false; + } + } else if (auto cconvAttrA = dyn_cast(lhs)) { + auto cconvAttrB = dyn_cast(rhs); + if (!cconvAttrB) { + return false; + } + + if (cconvAttrA.getCallingConv() != cconvAttrB.getCallingConv()) { + return false; + } + } else if (auto modFlagAttrA = dyn_cast(lhs)) { + auto modFlagAttrB = dyn_cast(rhs); + if (!modFlagAttrB) { + return false; + } + + if (modFlagAttrA.getBehavior() != modFlagAttrB.getBehavior() || + modFlagAttrA.getKey() != modFlagAttrB.getKey() || + modFlagAttrA.getValue() != modFlagAttrB.getValue()) { + return false; + } } return true; diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 70a5a499d1..86cb29b5ae 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -28,7 +28,7 @@ static void addSimplificationPasses(PassManager& pm) { pm.addPass(createCSEPass()); } -static LogicalResult +LogicalResult runWithPassManager(ModuleOp module, const function_ref populatePasses, const StringRef errorMessage) { @@ -53,10 +53,11 @@ void populateQCOCleanupPipeline(PassManager& pm) { pm.addPass(createRemoveDeadValuesPass()); } -void populateQIRCleanupPipeline(PassManager& pm) { +void populateQIRCleanupPipeline(PassManager& pm, bool useAdaptive) { addSimplificationPasses(pm); pm.addPass(qir::createQIRCleanupPass()); pm.addPass(createRemoveDeadValuesPass()); + pm.addPass(qir::createAttachQIRAttributes({useAdaptive})); } [[nodiscard]] LogicalResult runQCCleanupPipeline(ModuleOp module) { @@ -69,7 +70,10 @@ void populateQIRCleanupPipeline(PassManager& pm) { "Failed to run QCO cleanup pipeline."); } -[[nodiscard]] LogicalResult runQIRCleanupPipeline(ModuleOp module) { - return runWithPassManager(module, populateQIRCleanupPipeline, - "Failed to run QIR cleanup pipeline."); -} +[[nodiscard]] LogicalResult runQIRCleanupPipeline(ModuleOp module, + bool useAdaptive) { + return runWithPassManager( + module, + [&](PassManager& pm) { populateQIRCleanupPipeline(pm, useAdaptive); }, + "Failed to run QIR cleanup pipeline."); +} \ No newline at end of file diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index 191074ddac..ddc3e4ce4d 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -108,7 +108,7 @@ class CompilerPipelineTest auto module = mlir::qir::QIRProgramBuilder::build( context.get(), builder.fn, mlir::qir::QIRProgramBuilder::Profile::Adaptive); - EXPECT_TRUE(runQIRCleanupPipeline(module.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(module.get(), true).succeeded()); return module; } diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt index 2e5639472d..1b991762b4 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms - MLIRQCToQIR) + MLIRQCToQIRAdaptive) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp index a27053223e..8476c644b0 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp @@ -9,10 +9,11 @@ */ #include "TestCaseUtils.h" -#include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" +#include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" @@ -78,7 +79,7 @@ class QCToQIRAdaptiveTest static LogicalResult runQCToQIRAdaptiveConversion(ModuleOp module) { PassManager pm(module.getContext()); - populateQIRConversionPipeline(pm, true); + pm.addPass(createQCToQIRAdaptive()); return pm.run(module); } @@ -100,7 +101,7 @@ TEST_P(QCToQIRAdaptiveTest, ProgramEquivalence) { printer.record(program.get(), "Converted QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(program.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(program.get(), true).succeeded()); printer.record(program.get(), "Canonicalized Converted QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); @@ -111,7 +112,7 @@ TEST_P(QCToQIRAdaptiveTest, ProgramEquivalence) { printer.record(reference.get(), "Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(reference.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(reference.get(), true).succeeded()); printer.record(reference.get(), "Canonicalized Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt index cc2aecf0a3..e49b73d5d4 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries( MLIRQIRProgramBuilder MLIRQCPrograms MLIRQIRPrograms - MLIRQCToQIR) + MLIRQCToQIRBase) mqt_mlir_configure_unittest_target(${target_name}) diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp index cbf6e062bb..b637247132 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp @@ -9,7 +9,7 @@ */ #include "TestCaseUtils.h" -#include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" @@ -76,7 +76,7 @@ class QCToQIRBaseTest : public testing::TestWithParam { static LogicalResult runQCToQIRBaseConversion(ModuleOp module) { PassManager pm(module.getContext()); - populateQIRConversionPipeline(pm, false); + pm.addPass(createQCToQIRBase()); return pm.run(module); } @@ -98,7 +98,7 @@ TEST_P(QCToQIRBaseTest, ProgramEquivalence) { printer.record(program.get(), "Converted QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(program.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(program.get(), false).succeeded()); printer.record(program.get(), "Canonicalized Converted QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); @@ -109,7 +109,7 @@ TEST_P(QCToQIRBaseTest, ProgramEquivalence) { printer.record(reference.get(), "Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(reference.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(reference.get(), false).succeeded()); printer.record(reference.get(), "Canonicalized Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); From a5607e93e3c498e819be850f256a3da421231735 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:01:24 +0000 Subject: [PATCH 16/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Conversion/QCToQIR/CMakeLists.txt | 2 +- mlir/lib/Support/Passes.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt index a9bd0c58fb..5dac824d4f 100644 --- a/mlir/lib/Conversion/QCToQIR/CMakeLists.txt +++ b/mlir/lib/Conversion/QCToQIR/CMakeLists.txt @@ -8,4 +8,4 @@ add_subdirectory(QIRCommon) add_subdirectory(QIRBase) -add_subdirectory(QIRAdaptive) \ No newline at end of file +add_subdirectory(QIRAdaptive) diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 86cb29b5ae..45e1e3205e 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -76,4 +76,4 @@ void populateQIRCleanupPipeline(PassManager& pm, bool useAdaptive) { module, [&](PassManager& pm) { populateQIRCleanupPipeline(pm, useAdaptive); }, "Failed to run QIR cleanup pipeline."); -} \ No newline at end of file +} From 12634d3ec877220796de74e6bdce4b0e9fc2bc60 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 11:03:54 +0200 Subject: [PATCH 17/32] Fix totalState count --- mlir/lib/Compiler/CompilerPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 4418991a55..f55d9bee4b 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -99,7 +99,7 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // 10. QIR cleanup (optional) auto totalStages = 8; if (convertToQIR) { - totalStages += 3; + totalStages += 2; } auto currentStage = 0; From f7b04c39f090adc918964bd0dcfa493b5937dbf8 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 11:07:34 +0200 Subject: [PATCH 18/32] Fix build --- mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp b/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp index a753acafd5..765b249a41 100644 --- a/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp +++ b/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp @@ -72,7 +72,7 @@ TEST_P(QIRTest, ProgramEquivalence) { printer.record(program.get(), "Original QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(program.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(program.get(), true).succeeded()); printer.record(program.get(), "Canonicalized QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); @@ -82,7 +82,7 @@ TEST_P(QIRTest, ProgramEquivalence) { printer.record(reference.get(), "Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); - EXPECT_TRUE(runQIRCleanupPipeline(reference.get()).succeeded()); + EXPECT_TRUE(runQIRCleanupPipeline(reference.get(), true).succeeded()); printer.record(reference.get(), "Canonicalized Reference QIR IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); From 913f5630367b7908749d34edfbff83be5a6b8e72 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 12:13:09 +0200 Subject: [PATCH 19/32] Remove unused and include missing headers --- mlir/lib/Compiler/CompilerPipeline.cpp | 1 - mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 4 +++- mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp | 1 - mlir/lib/Support/IRVerification.cpp | 1 - .../QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp | 1 - 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index f55d9bee4b..821ccda2ee 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -15,7 +15,6 @@ #include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" #include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" -#include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index 983f495dac..5e93e547a6 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,9 +23,10 @@ #include #include #include -#include #include +#include +#include #include #include diff --git a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp index 821d09beaa..7a0893fc2e 100644 --- a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp +++ b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp @@ -24,7 +24,6 @@ #include #include -#include namespace mlir::qir { diff --git a/mlir/lib/Support/IRVerification.cpp b/mlir/lib/Support/IRVerification.cpp index 61d9312e5c..79ec785ba7 100644 --- a/mlir/lib/Support/IRVerification.cpp +++ b/mlir/lib/Support/IRVerification.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp index 8476c644b0..509f1ff321 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp @@ -13,7 +13,6 @@ #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" -#include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" From bb160f5913be9924f542aac80e007ee544f01171 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 12:14:26 +0200 Subject: [PATCH 20/32] Update CHANGELOG.md [no ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0214758b81..cb32d8946e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637], [#1676], [#1706], [#1776]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1774], [#1781]) + ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1774], [#1781], [#1787]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -403,6 +403,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1787]: https://github.com/munich-quantum-toolkit/core/pull/1787 [#1781]: https://github.com/munich-quantum-toolkit/core/pull/1781 [#1776]: https://github.com/munich-quantum-toolkit/core/pull/1776 [#1774]: https://github.com/munich-quantum-toolkit/core/pull/1774 From b22ea11f886b61f5b884cf89227d09dde189e2a6 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 12:50:49 +0200 Subject: [PATCH 21/32] Simplify conversion --- .../Conversion/QCToQIR/QIRCommon/QIRCommon.h | 3 --- .../Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp | 16 ++++++---------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h index 9c8a0753d7..2434359f3a 100644 --- a/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h +++ b/mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h @@ -67,9 +67,6 @@ struct LoweringState { Block* measurementsBlock{}; Block* outputBlock{}; - /// The number of recorded results in the computation. - size_t numResults = 0; - /// The qubit allocation mode used in the module AllocationMode allocationMode = AllocationMode::Unset; diff --git a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp index d0344c135c..97366772b2 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp @@ -15,6 +15,7 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" +#include #include #include #include @@ -221,20 +222,18 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { Value result; int64_t resultIndex = 0; + const auto nresults = resultPtrs.size(); if (op.getRegisterIndex() && op.getRegisterName() && op.getRegisterSize()) { const auto registerName = op.getRegisterName().value(); const auto registerSize = op.getRegisterSize().value(); const auto registerIndex = op.getRegisterIndex().value(); // Assign a base offset to this register if not yet seen - if (!state.registerOffsets.contains(registerName)) { - state.registerOffsets.try_emplace(registerName, state.numResults); - state.numResults += registerSize; - } - resultIndex = state.registerOffsets[registerName] + - static_cast(registerIndex); + const auto [it, _] = + state.registerOffsets.try_emplace(registerName, nresults); + resultIndex = it->second + static_cast(registerIndex); } else { - resultIndex = static_cast(state.numResults); + resultIndex = static_cast(nresults); } if (resultPtrs.contains(resultIndex)) { @@ -244,9 +243,6 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { rewriter.setInsertionPoint(state.entryBlock->getTerminator()); result = createPointerFromIndex(rewriter, op.getLoc(), resultIndex); resultPtrs.try_emplace(resultIndex, result); - if (std::cmp_greater_equal(resultIndex, state.numResults)) { - state.numResults = resultIndex + 1; - } } // Switch to measurements block From 8d3b27f7be3e10fbf3db015c29a1a5626887a9bb Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 13:01:10 +0200 Subject: [PATCH 22/32] Improve pass naming and description --- .../mlir/Dialect/QIR/Transforms/Passes.td | 5 ++-- .../Dialect/QIR/Builder/QIRProgramBuilder.cpp | 2 +- .../QIR/Transforms/AttachQIRAttributes.cpp | 24 +++++++++---------- mlir/lib/Support/Passes.cpp | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index 86bc12b944..7a876896ba 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -11,10 +11,9 @@ include "mlir/Pass/PassBase.td" -def AttachQIRAttributes : Pass<"attach-qir-attributes", "mlir::ModuleOp"> { +def QIRSetAttributesAndMetadata : Pass<"set-qir-attributes-and-metadata", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; - let summary = "Attaches the required attributes to the function marked as " - "entry_point, compliant with QIR 2.1"; + let summary = "Sets the required attributes to the entry point function and adds the required module flags, compliant with QIR 2.1"; let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */ "true", "Specifies the profile.">]; } diff --git a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp index 1c2a17fdc3..f775264568 100644 --- a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp +++ b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp @@ -1018,7 +1018,7 @@ OwningOpRef QIRProgramBuilder::finalize() { std::ignore = runWithPassManager( m, [&](PassManager& pm) { - pm.addPass(qir::createAttachQIRAttributes({isAdaptive})); + pm.addPass(qir::createQIRSetAttributesAndMetadata({isAdaptive})); }, "Failed to attach attributes"); isFinalized = true; diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index 5e93e547a6..98b40e9e0e 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -31,13 +31,13 @@ #include namespace mlir::qir { -#define GEN_PASS_DEF_ATTACHQIRATTRIBUTES +#define GEN_PASS_DEF_QIRSETATTRIBUTESANDMETADATA #include "mlir/Dialect/QIR/Transforms/Passes.h.inc" namespace { /// State object for tracking QIR metadata during conversion -struct QIRMetadata { +struct Metadata { /// Number of qubits used in the module size_t numQubits{0}; /// Number of measurement results stored in the module @@ -57,16 +57,16 @@ struct QIRMetadata { * @brief Attaches the required attributes to the function marked as * entry_point. */ -struct AttachQIRAttributes final - : impl::AttachQIRAttributesBase { - using AttachQIRAttributesBase::AttachQIRAttributesBase; +struct QIRSetAttributesAndMetadata final + : impl::QIRSetAttributesAndMetadataBase { + using QIRSetAttributesAndMetadataBase::QIRSetAttributesAndMetadataBase; protected: void runOnOperation() override { IRRewriter rewriter(&getContext()); auto main = getMainFunction(getOperation()); - setQIRAttributes(main, useAdaptive ? getAdaptive(main) : getBase(main), - rewriter); + setMetadata(main, useAdaptive ? getAdaptive(main) : getBase(main), + rewriter); } private: @@ -85,8 +85,8 @@ struct AttachQIRAttributes final /// /// These attributes are required by the QIR specification and inform QIR /// consumers about the module's resource requirements and capabilities. - void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata, - IRRewriter& rewriter) { + void setMetadata(LLVM::LLVMFuncOp& main, const Metadata& metadata, + IRRewriter& rewriter) { auto m = getOperation(); const auto createFlag = [&](LLVM::ModFlagBehavior behavior, StringRef name, int32_t val) { @@ -362,7 +362,7 @@ struct AttachQIRAttributes final } /// Return the metadata for a QIR base profile compliant program. - static QIRMetadata getBase(LLVM::LLVMFuncOp& main) { + static Metadata getBase(LLVM::LLVMFuncOp& main) { return {.numQubits = getNumQubits(main), .numResults = getNumResults(main), .useDynamicQubit = false, @@ -372,14 +372,14 @@ struct AttachQIRAttributes final } /// Return the metadata for a QIR base profile compliant program. - QIRMetadata getAdaptive(LLVM::LLVMFuncOp& main) { + Metadata getAdaptive(LLVM::LLVMFuncOp& main) { const auto& domInfo = getAnalysis(); const auto [useIteration, useCondTerm] = usesBackwardsBranching(main, domInfo); const auto [useDynamicQubit, useDynamicResult, useArrays] = usesDynamic(main); - QIRMetadata md; + Metadata md; md.useDynamicQubit = useDynamicQubit; md.useDynamicResult = useDynamicResult; md.useArrays = useArrays; diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 45e1e3205e..bbd56a68c7 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -57,7 +57,7 @@ void populateQIRCleanupPipeline(PassManager& pm, bool useAdaptive) { addSimplificationPasses(pm); pm.addPass(qir::createQIRCleanupPass()); pm.addPass(createRemoveDeadValuesPass()); - pm.addPass(qir::createAttachQIRAttributes({useAdaptive})); + pm.addPass(qir::createQIRSetAttributesAndMetadata({useAdaptive})); } [[nodiscard]] LogicalResult runQCCleanupPipeline(ModuleOp module) { From c64c0e3655285def6fc59f668a867d1591677dc9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:01:54 +0000 Subject: [PATCH 23/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QIR/Transforms/Passes.td | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td index 7a876896ba..d88f5d79fa 100644 --- a/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QIR/Transforms/Passes.td @@ -11,9 +11,11 @@ include "mlir/Pass/PassBase.td" -def QIRSetAttributesAndMetadata : Pass<"set-qir-attributes-and-metadata", "mlir::ModuleOp"> { +def QIRSetAttributesAndMetadata + : Pass<"set-qir-attributes-and-metadata", "mlir::ModuleOp"> { let dependentDialects = ["mlir::LLVM::LLVMDialect"]; - let summary = "Sets the required attributes to the entry point function and adds the required module flags, compliant with QIR 2.1"; + let summary = "Sets the required attributes to the entry point function and " + "adds the required module flags, compliant with QIR 2.1"; let options = [Option<"useAdaptive", "use-adaptive", "bool", /*default= */ "true", "Specifies the profile.">]; } From 2b3b8275686a4fbed9f7c7241134014be26b5376 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 13:02:38 +0200 Subject: [PATCH 24/32] Fix lint --- mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp index 97366772b2..c7043422f1 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp @@ -15,7 +15,6 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include #include #include #include @@ -225,7 +224,6 @@ struct ConvertQCMeasureOp final : StatefulOpConversionPattern { const auto nresults = resultPtrs.size(); if (op.getRegisterIndex() && op.getRegisterName() && op.getRegisterSize()) { const auto registerName = op.getRegisterName().value(); - const auto registerSize = op.getRegisterSize().value(); const auto registerIndex = op.getRegisterIndex().value(); // Assign a base offset to this register if not yet seen From 5c68d9e49ac6fee205a58e17cd40f5b81d68fe9f Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 13:49:57 +0200 Subject: [PATCH 25/32] Fix backward branching logic --- .../QIR/Transforms/AttachQIRAttributes.cpp | 96 ++++++++----------- 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index 98b40e9e0e..b8ab199381 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -242,34 +242,38 @@ struct QIRSetAttributesAndMetadata final return seen.size(); } - /// Determine whether an `LLVM::CondBrOp` belongs to an iterative loop (true) + /// Determine whether an loop (as a set of blocks) is an iterative loop (true) /// or a conditionally terminated loop (false). - static bool classifyCondBrOp(LLVM::CondBrOp& condBrOp) { - auto condition = condBrOp.getCondition(); - auto callOp = dyn_cast(condition.getDefiningOp()); - if (!callOp) { - - // If the condition is not produced by a measurement call, we - // consider it a basic loop. - return true; - } - - if (!callOp.getCallee()) { - return true; - } + static bool classifyLoop(const SmallPtrSet& loop) { + for (Block* block : loop) { + Operation* terminator = block->getTerminator(); + assert(terminator != nullptr); + + if (auto condBrOp = dyn_cast(terminator)) { + auto condition = condBrOp.getCondition(); + auto callOp = dyn_cast(condition.getDefiningOp()); + + // If the condition is not produced by a measurement call, we + // consider it a basic loop. + if (!callOp || !callOp.getCallee()) { + return true; + } - if (*callOp.getCallee() == QIR_READ_RESULT) { + // If the condition has been produced by a measurement call + // (e.g. a until-zero-measurement loop), and breaks outside the loop, + // we found a "conditionally terminating loop". + if (*callOp.getCallee() == QIR_READ_RESULT && + (!loop.contains(condBrOp.getTrueDest()) || + !loop.contains(condBrOp.getFalseDest()))) { + return false; + } - // If the condition has been produced by a measurement call - // (e.g. a until-zero-measurement loop), we found a - // "conditionally terminating loop". - return false; + // Unseen edge case (so far): The condition of the terminator + // operation is produced by a function call, which isn't a + // measurement. + return true; + } } - - // Unseen edge case (so far): The condition of the terminator - // operation is produced by a function call, which isn't a - // measurement. - return true; } /// Return pair of booleans, indicating whether the entry point uses @@ -279,52 +283,36 @@ struct QIRSetAttributesAndMetadata final bool useIteration{false}; bool useCondTerm{false}; - DenseSet visited; - SmallVector worklist; + SmallVector worklist; for (Block& block : main.getBlocks()) { for (Block* successor : block.getSuccessors()) { - if (domInfo.dominates(successor, &block)) { // Find back edge. + if (domInfo.dominates(successor, &block)) { // Back edge. Block* header = successor; Block* tail = █ - visited.insert(header); - - if (header == tail) { - Operation* terminator = header->getTerminator(); - if (auto condBrOp = dyn_cast(terminator)) { - const auto res = classifyCondBrOp(condBrOp); - if (res) { - useIteration |= true; - } else { - useCondTerm |= true; - } - } - continue; + SmallPtrSet loop{header}; + if (header != tail) { + worklist.push_back(tail); } - worklist.push_back(tail); while (!worklist.empty()) { Block* curr = worklist.pop_back_val(); - Operation* terminator = curr->getTerminator(); - - if (auto condBrOp = dyn_cast(terminator)) { - const auto res = classifyCondBrOp(condBrOp); - if (res) { - useIteration |= true; - } else { - useCondTerm |= true; - } - } - for (Block* pred : curr->getPredecessors()) { - if (visited.insert(pred).second) { + if (loop.insert(pred).second) { worklist.push_back(pred); } } } - visited.clear(); + if(classifyLoop(loop)) { + useIteration |= true; + } else { + useCondTerm |= true; + } + + + loop.clear(); } } } From 3443bd869d4d5834e958b186ab36d4e1c00cc26f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:50:26 +0000 Subject: [PATCH 26/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index b8ab199381..8abaa331e3 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -305,13 +305,12 @@ struct QIRSetAttributesAndMetadata final } } - if(classifyLoop(loop)) { + if (classifyLoop(loop)) { useIteration |= true; } else { useCondTerm |= true; } - loop.clear(); } } From b0834cc83827a7fc1b778c3c4b392041f0d2e9ca Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 13:52:10 +0200 Subject: [PATCH 27/32] Apply bunny changes --- .../Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index b8ab199381..1a0d3c899f 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -65,6 +65,9 @@ struct QIRSetAttributesAndMetadata final void runOnOperation() override { IRRewriter rewriter(&getContext()); auto main = getMainFunction(getOperation()); + if (!main) { + return; + } setMetadata(main, useAdaptive ? getAdaptive(main) : getBase(main), rewriter); } @@ -251,6 +254,11 @@ struct QIRSetAttributesAndMetadata final if (auto condBrOp = dyn_cast(terminator)) { auto condition = condBrOp.getCondition(); + + if (isa(condition)) { // Ensure that there is a def-op. + return true; + } + auto callOp = dyn_cast(condition.getDefiningOp()); // If the condition is not produced by a measurement call, we @@ -305,13 +313,12 @@ struct QIRSetAttributesAndMetadata final } } - if(classifyLoop(loop)) { + if (classifyLoop(loop)) { useIteration |= true; } else { useCondTerm |= true; } - loop.clear(); } } @@ -359,7 +366,7 @@ struct QIRSetAttributesAndMetadata final .backwardsBranching = 0}; } - /// Return the metadata for a QIR base profile compliant program. + /// Return the metadata for a QIR adaptive profile compliant program. Metadata getAdaptive(LLVM::LLVMFuncOp& main) { const auto& domInfo = getAnalysis(); const auto [useIteration, useCondTerm] = From 0bc1bbf008b28a2c63c77c14acd861223948bcb7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:54:19 +0000 Subject: [PATCH 28/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0d4704250..eef13a2017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,9 @@ with the exception that minor releases may include breaking changes. [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], - [#1774], [#1781], [#1787]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], - [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], + [#1774], [#1781], [#1787]) + ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], + [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed From 3630496c4944eb4f1bfb86e62f0bdcf9a0bf2c62 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 17 Jun 2026 14:08:09 +0200 Subject: [PATCH 29/32] Remove missing include --- mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index 1a0d3c899f..1f9e96915b 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace mlir::qir { #define GEN_PASS_DEF_QIRSETATTRIBUTESANDMETADATA From cd87972302920a136becff657fa70c953c997eec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:08:48 +0000 Subject: [PATCH 30/32] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index 1f9e96915b..e4015c7254 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -24,12 +24,12 @@ #include #include +#include #include #include #include #include #include -#include namespace mlir::qir { #define GEN_PASS_DEF_QIRSETATTRIBUTESANDMETADATA From 2d8a560b447e106f88ab9f94f78b5adfd32bb63a Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Thu, 18 Jun 2026 07:47:08 +0200 Subject: [PATCH 31/32] Update 'removeExistingModuleFlags' --- mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index e4015c7254..b564140bfd 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -141,9 +141,7 @@ struct QIRSetAttributesAndMetadata final static void removeExistingModuleFlags(ModuleOp m, IRRewriter& rewriter) { SmallVector flagOps; m->walk([&](LLVM::ModuleFlagsOp op) { flagOps.emplace_back(op); }); - for (Operation* op : llvm::make_early_inc_range(flagOps)) { - rewriter.eraseOp(op); - } + llvm::for_each(flagOps, [&](Operation* op) { rewriter.eraseOp(op); }); } /// Count the number of uniquely indexed qubit pointers. From 9aa255c209906c233701057de45b9f380d24fb7f Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Thu, 18 Jun 2026 07:47:40 +0200 Subject: [PATCH 32/32] Update spelling --- mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp index b564140bfd..749762ef79 100644 --- a/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp @@ -244,7 +244,7 @@ struct QIRSetAttributesAndMetadata final return seen.size(); } - /// Determine whether an loop (as a set of blocks) is an iterative loop (true) + /// Determine whether a loop (as a set of blocks) is an iterative loop (true) /// or a conditionally terminated loop (false). static bool classifyLoop(const SmallPtrSet& loop) { for (Block* block : loop) {