Skip to content

Commit 5be53bc

Browse files
* Properly(?) return measure_result type - may need clean up
- Factory and Marshal * Can use `auto` now * NVQIR function for logging result record seems to have issues Signed-off-by: Pradnya Khalate <pkhalate@nvidia.com>
1 parent b378f8f commit 5be53bc

File tree

12 files changed

+84
-93
lines changed

12 files changed

+84
-93
lines changed

lib/Frontend/nvqpp/ASTBridge.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ listReachableFunctions(clang::CallGraphNode *cgn) {
5353
// Does `ty` refer to a Quake quantum type? This also checks custom recursive
5454
// types. It does not check builtin recursive types; e.g., `!llvm.ptr<T>`.
5555
static bool isQubitType(Type ty) {
56-
if (quake::isQuakeType(ty))
56+
if (quake::isQuantumType(ty))
5757
return true;
5858
// FIXME: next if case is a bug.
5959
if (auto vecTy = dyn_cast<cudaq::cc::StdvecType>(ty))

lib/Optimizer/Builder/Factory.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ Type genBufferType(Type ty) {
7171
assert(!cudaq::cc::isDynamicType(ty) && "must be a type of static extent");
7272
return ty;
7373
}
74+
if (isa<quake::MeasureType>(ty)) {
75+
auto i32Ty = IntegerType::get(ctx, 32);
76+
auto i64Ty = IntegerType::get(ctx, 64);
77+
return cudaq::cc::StructType::get(ctx, {i32Ty, i64Ty});
78+
}
7479
return ty;
7580
}
7681

@@ -430,6 +435,13 @@ Type factory::convertToHostSideType(Type ty, ModuleOp mod) {
430435
return cc::PointerType::get(factory::stlVectorType(
431436
IntegerType::get(ctx, /*FIXME sizeof a pointer?*/ 64)));
432437
}
438+
if (isa<quake::MeasureType>(ty)) {
439+
auto *ctx = ty.getContext();
440+
auto i32Ty = IntegerType::get(ctx, 32);
441+
auto i64Ty = IntegerType::get(ctx, 64);
442+
// Return the `measure_result` struct {int result, size_t uniqueId}
443+
return cc::StructType::get(ctx, {i32Ty, i64Ty});
444+
}
433445
return ty;
434446
}
435447

@@ -644,7 +656,7 @@ FunctionType factory::toHostSideFuncType(FunctionType funcTy, bool addThisPtr,
644656
hasSRet = true;
645657
} else {
646658
assert(funcTy.getNumResults() == 1);
647-
resultTy = funcTy.getResult(0);
659+
resultTy = convertToHostSideType(funcTy.getResult(0), module);
648660
}
649661
}
650662
// If this kernel is a plain old function or a static member function, we

lib/Optimizer/Builder/Intrinsics.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ static constexpr IntrinsicCode intrinsicTable[] = {
457457
)#"},
458458
{cudaq::opt::QIRIntegerRecordOutput, {}, R"#(
459459
func.func private @__quantum__rt__int_record_output(i64, !cc.ptr<i8>)
460+
)#"},
461+
{cudaq::opt::QIRRecordOutput, {}, R"#(
462+
func.func private @__quantum__rt__result_record_output(!cc.ptr<!llvm.struct<"Result", opaque>>, !cc.ptr<i8>)
460463
)#"},
461464
{cudaq::opt::QIRTupleRecordOutput, {}, R"#(
462465
func.func private @__quantum__rt__tuple_record_output(i64, !cc.ptr<i8>)

lib/Optimizer/Builder/Marshal.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,9 @@ std::pair<bool, func::FuncOp> cudaq::opt::marshal::lookupHostEntryPointFunc(
790790
// No host entry point needed.
791791
return {false, func::FuncOp{}};
792792
}
793+
if (!funcOp->hasAttr(cudaq::entryPointAttrName)) {
794+
return {false, func::FuncOp{}};
795+
}
793796
if (auto *decl = module.lookupSymbol(mangledEntryPointName))
794797
if (auto func = dyn_cast<func::FuncOp>(decl)) {
795798
func.eraseBody();

lib/Optimizer/CodeGen/ConvertCCToLLVM.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ void cudaq::opt::populateCCTypeConversions(LLVMTypeConverter *converter) {
8181
return LLVM::LLVMStructType::getLiteral(type.getContext(), members,
8282
type.getPacked());
8383
});
84+
// Add MeasureType conversion for function signatures in CallableType
85+
converter->addConversion([](quake::MeasureType type) -> Type {
86+
auto ctx = type.getContext();
87+
auto i32Ty = IntegerType::get(ctx, 32);
88+
auto i64Ty = IntegerType::get(ctx, 64);
89+
return LLVM::LLVMStructType::getLiteral(ctx, {i32Ty, i64Ty});
90+
});
8491
}
8592

8693
std::size_t cudaq::opt::getDataSize(llvm::DataLayout &dataLayout, Type ty) {

lib/Optimizer/CodeGen/ReturnToOutputLog.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,22 @@ class ReturnRewrite : public OpRewritePattern<cudaq::cc::LogOutputOp> {
170170
cudaq::opt::QIRRecordOutput,
171171
ArrayRef<Value>{val, label});
172172
})
173+
.Case([&](cudaq::cc::PointerType ptrTy) {
174+
// Check if this is a pointer to %Result (converted MeasureType)
175+
if (auto structTy =
176+
dyn_cast<LLVM::LLVMStructType>(ptrTy.getElementType()))
177+
if (structTy.isIdentified() && structTy.getName() == "Result") {
178+
// Handle as measure result
179+
std::string labelStr = "result";
180+
if (prefix)
181+
labelStr = prefix->str();
182+
Value label = makeLabel(loc, rewriter, labelStr);
183+
rewriter.create<func::CallOp>(loc, TypeRange{},
184+
cudaq::opt::QIRRecordOutput,
185+
ArrayRef<Value>{val, label});
186+
return;
187+
}
188+
})
173189
.Default([&](Type) {
174190
// If we reach here, we don't know how to handle this type.
175191
Value one = rewriter.create<arith::ConstantIntOp>(loc, 1, 64);
@@ -236,6 +252,11 @@ struct ReturnToOutputLogPass
236252
signalPassFailure();
237253
return;
238254
}
255+
if (failed(irBuilder.loadIntrinsic(module, cudaq::opt::QIRRecordOutput))) {
256+
module.emitError("could not load QIR result record output function.");
257+
signalPassFailure();
258+
return;
259+
}
239260
if (failed(irBuilder.loadIntrinsic(module, cudaq::opt::QISTrap))) {
240261
module.emitError("could not load QIR trap function.");
241262
signalPassFailure();

runtime/common/RecordLogParser.cpp

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -105,77 +105,6 @@ void cudaq::RecordLogParser::handleOutput(
105105
const std::string &recValue = entries[2];
106106
std::string recLabel = (entries.size() == 4) ? entries[3] : "";
107107
cudaq::trim(recLabel);
108-
if (recType == "RESULT") {
109-
// Sample-type QIR output, where we have an array of `RESULT` per shot. For
110-
// example,
111-
// START
112-
// OUTPUT RESULT 1 r00000
113-
// ....
114-
// OUTPUT RESULT 1 r00009
115-
// END 0
116-
117-
currentOutput = OutputType::RESULT;
118-
const bool isUninitializedContainer =
119-
(containerMeta.m_type == ContainerType::NONE) ||
120-
(containerMeta.m_type == ContainerType::ARRAY &&
121-
containerMeta.elementCount == 0);
122-
if (isUninitializedContainer) {
123-
// NOTE: This is a temporary workaround until all backends consistently
124-
// use the new transformation pass that wraps result records inside an
125-
// array record output. For now, we permit "naked" RESULT records, i.e.,
126-
// if the QIR produced by a sampled kernel emits a sequence of RESULT
127-
// records without enclosing them in an ARRAY, we interpret them
128-
// collectively as an array of results.
129-
// NOTE: This assumption prevents us from correctly supporting `run` with
130-
// `qir-base` profile.
131-
containerMeta.m_type = ContainerType::ARRAY;
132-
containerMeta.elementCount =
133-
std::stoul(metadata[ResultCountMetadataName]);
134-
containerMeta.arrayType = "i1";
135-
preallocateArray();
136-
}
137-
138-
// Note: For ordered schema, we expect the results are sequential in the
139-
// same order that mz operations are called. This may include results in
140-
// named registers (specified in kernel code) and other auto-generated
141-
// register names. If index cannot be extracted from the label, we fall back
142-
// to using this mechanism.
143-
auto idxLabel = std::to_string(containerMeta.processedElements);
144-
145-
// Get the index from the label, if feasible.
146-
/// TODO: The `sample` API should be updated to not allow explicit
147-
/// measurement operations in the kernel when targeting hardware backends.
148-
// Until then, we handle both cases here - auto-generated labels like
149-
// r00000, r00001, ... and named results like result%0, result%1, ...
150-
if (!recLabel.empty()) {
151-
std::size_t percentPos = recLabel.find('%');
152-
if (percentPos != std::string::npos) {
153-
idxLabel = recLabel.substr(percentPos + 1);
154-
}
155-
// This logic is fragile; for example user may have only one mz assigned
156-
// to variable like r00001 and it will be interpreted as index 1, and
157-
// cause `Array index out of bounds` error. The proper fix is to disallow
158-
// explicit mz operations in sampled kernels. Also, `run` is appropriate
159-
// for getting sub-register results.
160-
else if (recLabel.size() == 6 && recLabel[0] == 'r') {
161-
// check that the last 5 characters are all digits
162-
bool allDigits = true;
163-
for (std::size_t i = 1; i < 6; ++i) {
164-
if (recLabel[i] < '0' || recLabel[i] > '9') {
165-
allDigits = false;
166-
break;
167-
}
168-
}
169-
if (allDigits) {
170-
idxLabel = recLabel.substr(1);
171-
}
172-
}
173-
}
174-
175-
processArrayEntry(recValue, fmt::format("[{}]", idxLabel));
176-
containerMeta.processedElements++;
177-
return;
178-
}
179108
if (recType == "ARRAY") {
180109
containerMeta.m_type = ContainerType::ARRAY;
181110
containerMeta.elementCount = std::stoul(recValue);
@@ -196,6 +125,8 @@ void cudaq::RecordLogParser::handleOutput(
196125
}
197126
return;
198127
}
128+
if (recType == "RESULT")
129+
currentOutput = OutputType::RESULT;
199130
if (recType == "BOOL")
200131
currentOutput = OutputType::BOOL;
201132
else if (recType == "INT")

runtime/nvqir/NVQIR.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,23 @@ void __quantum__rt__result_record_output(Result *r, int8_t *name) {
675675
if (ctx && ctx->name == "run") {
676676

677677
std::string regName(reinterpret_cast<const char *>(name));
678+
679+
// Base profile - value stored in measRes2Val
680+
auto iter = measRes2Val.find(r);
681+
if (iter != measRes2Val.end()) {
682+
quantumRTGenericRecordOutput("RESULT", (iter->second ? 1 : 0),
683+
regName.c_str());
684+
return;
685+
}
686+
687+
// Full QIR - r is ResultOne or ResultZero (measurement already happened)
688+
if (r == ResultOne || r == ResultZero) {
689+
bool val = (r == ResultOne);
690+
quantumRTGenericRecordOutput("RESULT", (val ? 1 : 0), regName.c_str());
691+
return;
692+
}
693+
694+
// Fallback - use qubit mapping and re-measure (legacy behavior)
678695
auto qI = qubitToSizeT(measRes2QB[r]);
679696
auto b = nvqir::getCircuitSimulatorInternal()->mz(qI, regName);
680697
quantumRTGenericRecordOutput("RESULT", (b ? 1 : 0), regName.c_str());

runtime/nvqir/QIRTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ void __nvqpp_cleanup_arrays();
8989
}
9090

9191
// Results
92+
/// FIXME: What should this be?
9293
using Result = bool;
9394
static const Result ResultZeroVal = false;
9495
static const Result ResultOneVal = true;

targettests/Kernel/inline-qpu-func.cpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,21 @@
1010

1111
#include "cudaq.h"
1212

13-
// This function has no cudaq::qubit's in the parameter list, so it will be
14-
// tagged as a possible cudaq-entrypoint kernel. Make sure we can still inline
15-
// it if called from another kernel.
16-
/// FIXME: This isn't working anymore
17-
// bool xor_result(const std::vector<cudaq::measure_result> &result_vec) __qpu__
18-
// {
19-
// bool result = false;
20-
// for (auto x : result_vec)
21-
// result ^= x;
22-
// return result;
23-
// }
13+
// This is device only kernel since entry-point kernels cannot accept
14+
// `measure_result` or `std::vector<measure_result>` as parameters.
15+
bool xor_result(const std::vector<cudaq::measure_result> &result_vec) __qpu__ {
16+
bool result = false;
17+
for (auto x : result_vec)
18+
result ^= x;
19+
return result;
20+
}
2421

2522
bool kernel() __qpu__ {
2623
cudaq::qvector q(7);
2724
x(q);
2825
std::vector<cudaq::measure_result> mz_res = mz(q);
29-
bool result = false;
30-
for (auto x : mz_res)
31-
result ^= static_cast<bool>(x);
32-
return result;
26+
bool res = xor_result(mz_res);
27+
return res;
3328
}
3429

3530
int main(int argc, char *argv[]) {

0 commit comments

Comments
 (0)