@@ -820,6 +820,25 @@ struct DiscriminateOpRewrite
820820 ConversionPatternRewriter &rewriter) const override {
821821 auto loc = disc.getLoc ();
822822 Value m = adaptor.getMeasurement ();
823+ // Handle-form: the operand is the `i64` chronological measurement
824+ // index produced by `mz_handle__to__register`. Round-trip it through
825+ // `Result*` and call`read_result__body`, which looks the bit up in
826+ // `measRes2Val` under the inttoptr-encoded `Result*` key populated
827+ // by`mz_handle__to__register`.
828+ if (isa<IntegerType>(m.getType ())) {
829+ auto ctx = rewriter.getContext ();
830+ auto resultPtrTy = cudaq::cc::PointerType::get (
831+ LLVM::LLVMStructType::getOpaque (" Result" , ctx));
832+ auto resAsPtr = cudaq::cc::CastOp::create (rewriter, loc, resultPtrTy, m);
833+ rewriter.replaceOpWithNewOp <func::CallOp>(
834+ disc, rewriter.getI1Type (), cudaq::opt::qir0_1::ReadResultBody,
835+ ValueRange{resAsPtr});
836+ return success ();
837+ }
838+ // Non-handle path: legacy `Result* -> ptr<i1>; load` pattern. Safe
839+ // because the only producer of `Result*` outside the handle path is
840+ // the sentinel-returning `mz` / `mz__to__register`, where
841+ // `Result = bool` and the pointer is dereferenceable.
823842 auto i1PtrTy = cudaq::cc::PointerType::get (rewriter.getI1Type ());
824843 auto cast = cudaq::cc::CastOp::create (rewriter, loc, i1PtrTy, m);
825844 rewriter.replaceOpWithNewOp <cudaq::cc::LoadOp>(disc, cast);
@@ -844,12 +863,22 @@ struct DiscriminateOpToCallRewrite
844863 // expected by the QIR read-result functions.
845864 SmallVector<Value> operands{adaptor.getOperands ().begin (),
846865 adaptor.getOperands ().end ()};
847- if (operands.size () == 1 && isa<IntegerType>(operands.front ().getType ())) {
866+ const bool operandIsHandle =
867+ operands.size () == 1 && isa<IntegerType>(operands.front ().getType ());
868+ if (operandIsHandle) {
848869 auto resultTy = M::getResultType (rewriter.getContext ());
849870 operands.front () =
850871 cudaq::cc::CastOp::create (rewriter, loc, resultTy, operands.front ());
851872 }
852- if constexpr (M::discriminateToClassical) {
873+ // For handle-form callers, the i64 payload is the chronological
874+ // measurement index produced by `mz_handle__to__register`. Loading
875+ // through `Result*` as if it were `bool*` (the legacy bitcast+load
876+ // pattern below) would dereference an integer-encoded pointer and
877+ // segfault — the read-result runtime call is the QIR-spec way to
878+ // recover the bit. The `mz_handle__to__register` adapter populates
879+ // `measRes2Val` keyed by the index-encoded `Result*`, so the lookup
880+ // resolves.
881+ if (operandIsHandle || M::discriminateToClassical) {
853882 if constexpr (M::qirVersion == QirVersion::version_1_0) {
854883 rewriter.replaceOpWithNewOp <func::CallOp>(
855884 disc, rewriter.getI1Type (), cudaq::opt::qir1_0::ReadResult,
@@ -1490,20 +1519,38 @@ struct MeasurementOpPattern : public OpConversionPattern<cudaq::quake::MzOp> {
14901519 adaptor.getTargets ().end ()};
14911520 auto functionName = M::getQIRMeasure ();
14921521
1493- // Handle-form measurements produce a `!cc.measure_handle` SSA value
1494- // whose converted type is `i64`. The QIR measurement function still
1495- // returns `Result*`, so we bridge the call's `Result*` result to the
1496- // converted `i64` payload via `cc.cast`.
1522+ // Handle-form measurements produce a `!cc.measure_handle` SSA value whose
1523+ // converted type is `i64`. Route handle-form callers to the sibling runtime
1524+ // entry
1525+ // `__quantum__qis__mz_handle__to__register` which returns the chronological
1526+ // measurement index directly as `i64` (the QIR Base/ Adaptive Profile
1527+ // convention that the integer encoded in `Result*` identifies the
1528+ // measurement, see
1529+ // https://github.com/qir-alliance/qir-spec/blob/1.0/specification/profiles/Base_Profile.md).
14971530 const bool measOutIsHandle =
14981531 isa<cudaq::cc::MeasureHandleType>(mz.getMeasOut ().getType ());
14991532
15001533 // Are we using the measurement that returns a result?
15011534 if constexpr (M::mzReturnsResultType) {
1502- // Yes, the measurement results the result, so we can use a
1503- // straightforward codegen pattern. Use either the mz or the
1504- // mz_to_register call (with the name as an extra argument) and forward
1505- // the result of the call as the result.
1535+ // Handle-form gets its own runtime entry that returns `i64` directly.
1536+ if (measOutIsHandle) {
1537+ auto cstringGlobal =
1538+ createGlobalCString (mz, loc, rewriter, regNameAttr.getValue ());
1539+ args.push_back (cstringGlobal);
1540+ auto i64Ty = rewriter.getI64Type ();
1541+ auto call = func::CallOp::create (
1542+ rewriter, loc, i64Ty, cudaq::opt::QIRMeasureHandleToRegister, args);
1543+ call->setAttr (cudaq::opt::QIRRegisterNameAttr, regNameAttr);
1544+ SmallVector<Value> replaceVals;
1545+ replaceVals.push_back (call.getResult (0 ));
1546+ auto assundry = filterArgs (mz, adaptor.getTargets ());
1547+ replaceVals.append (assundry.begin (), assundry.end ());
1548+ rewriter.replaceOp (mz, replaceVals);
1549+ return success ();
1550+ }
15061551
1552+ // Non-handle path: use the standard mz / mz__to__register call and
1553+ // forward its `Result*` result unchanged.
15071554 if (mz->getAttr (cudaq::opt::MzAssignedNameAttrName)) {
15081555 functionName = cudaq::opt::QIRMeasureToRegister;
15091556 auto cstringGlobal =
@@ -1515,13 +1562,7 @@ struct MeasurementOpPattern : public OpConversionPattern<cudaq::quake::MzOp> {
15151562 func::CallOp::create (rewriter, loc, resultTy, functionName, args);
15161563 auto assundry = filterArgs (mz, adaptor.getTargets ());
15171564 SmallVector<Value> replaceVals;
1518- if (measOutIsHandle) {
1519- auto i64Ty = rewriter.getI64Type ();
1520- replaceVals.push_back (
1521- cudaq::cc::CastOp::create (rewriter, loc, i64Ty, call.getResult (0 )));
1522- } else {
1523- replaceVals.append (call.getResults ().begin (), call.getResults ().end ());
1524- }
1565+ replaceVals.append (call.getResults ().begin (), call.getResults ().end ());
15251566 replaceVals.append (assundry.begin (), assundry.end ());
15261567 rewriter.replaceOp (mz, replaceVals);
15271568 call->setAttr (cudaq::opt::QIRRegisterNameAttr, regNameAttr);
0 commit comments