@@ -129,42 +129,55 @@ struct ConvertFuncReturnOp final : StatefulOpConversionPattern<func::ReturnOp> {
129129 ConversionPatternRewriter& rewriter) const override {
130130 auto & state = getState ();
131131
132- // Only insert sinks at function exit (never inside nested modifier
133- // regions).
134- if (state.inNestedRegion == 0 ) {
135- llvm::SmallVector<Value> liveQubits;
136- liveQubits.reserve (state.qubitMap .size ());
132+ if (state.inNestedRegion != 0 ) {
133+ rewriter.replaceOpWithNewOp <func::ReturnOp>(op, adaptor.getOperands ());
134+ return success ();
135+ }
137136
138- llvm::DenseSet<Value> escapedQubits;
139- for (Value returned : adaptor.getOperands ()) {
140- escapedQubits.insert (returned);
137+ // Build return values from qubitMap (adaptor.getOperands() may carry stale
138+ // root values because gate patterns use eraseOp instead of replaceOp).
139+ llvm::SmallVector<Value> returnValues;
140+ llvm::DenseSet<Value> escapedQubits;
141+ returnValues.reserve (op.getNumOperands ());
142+ for (auto [qcOperand, adaptorOperand] :
143+ llvm::zip (op.getOperands (), adaptor.getOperands ())) {
144+ if (state.qubitMap .contains (qcOperand)) {
145+ auto latest = state.qubitMap [qcOperand];
146+ returnValues.push_back (latest);
147+ escapedQubits.insert (latest);
148+ } else {
149+ returnValues.push_back (adaptorOperand);
141150 }
151+ }
142152
143- llvm::DenseSet<Value> seen;
144- for (const auto & [/* qcQubit*/ _, qcoQubit] : state.qubitMap ) {
145- if (!escapedQubits.contains (qcoQubit) && seen.insert (qcoQubit).second ) {
146- liveQubits.push_back (qcoQubit);
147- }
153+ // Collect non-escaped live qubits for deallocation.
154+ llvm::SmallVector<Value> liveQubits;
155+ liveQubits.reserve (state.qubitMap .size ());
156+ llvm::DenseSet<Value> seen;
157+ for (const auto & [qcQubit, qcoQubit] : state.qubitMap ) {
158+ if (escapedQubits.contains (qcoQubit) || !seen.insert (qcoQubit).second ) {
159+ continue ;
148160 }
161+ liveQubits.emplace_back (qcoQubit);
162+ }
149163
150- // Sort deterministically (mirrors QCOProgramBuilder::finalize()).
151- llvm::sort (liveQubits, [](Value a, Value b) {
152- auto * opA = a.getDefiningOp ();
153- auto * opB = b.getDefiningOp ();
154- if (!opA || !opB || opA->getBlock () != opB->getBlock ()) {
155- return a.getAsOpaquePointer () < b.getAsOpaquePointer ();
156- }
157- return opA->isBeforeInBlock (opB);
158- });
159-
160- for (auto qubit : liveQubits) {
161- rewriter.create <qco::DeallocOp>(op.getLoc (), qubit);
164+ // Sort deterministically (mirrors QCOProgramBuilder::finalize()).
165+ llvm::sort (liveQubits, [](Value a, Value b) {
166+ auto * opA = a.getDefiningOp ();
167+ auto * opB = b.getDefiningOp ();
168+ if (!opA || !opB || opA->getBlock () != opB->getBlock ()) {
169+ return a.getAsOpaquePointer () < b.getAsOpaquePointer ();
162170 }
171+ return opA->isBeforeInBlock (opB);
172+ });
163173
164- state.qubitMap .clear ();
174+ for (Value qubit : liveQubits) {
175+ rewriter.create <qco::DeallocOp>(op.getLoc (), qubit);
165176 }
166177
167- rewriter.replaceOpWithNewOp <func::ReturnOp>(op, adaptor.getOperands ());
178+ state.qubitMap .clear ();
179+
180+ rewriter.replaceOpWithNewOp <func::ReturnOp>(op, returnValues);
168181 return success ();
169182 }
170183};
@@ -186,9 +199,9 @@ class QCToQCOTypeConverter final : public TypeConverter {
186199 // Identity conversion for all types by default
187200 addConversion ([](Type type) { return type; });
188201
189- // Convert QC qubit references to QCO qubit values
190- addConversion ([ctx](qc::QubitType /* type*/ ) -> Type {
191- return qco::QubitType::get (ctx);
202+ // Convert QC qubit references to QCO qubit values, preserving isStatic
203+ addConversion ([ctx](qc::QubitType type) -> Type {
204+ return qco::QubitType::get (ctx, type. getIsStatic () );
192205 });
193206 }
194207};
@@ -1008,11 +1021,11 @@ struct ConvertQCCtrlOp final : StatefulOpConversionPattern<qc::CtrlOp> {
10081021 " QC ctrl region unexpectedly has entry block arguments" );
10091022 SmallVector<Value> qcoTargetAliases;
10101023 qcoTargetAliases.reserve (numTargets);
1011- const auto qubitType = qco::QubitType::get (qcoOp.getContext ());
10121024 const auto opLoc = op.getLoc ();
10131025 rewriter.modifyOpInPlace (qcoOp, [&] {
10141026 for (auto i = 0UL ; i < numTargets; i++) {
1015- qcoTargetAliases.emplace_back (entryBlock.addArgument (qubitType, opLoc));
1027+ qcoTargetAliases.emplace_back (
1028+ entryBlock.addArgument (qcoTargets[i].getType (), opLoc));
10161029 }
10171030 });
10181031 state.targetsIn [state.inNestedRegion ] = std::move (qcoTargetAliases);
@@ -1092,11 +1105,11 @@ struct ConvertQCInvOp final : StatefulOpConversionPattern<qc::InvOp> {
10921105 " QC inv region unexpectedly has entry block arguments" );
10931106 SmallVector<Value> qcoTargetAliases;
10941107 qcoTargetAliases.reserve (numTargets);
1095- const auto qubitType = qco::QubitType::get (qcoOp.getContext ());
10961108 const auto opLoc = op.getLoc ();
10971109 rewriter.modifyOpInPlace (qcoOp, [&] {
10981110 for (auto i = 0UL ; i < numTargets; i++) {
1099- qcoTargetAliases.emplace_back (entryBlock.addArgument (qubitType, opLoc));
1111+ qcoTargetAliases.emplace_back (
1112+ entryBlock.addArgument (qcoTargets[i].getType (), opLoc));
11001113 }
11011114 });
11021115 targetsIn[inNestedRegion] = std::move (qcoTargetAliases);
@@ -1218,9 +1231,9 @@ struct QCToQCO final : impl::QCToQCOBase<QCToQCO> {
12181231 // Conversion of qc types in func.return
12191232 //
12201233 // Note: `func.return` may already be type-legal even though we still need
1221- // to insert `qco.dealloc` sinks for remaining live qubits. Therefore, we
1222- // make it dynamically illegal unless the lowering state has no remaining
1223- // qubits.
1234+ // to insert sink operations ( `qco.dealloc`) for remaining live
1235+ // qubits. Therefore, we make it dynamically illegal unless the lowering
1236+ // state has no remaining qubits.
12241237 patterns.add <ConvertFuncReturnOp>(typeConverter, context, &state);
12251238 populateReturnOpTypeConversionPattern (patterns, typeConverter);
12261239 target.addDynamicallyLegalOp <func::ReturnOp>([&](const func::ReturnOp op) {
0 commit comments