Skip to content
Open
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 doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

<h3>Improvements 🛠</h3>

* Add checking of the pointer and status code from `runtime_call`.
[(#3150)](https://github.com/PennyLaneAI/catalyst/pull/3150)

* a PennyLane `Backline` is serialized to the `catalyst.backline` module attribute and compiled
through the transport passes.
[(#3068)](https://github.com/PennyLaneAI/catalyst/pull/3068)
Expand Down
78 changes: 51 additions & 27 deletions mlir/lib/Transport/Transforms/TransportToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ Value globalStr(ConversionPatternRewriter &rewriter, Location loc, ModuleOp mod,
ArrayRef<LLVM::GEPArg>{0, 0}, LLVM::GEPNoWrapFlags::inbounds);
}

void emitCheckedStatus(ConversionPatternRewriter &rewriter, Location loc, ModuleOp mod,
StringRef name, ArrayRef<Type> paramTys, ValueRange args, StringRef what) {
auto *ctx = rewriter.getContext();
Value rc = emitCall(rewriter, loc, mod, name, paramTys, i32Ty(ctx), args);
Value msg = globalStr(rewriter, loc, mod, "transport_check_", what);
emitCall(rewriter, loc, mod, "__catalyst__transport__check", {i32Ty(ctx), ptrTy(ctx)}, Type(),
{rc, msg});
}

Value emitCheckedSession(ConversionPatternRewriter &rewriter, Location loc, ModuleOp mod,
StringRef name, ArrayRef<Type> paramTys, ValueRange args, StringRef what) {
auto *ctx = rewriter.getContext();
Value s = emitCall(rewriter, loc, mod, name, paramTys, ptrTy(ctx), args);
Value msg = globalStr(rewriter, loc, mod, "transport_session_", what);
emitCall(rewriter, loc, mod, "__catalyst__transport__check_session", {ptrTy(ctx), ptrTy(ctx)},
Type(), {s, msg});
return s;
}

Value constInt(ConversionPatternRewriter &rewriter, Location loc, Type ty, int64_t v) {
return LLVM::ConstantOp::create(rewriter, loc, ty, rewriter.getIntegerAttr(ty, v));
}
Expand Down Expand Up @@ -121,9 +140,9 @@ struct CreateLowering : public OpConversionPattern<CreateOp> {
Value key = globalStr(rewriter, op.getLoc(), mod, "transport_key_", op.getKey());
Value role =
constInt(rewriter, op.getLoc(), i32Ty(ctx), static_cast<int64_t>(sessTy.getRole()));
Value s = emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__create",
{ptrTy(ctx), ptrTy(ctx), i32Ty(ctx), ptrTy(ctx)}, ptrTy(ctx),
{lib, cfg, role, key});
Value s = emitCheckedSession(rewriter, op.getLoc(), mod, "__catalyst__transport__create",
{ptrTy(ctx), ptrTy(ctx), i32Ty(ctx), ptrTy(ctx)},
{lib, cfg, role, key}, "create");
rewriter.replaceOp(op, s);
return success();
}
Expand All @@ -150,9 +169,9 @@ template <typename OpT, bool Async> struct ConnectLoweringBase : public OpConver
{adaptor.getSession(), peer, port});
rewriter.replaceOp(op, r);
} else {
emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__connect",
{ptrTy(ctx), ptrTy(ctx), IntegerType::get(ctx, 16)}, i32Ty(ctx),
{adaptor.getSession(), peer, port});
emitCheckedStatus(rewriter, op.getLoc(), mod, "__catalyst__transport__connect",
{ptrTy(ctx), ptrTy(ctx), IntegerType::get(ctx, 16)},
{adaptor.getSession(), peer, port}, "connect");
rewriter.eraseOp(op);
}
return success();
Expand All @@ -174,8 +193,8 @@ struct ExchangeKeysLoweringBase : public OpConversionPattern<OpT> {
{ptrTy(ctx)}, i64Ty(ctx), {adaptor.getSession()});
rewriter.replaceOp(op, r);
} else {
emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__exchange_keys",
{ptrTy(ctx)}, i32Ty(ctx), {adaptor.getSession()});
emitCheckedStatus(rewriter, op.getLoc(), mod, "__catalyst__transport__exchange_keys",
{ptrTy(ctx)}, {adaptor.getSession()}, "exchange_keys");
rewriter.eraseOp(op);
}
return success();
Expand All @@ -189,8 +208,8 @@ struct AwaitLowering : public OpConversionPattern<AwaitOp> {
LogicalResult matchAndRewrite(AwaitOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto *ctx = op.getContext();
emitCall(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__await", {i64Ty(ctx)},
i32Ty(ctx), {adaptor.getToken()});
emitCheckedStatus(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__await",
{i64Ty(ctx)}, {adaptor.getToken()}, "await");
rewriter.eraseOp(op);
return success();
}
Expand All @@ -203,8 +222,9 @@ struct EstablishChannelLowering : public OpConversionPattern<EstablishChannelOp>
auto *ctx = op.getContext();
Value transport =
globalStr(rewriter, op.getLoc(), moduleOf(op), "transport_kind_", op.getTransport());
emitCall(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__establish_channel",
{ptrTy(ctx), ptrTy(ctx)}, i32Ty(ctx), {adaptor.getSession(), transport});
emitCheckedStatus(rewriter, op.getLoc(), moduleOf(op),
"__catalyst__transport__establish_channel", {ptrTy(ctx), ptrTy(ctx)},
{adaptor.getSession(), transport}, "establish_channel");
rewriter.eraseOp(op);
return success();
}
Expand All @@ -217,8 +237,9 @@ struct SetCoprocessorFnLowering : public OpConversionPattern<SetCoprocessorFnOp>
auto *ctx = op.getContext();
ModuleOp mod = moduleOf(op);
Value sym = globalStr(rewriter, op.getLoc(), mod, "transport_coproc_fn_", op.getSymbol());
emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__set_coprocessor_fn",
{ptrTy(ctx), ptrTy(ctx)}, i32Ty(ctx), {adaptor.getSession(), sym});
emitCheckedStatus(rewriter, op.getLoc(), mod, "__catalyst__transport__set_coprocessor_fn",
{ptrTy(ctx), ptrTy(ctx)}, {adaptor.getSession(), sym},
"set_coprocessor_fn");
rewriter.eraseOp(op);
return success();
}
Expand All @@ -232,9 +253,10 @@ struct SetMessageSizesLowering : public OpConversionPattern<SetMessageSizesOp> {
Value idx = constInt(rewriter, op.getLoc(), i32Ty(ctx), op.getWorkItemIdx());
Value inB = constInt(rewriter, op.getLoc(), i64Ty(ctx), op.getInBytes());
Value outB = constInt(rewriter, op.getLoc(), i64Ty(ctx), op.getOutBytes());
emitCall(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__set_message_sizes",
{ptrTy(ctx), i32Ty(ctx), i64Ty(ctx), i64Ty(ctx)}, i32Ty(ctx),
{adaptor.getSession(), idx, inB, outB});
emitCheckedStatus(rewriter, op.getLoc(), moduleOf(op),
"__catalyst__transport__set_message_sizes",
{ptrTy(ctx), i32Ty(ctx), i64Ty(ctx), i64Ty(ctx)},
{adaptor.getSession(), idx, inB, outB}, "set_message_sizes");
rewriter.eraseOp(op);
return success();
}
Expand Down Expand Up @@ -288,9 +310,10 @@ struct StagePayloadLowering : public OpConversionPattern<StagePayloadOp> {
auto [srcPtr, bytes] =
memrefPtrAndBytes(rewriter, op.getLoc(), adaptor.getPayload(), memTy);
Value decoderId = constInt(rewriter, op.getLoc(), i32Ty(ctx), op.getDecoderId());
emitCall(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__stage_payload",
{ptrTy(ctx), ptrTy(ctx), i64Ty(ctx), i32Ty(ctx)}, i32Ty(ctx),
{adaptor.getSession(), srcPtr, bytes, decoderId});
emitCheckedStatus(rewriter, op.getLoc(), moduleOf(op),
"__catalyst__transport__stage_payload",
{ptrTy(ctx), ptrTy(ctx), i64Ty(ctx), i32Ty(ctx)},
{adaptor.getSession(), srcPtr, bytes, decoderId}, "stage_payload");
rewriter.eraseOp(op);
return success();
}
Expand All @@ -302,8 +325,8 @@ struct PostLowering : public OpConversionPattern<PostOp> {
ConversionPatternRewriter &rewriter) const override {
auto *ctx = op.getContext();
Value idx = constInt(rewriter, op.getLoc(), i32Ty(ctx), op.getWorkItemIdx());
emitCall(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__post",
{ptrTy(ctx), i32Ty(ctx)}, i32Ty(ctx), {adaptor.getSession(), idx});
emitCheckedStatus(rewriter, op.getLoc(), moduleOf(op), "__catalyst__transport__post",
{ptrTy(ctx), i32Ty(ctx)}, {adaptor.getSession(), idx}, "post");
rewriter.eraseOp(op);
return success();
}
Expand All @@ -326,9 +349,9 @@ struct CollectLowering : public OpConversionPattern<CollectOp> {
return rewriter.notifyMatchFailure(op, "collect dest must have identity layout");
}
auto [dstPtr, bytes] = memrefPtrAndBytes(rewriter, op.getLoc(), adaptor.getDest(), memTy);
emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__collect",
{ptrTy(ctx), ptrTy(ctx), i64Ty(ctx)}, i32Ty(ctx),
{adaptor.getSession(), dstPtr, bytes});
emitCheckedStatus(rewriter, op.getLoc(), mod, "__catalyst__transport__collect",
{ptrTy(ctx), ptrTy(ctx), i64Ty(ctx)},
{adaptor.getSession(), dstPtr, bytes}, "collect");
rewriter.eraseOp(op);
return success();
}
Expand Down Expand Up @@ -371,8 +394,9 @@ struct GetSessionLowering : public OpConversionPattern<GetSessionOp> {
Value role =
constInt(rewriter, op.getLoc(), i32Ty(ctx), static_cast<int64_t>(sessTy.getRole()));
Value key = globalStr(rewriter, op.getLoc(), mod, "transport_key_", op.getKey());
Value s = emitCall(rewriter, op.getLoc(), mod, "__catalyst__transport__get_session",
{i32Ty(ctx), ptrTy(ctx)}, ptrTy(ctx), {role, key});
Value s =
emitCheckedSession(rewriter, op.getLoc(), mod, "__catalyst__transport__get_session",
{i32Ty(ctx), ptrTy(ctx)}, {role, key}, "get_session");
rewriter.replaceOp(op, s);
return success();
}
Expand Down
15 changes: 15 additions & 0 deletions mlir/test/Transport/ConvertTransportToLLVM.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
// RUN: quantum-opt %s --convert-transport-to-llvm --split-input-file | FileCheck %s

// CHECK-DAG: llvm.func @__catalyst__transport__create(!llvm.ptr, !llvm.ptr, i32, !llvm.ptr) -> !llvm.ptr
// CHECK-DAG: llvm.func @__catalyst__transport__check_session(!llvm.ptr, !llvm.ptr)
// CHECK-DAG: llvm.func @__catalyst__transport__check(i32, !llvm.ptr)
// CHECK-DAG: llvm.func @__catalyst__transport__connect(!llvm.ptr, !llvm.ptr, i16) -> i32
// CHECK-DAG: llvm.func @__catalyst__transport__exchange_keys(!llvm.ptr) -> i32
// CHECK-DAG: llvm.func @__catalyst__transport__establish_channel(!llvm.ptr, !llvm.ptr) -> i32
Expand All @@ -30,22 +32,30 @@
// CHECK-LABEL: func.func @controller
func.func @controller(%syndrome: memref<?xi8>, %correction: memref<?xi8>) {
// CHECK: %[[S:.*]] = llvm.call @__catalyst__transport__create({{.*}}) : (!llvm.ptr, !llvm.ptr, i32, !llvm.ptr) -> !llvm.ptr
// CHECK: llvm.call @__catalyst__transport__check_session(%[[S]]
%s = transport.create {backend_lib = "libbackend.so", config = "cfg"} -> !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__connect(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.connect %s {peer = "127.0.0.1", oob_port = 18560 : ui16} : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__exchange_keys(%[[S]])
// CHECK: llvm.call @__catalyst__transport__check(
transport.exchange_keys %s : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__establish_channel(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.establish_channel %s "rdma" : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__set_message_sizes(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.set_message_sizes %s {in_bytes = 8 : i64, out_bytes = 8 : i64} : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__start(%[[S]])
transport.start %s : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__stage_payload(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
// CHECK: llvm.call @__catalyst__transport__post(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.stage_payload %s, %syndrome : !transport.session<controller>, memref<?xi8>
transport.post %s : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__collect(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.collect %s, %correction : !transport.session<controller>, memref<?xi8>
// CHECK: llvm.call @__catalyst__transport__stop(%[[S]])
transport.stop %s : !transport.session<controller>
Expand All @@ -60,12 +70,15 @@ func.func @controller(%syndrome: memref<?xi8>, %correction: memref<?xi8>) {
// CHECK-LABEL: func.func @coprocessor
func.func @coprocessor() {
// CHECK: %[[C:.*]] = llvm.call @__catalyst__transport__create({{.*}}) : (!llvm.ptr, !llvm.ptr, i32, !llvm.ptr) -> !llvm.ptr
// CHECK: llvm.call @__catalyst__transport__check_session(%[[C]]
%c = transport.create {backend_lib = "libbackend.so", config = "cfg"} -> !transport.session<coprocessor>
// CHECK: llvm.call @__catalyst__transport__connect_async(%[[C]]
%t = transport.connect_async %c {peer = "127.0.0.1", oob_port = 18560 : ui16} : !transport.session<coprocessor> -> !transport.token
// CHECK: llvm.call @__catalyst__transport__await
// CHECK: llvm.call @__catalyst__transport__check(
transport.await %t : !transport.token
// CHECK: llvm.call @__catalyst__transport__set_coprocessor_fn(%[[C]], %{{.*}}) : (!llvm.ptr, !llvm.ptr) -> i32
// CHECK: llvm.call @__catalyst__transport__check(
transport.set_coprocessor_fn %c {symbol = "foo"} : !transport.session<coprocessor>
// CHECK: llvm.call @__catalyst__transport__destroy(%[[C]])
transport.destroy %c : !transport.session<coprocessor>
Expand All @@ -80,11 +93,13 @@ func.func @coprocessor() {
func.func @resolve(%syndrome: memref<?xi8>, %correction: memref<?xi8>) {
// CHECK: %[[R:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[S:.*]] = llvm.call @__catalyst__transport__get_session(%[[R]], {{.*}}) : (i32, !llvm.ptr) -> !llvm.ptr
// CHECK: llvm.call @__catalyst__transport__check_session(%[[S]]
%s = transport.get_session {key = "cop0"} : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__post(%[[S]]
transport.stage_payload %s, %syndrome : !transport.session<controller>, memref<?xi8>
transport.post %s : !transport.session<controller>
// CHECK: llvm.call @__catalyst__transport__collect(%[[S]]
// CHECK: llvm.call @__catalyst__transport__check(
transport.collect %s, %correction : !transport.session<controller>, memref<?xi8>
return
}
Expand Down
7 changes: 7 additions & 0 deletions runtime/include/TransportCAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ enum {
CATALYST_TRANSPORT_ROLE_COPROCESSOR = 1,
};

// Abort if `rc` is not CATALYST_TRANSPORT_OK. Compiled-program adapters call this so a failed
// round cannot be mistaken for success. The C functions themselves still return `rc`.
void __catalyst__transport__check(int rc, const char *what);

// Abort if `s` is `nullptr`. Compiled-program adapters call this after create / get_session.
void __catalyst__transport__check_session(CatalystTransportSession *s, const char *what);

// Create a session from a named backend plugin `.so` (dlopen'd by the runtime). `role` selects
// which factory symbol is looked up (controller vs coprocessor). `config` is the backend's string.
// Returns NULL on failure.
Expand Down
23 changes: 21 additions & 2 deletions runtime/lib/transport/TransportCAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "ConfigParser.hpp"
#include "DynamicLibraryLoader.hpp"
#include "Exception.hpp"
#include "Transport.hpp"
#include "TransportBackend.h"
#include "WireProtocol.hpp"
Expand Down Expand Up @@ -255,6 +256,24 @@ void *resolve_coprocessor_fn_symbol(CatalystTransportSession *s, const char *sym

extern "C" {

void __catalyst__transport__check(int rc, const char *what) {
if (rc == ::CATALYST_TRANSPORT_OK) {
return;
}
const std::string msg = std::string{"[transport] "} + ((what != nullptr) ? what : "call") +
" failed (" + collect_error_name(rc) + ")";
RT_FAIL(msg.c_str());
}

void __catalyst__transport__check_session(CatalystTransportSession *s, const char *what) {
if (s != nullptr) {
return;
}
const std::string msg =
std::string{"[transport] "} + ((what != nullptr) ? what : "session") + ": null session";
RT_FAIL(msg.c_str());
}

CatalystTransportSession *__catalyst__transport__create(const char *backend_lib, const char *config,
std::int32_t role, const char *key) {
try {
Expand Down Expand Up @@ -457,8 +476,8 @@ int __catalyst__transport__collect(CatalystTransportSession *s, void *reply,
return s->sess->collect(replies, replies_bytes, 1);
});
if (rc != CATALYST_TRANSPORT_OK) {
// The generated code discards this return value, so a failed round is otherwise silent:
// `reply` keeps whatever it held, and the caller consumes that as a valid result.
// C callers see the return code. Compiled adapters call __catalyst__transport__check so a
// failed round cannot be consumed as a valid reply. Log here so the C path is not silent.
std::cerr << "[transport] collect failed (rc=" << rc << ": " << collect_error_name(rc)
<< "); the reply buffer was not written\n";
}
Expand Down
Loading
Loading