diff --git a/mlir/include/mlir/Conversion/QCToQCO/QCToQCO.td b/mlir/include/mlir/Conversion/QCToQCO/QCToQCO.td index 6b1c2d8868..16c08981f0 100644 --- a/mlir/include/mlir/Conversion/QCToQCO/QCToQCO.td +++ b/mlir/include/mlir/Conversion/QCToQCO/QCToQCO.td @@ -14,6 +14,9 @@ def QCToQCO : Pass<"qc-to-qco", "mlir::ModuleOp"> { let description = [{ This pass converts all operations from the QC dialect to their equivalent operations in the QCO dialect. It handles the transformation of qubit references in QC to qubit values in QCO, ensuring that the semantics of quantum operations are preserved during the conversion process. + Control flow must use structured SCF operations. Operations with block + successors, including `cf.br`, `cf.cond_br`, and `cf.switch`, are diagnosed + before conversion. }]; let dependentDialects = ["mlir::arith::ArithDialect", "mlir::qco::QCODialect", diff --git a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp index 322b9ac642..91a06b0911 100644 --- a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp +++ b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -461,10 +460,15 @@ static void commitQubits(LoweringState& state, Operation* anchor, return success(); } -/// Rejects quantum SSA sources unsupported by the lowering state. -[[nodiscard]] static LogicalResult -validateQuantumValueSources(Operation* root) { +/// Rejects input unsupported by the lowering state. +[[nodiscard]] static LogicalResult validateSupportedInput(Operation* root) { const auto result = root->walk([&](Operation* operation) { + if (operation->getNumSuccessors() != 0) { + operation->emitOpError( + "QC-to-QCO does not support unstructured control flow; use SCF " + "operations"); + return WalkResult::interrupt(); + } if (auto returnOp = dyn_cast(operation)) { auto function = returnOp->getParentOfType(); llvm::SmallDenseSet returnedQubits; @@ -2020,7 +2024,7 @@ struct QCToQCO final : impl::QCToQCOBase { LoweringState preflightState; if (failed(validateModifierBodies(moduleOp)) || - failed(validateQuantumValueSources(moduleOp)) || + failed(validateSupportedInput(moduleOp)) || failed(collectRegisterAccesses(moduleOp, preflightState))) { signalPassFailure(); return; @@ -2128,9 +2132,6 @@ struct QCToQCO final : impl::QCToQCOBase { target.addDynamicallyLegalOp( [&](func::CallOp op) { return typeConverter.isLegal(op); }); - // Conversion of qc types in control-flow ops (e.g., cf.br, cf.cond_br) - populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); - // Convert structured parents and their contents first. if (failed(applyPartialConversion(moduleOp, target, std::move(patterns)))) { signalPassFailure(); diff --git a/mlir/unittests/Conversion/QCToQCO/CMakeLists.txt b/mlir/unittests/Conversion/QCToQCO/CMakeLists.txt index 1bcb89dc62..0d783be5e9 100644 --- a/mlir/unittests/Conversion/QCToQCO/CMakeLists.txt +++ b/mlir/unittests/Conversion/QCToQCO/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(${target_name} test_qc_to_qco.cpp) target_link_libraries( ${target_name} PRIVATE MLIRParser + MLIRControlFlowDialect MLIRSupportMQT GTest::gtest_main MLIRQCProgramBuilder diff --git a/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp b/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp index 4803de99c7..0681e2d68a 100644 --- a/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp +++ b/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -808,6 +809,39 @@ module { EXPECT_TRUE(isa(*call.getResult(1).getUsers().begin())); } +TEST_F(QCToQCORegressionTest, RejectsUnstructuredControlFlow) { + context.getOrLoadDialect(); + auto moduleOp = parseSourceString(R"mlir( +module { + func.func @main() attributes {mqt.entry_point} { + %q = qc.static 0 : !qc.qubit + %c = arith.constant true + cf.cond_br %c, ^then, ^else + ^then: + qc.x %q : !qc.qubit + return + ^else: + qc.z %q : !qc.qubit + return + } +} +)mlir", + &context); + ASSERT_TRUE(moduleOp); + ASSERT_TRUE(succeeded(verify(*moduleOp))); + + bool sawExpectedDiagnostic = false; + ScopedDiagnosticHandler handler(&context, [&](Diagnostic& diagnostic) { + sawExpectedDiagnostic |= + StringRef(diagnostic.str()) + .contains("QC-to-QCO does not support unstructured control flow"); + return success(); + }); + + EXPECT_TRUE(failed(runQCToQCOConversion(*moduleOp))); + EXPECT_TRUE(sawExpectedDiagnostic); +} + TEST_F(QCToQCORegressionTest, PreflightRejectsNonOneDimensionalQubitRegisters) { constexpr llvm::StringLiteral source = R"mlir( module {