Commit bcf1d4c
authored
feat: add GPU lowering to selene-hugr-qis-compiler (#1169)
This PR provides lowering for qsystem's GPU extension for
selene-specific workflows.
This is my first compilation PR, so if it's janky I apologise in
advance. There were some battles with getting to grips with inkwell, but
I'm very happy with the result.
# Background
The qsystem GPU extension provides a general mechanism for declaring and
using the API of a gpu library.
For generality, this mechanism involves:
- the user defining functions with:
- any (valid) function name,
- any arrangement of integer, floating point, or boolean arguments,
- a return type of void, float, or integer.
- function names having some mapping to an integer tag
- the ability to acquire and discard a GPU context
- the ability to invoke the aforementioned functions given a gpu
context, an integer tag, and the appropriate parameters
- the ability to retrieve results after invoking said functions
The intent is that the library appropriately fulfilling the user's
specifications is linked with the user program in order to provide the
required functionality. In Selene, this would look like adding the
library as a `utility` at the build stage.
As this is the first such utility that represents a generic usecase,
breakages could get ugly. As such, I took some care to handle future
breaking changes.
# API design
This lowering assumes the following function signatures in the library
to be linked with the user program:
```c
char const* gpu_get_error();
```
This provides a string representing an error that should be made
available upon any of the following functions failing. It should return
nullptr if there is no error to fetch.
Each of the following functions provide bool return values which
represent success (true) or failure (false). Each result is validated
and, upon false, provides an extended panic message to the user
containing the result of `gpu_get_error()` (or "No error message
available" upon a nullptr being returned).
I utilise Selene's `panic_str` QIS extension to emit panics that are not
constrained to 255 bytes, as a custom library may wish to provide far
more detail than would fit in 255 bytes. Given this lowering is specific
to selene, I don't believe that deviating from standard QIS will be a
controversial choice.
```c
bool gpu_validate_api(uint64_t major, uint64_t minor, uint64_t patch);
```
We do not currently know how the remainder of the API will change over
time:
- It's feasible that array arguments, strings etc could be added at a
later time. This will particularly impact the signature API (see the
`gpu_call` description).
- Returning different kinds of data may be useful in future (and so
fetching results may require additional approaches)
As such, this should be the first call to the GPU library, passing an
API version (currently 0.1.0, the version I'm assigning to the API I am
currently describing). It is invoked before any function that depends on
breakable aspects of the API, with caching to avoid multiple calls. It
should return true on success, false otherwise. Upon failure,
gpu_get_error() is called, and therefore **these two functions must be
kept compatible in future editions** if we opt to make breaking changes
in future.
The remainder of functions may be broken in future editions, and as long
as gpu_validate_api is managed appropriately on the library side, we
should at least be able to fail early (at linking or early at runtime)
to prevent undefined behaviour (e.g. by invoking a function with a
modified signature).
```c
bool gpu_init(uint64_t _reserved, uint64_t* gpu_ref_out);
```
The `reserved` identifier may be used in future if we wish to use
multiple instances of gpu libraries, e.g. for maintaining distinct state
between them.
```c
bool gpu_discard(uint64_t gpu_ref);
```
After which, the library should free resources associated with the
`gpu_ref` handle, and further invocations using this handle should
error.
```c
bool gpu_get_function_id(char const* name, uint64_t* id_out)
```
Some implementations may wish to map functions to indices in an array,
or use a hash on the incoming names, or something else.
This is an opportune moment to return false and thus fail early if the
requested function name is not supported by the linked library.
```c
bool gpu_call(
uint64_t handle,
uint64_t function_id,
uint64_t blob_size,
char const* blob,
char const* signature
)
```
The handle and function_id parameters should be apparent at this point.
When a function is invoked, it requires parameters. This is where the
blob and signature come in:
- parameters are packed (in bytes) into an array, which is sent to the
gpu_call.
- I chose this over varargs because varargs aren't particularly friendly
to work with in a diagnostic setting. They can't be passed to other
functions, for example.
- the size is also passed to the library, which allows for quick
validation and safer parsing
- for completeness, a signature string (generated at compile-time) is
also passed as an argument.
- Types are encoded as `i64 => i, u64 => i, f64 => f, bool => b`, and
the inputs and return type are separated by a colon.
- The resulting signature string is in the form e.g.
- `iifb:v` for `(int64, uint64, float, bool) -> void`
- `b:f` for `(bool)->float`
- This is also an opportunity for validation. _Perhaps it should be
moved to `get_function_id` to for validation to take place earlier_.
```c
bool gpu_get_result(uint64_t gpu_ref, uint64_t out_len, char* out_result)
```
This extracts a result from a previous call, in a FIFO manner. Currently
the only `out_len` supported by the underlying hugr is 64 bits, as
functions are assumed to return double or uint64, but this can be
changed at a later date without breaking the API. The lowering provided
in this PR handles the casting and alignment.
# Controversial choices
- It might be more appropriate to return an integer as an error code,
rather than a boolean flag representing success or failure.
However, error codes are primarily useful in areas where we have a
defined system for handling different forms of error. We don't really
have a way of catching those errors in the user code, so we always end
up either continuing or terminating. Bool felt satisfactory here.
In future this could be broken - all we need to do is keep the bool
return for `gpu_validate_api`, which is a clear success or fail case
anyway.
- The use of `panic_str` may seem odd, but I found it very useful during
the implementation of this. A library I am testing out provides stack
traces upon failure, and this helped identify the issue with my calls -
directly in panic messages on the other side, where a normal `panic()`
would have otherwise truncated it. If it helped me, it will help users.
- I chose to force the validation of returned function statuses to _not_
be inline. It's a two-line removal if we want it inline. The primary
reason I chose to do this is that the error handling needlessly clutters
up otherwise-clean LLVM IR. If that isn't a good enough reason I can
remove it.1 parent b82c8ad commit bcf1d4c
File tree
32 files changed
+3192
-0
lines changed- qis-compiler
- python/tests
- resources
- snapshots/test_basic_generation/test_gpu
- aarch64-apple-darwin
- x86_64-apple-darwin
- x86_64-unknown-linux-gnu
- x86_64-windows-msvc
- rust
- snapshots
32 files changed
+3192
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| 40 | + | |
39 | 41 | | |
40 | 42 | | |
41 | 43 | | |
Binary file not shown.
Lines changed: 204 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
0 commit comments