Skip to content

Support rtio.rpc op for OQD's AWG end to end pipeline#2577

Open
rniczh wants to merge 16 commits intomainfrom
rniczh/support-rtio-rpc-op
Open

Support rtio.rpc op for OQD's AWG end to end pipeline#2577
rniczh wants to merge 16 commits intomainfrom
rniczh/support-rtio-rpc-op

Conversation

@rniczh
Copy link
Contributor

@rniczh rniczh commented Mar 12, 2026

Context:

A new rtio.rpc operation is added to the RTIO dialect. It represents a host RPC call triggered by the kernel, optionally carrying runtime arguments and supporting both synchronous and async modes. The op is lowered to rpc_send / rpc_recv LLVM calls (the ARTIQ RPC wire protocol). It is required by both AWG control (program_awg, awg_close) and measurement result collection (set_dataset, transfer_data).

The RTIO dialect has no representation for RPC calls. Our use cases:

Use case Callee Args from kernel Mode
AWG setup program_awg none synchronous
AWG teardown awg_close none synchronous
Dataset init set_dataset key (str), value (array ptr) synchronous
Measurement result transfer_data key (str), index (i64), value (f64) async

The ARTIQ runtime exposes rpc_send(int32_t service_id, const char *tag, ...) and rpc_recv(void *result) as extern C functions linked into the ELF.

Description of the Change:

Added rtio.rpc in RTIO dialect.
This op contains the following attributes:

- callee:      The symbolic name of the host-side callable to invoke.
- tag:         type-tag string encoding the return and argument types,
               which is compatiable with the ARTIQ RPC wire protocol.
               (e.g. "n:" for void no-arg, "n:si" for void(str,i32)).
- is_async:    If present the call is async.
- rpc_id:      Populated by the RPC-ID assignment sub-pass.

Benefits:

Possible Drawbacks:

Related GitHub Issues:
[sc-113163]

@rniczh rniczh changed the title Support rtio.rpc op [WIP] Support rtio.rpc op Mar 12, 2026
@rniczh rniczh marked this pull request as draft March 12, 2026 15:49
@rniczh rniczh changed the title [WIP] Support rtio.rpc op Support rtio.rpc op Mar 16, 2026
@rniczh rniczh marked this pull request as ready for review March 16, 2026 16:04
@github-actions
Copy link
Contributor

Hello. You may have forgotten to update the changelog!
Please edit doc/releases/changelog-dev.md on your branch with:

  • A one-to-two sentence description of the change. You may include a small working example for new features.
  • A link back to this PR.
  • Your name (or GitHub username) in the contributors section.

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.00%. Comparing base (d54fdea) to head (936a580).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2577   +/-   ##
=======================================
  Coverage   97.00%   97.00%           
=======================================
  Files         157      157           
  Lines       17470    17470           
  Branches     1686     1686           
=======================================
  Hits        16946    16946           
  Misses        387      387           
  Partials      137      137           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rniczh rniczh changed the title Support rtio.rpc op Support rtio.rpc op for OQD's AWG end to end pipeline Mar 16, 2026
Copy link
Contributor

@mehrdad2m mehrdad2m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @rniczh, Looks great. minor comments

- callee: The symbolic name of the host-side callable to invoke.
- tag: type-tag string encoding the return and argument types,
which is compatiable with the ARTIQ RPC wire protocol.
(e.g. "n:" for void no-arg, "n:si" for void(str,i32)).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ARTIQ related part seems out of context. Ideally we should try to not pollute this dialect with ARTIQ details.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that I think more, this could be a general enough scheme that is not ARTIQ dependant, but still you may want lower to any arbitrary tagging system, so not tying up it up to ARTIQ seams reasonable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's right, for ARTIQ that tag can be derived from this operand types I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the tag adb3b83. which can be derived from the operand/result types during the conversion from rtio to artiq

which is compatiable with the ARTIQ RPC wire protocol.
(e.g. "n:" for void no-arg, "n:si" for void(str,i32)).
- is_async: If present the call is async.
- rpc_id: Populated by the RPC-ID assignment sub-pass.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the rpc_id explanation is a bit too specific. We should explain what the attribute is or why it is useful. not what we do with it in the lowering.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I updated the explanation here. adb3b83
which is the only way that host device to communicate with the RPC server (through the rpc id to determine which function should be used)

//===----------------------------------------------------------------------===//

namespace {
/// ARTIQ RPC tag format: <return_type>:<arg_types>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm again I am wondering if this would be better suited in a lower level place, maybe in the the lowering pass. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit nitpicky given that currently, we only use this dialect in the context of lowering to ARTIQ. So if the work needed to move it down is substantial, I think it would be fine to have it here with a TODO for now since the dialect is not used anywhere else, but keeping it independent of the ARTIQ internal details would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, adb3b83. I removed the tag, it will only be constructed when converting to the artiq llvm ir

size_t colon = tag.find(':');
StringRef returnPart = colon != StringRef::npos ? tag.take_front(colon) : "n";

int slotSize = (returnPart == "n") ? 8 : getSlotSizeForReturnCode(returnPart[0]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we single out "n"? couldn't we include it in the switch case within getSlotSizeForReturnCode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originally I was want to distinguish the n with other types (since it's void). But yeah, I think it's fine to move it into the getSlotSzieForReturnCode function. It doesn't need to be separated out. d36e3b5

StringRef returnPart = colon != StringRef::npos ? tag.take_front(colon) : "n";

int slotSize = (returnPart == "n") ? 8 : getSlotSizeForReturnCode(returnPart[0]);
auto slotTy = LLVM::LLVMArrayType::get(i8Ty, slotSize);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the logic here? I don't understand why we allocate an array of bytes instead of using a concrete resultTy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the wire protocol of the ARTIQ, they only allow you to pass raw bytes buffer with llvm ptr, except for the the rpc id (the first arg).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants