Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions mlir/include/mlir/Conversion/QCToQCO/QCToQCO.td
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 9 additions & 8 deletions mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <llvm/ADT/ScopeExit.h>
#include <mlir/Dialect/Arith/IR/Arith.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/Func/Transforms/FuncConversions.h>
#include <mlir/Dialect/MemRef/IR/MemRef.h>
#include <mlir/Dialect/SCF/IR/SCF.h>
#include <mlir/Dialect/Utils/StaticValueUtils.h>
Expand Down Expand Up @@ -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<func::ReturnOp>(operation)) {
auto function = returnOp->getParentOfType<func::FuncOp>();
llvm::SmallDenseSet<Value, 4> returnedQubits;
Expand Down Expand Up @@ -2020,7 +2024,7 @@ struct QCToQCO final : impl::QCToQCOBase<QCToQCO> {

LoweringState preflightState;
if (failed(validateModifierBodies(moduleOp)) ||
failed(validateQuantumValueSources(moduleOp)) ||
failed(validateSupportedInput(moduleOp)) ||
failed(collectRegisterAccesses(moduleOp, preflightState))) {
signalPassFailure();
return;
Expand Down Expand Up @@ -2128,9 +2132,6 @@ struct QCToQCO final : impl::QCToQCOBase<QCToQCO> {
target.addDynamicallyLegalOp<func::CallOp>(
[&](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();
Expand Down
1 change: 1 addition & 0 deletions mlir/unittests/Conversion/QCToQCO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <llvm/ADT/StringRef.h>
#include <llvm/Support/ErrorHandling.h>
#include <mlir/Dialect/Arith/IR/Arith.h>
#include <mlir/Dialect/ControlFlow/IR/ControlFlow.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/MemRef/IR/MemRef.h>
#include <mlir/Dialect/SCF/IR/SCF.h>
Expand Down Expand Up @@ -808,6 +809,39 @@ module {
EXPECT_TRUE(isa<qtensor::InsertOp>(*call.getResult(1).getUsers().begin()));
}

TEST_F(QCToQCORegressionTest, RejectsUnstructuredControlFlow) {
context.getOrLoadDialect<cf::ControlFlowDialect>();
auto moduleOp = parseSourceString<ModuleOp>(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 {
Expand Down
Loading