Skip to content

Commit 6ab86f1

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Support int64_t in BackendOption variant for pointer-sized opaque handles (#19232)
Summary: Add an `int64_t` alternative to the `OptionValue` variant in `executorch/runtime/backend/options.h`, plus a matching `set_option(int64_t)` overload on `BackendOptions` and an extended `get_runtime_spec<int64_t>` template in `BackendInitContext`. ## Motivation Backends increasingly need to receive pointer-sized opaque handles at `Method::load_method()` time via the existing `runtime_specs` channel: - NVIDIA CUDA driver handles (e.g., `CUgreenCtx` for green-context-aware TensorRT inference, where the runtime user pre-creates a green context and binds the engine's stream to it via `cuGreenCtxStreamCreate`). - Externally-created CUDA streams shared across delegates. - Vulkan/Metal command queue handles, etc. The current `OptionValue` variant supports `bool`, `int`, and a 256-byte char array. None of these safely round-trips a 64-bit pointer on LP64 platforms — `int` is 32-bit and would silently truncate the upper half of a pointer, producing use-after-free or segfaults at the consumer side. A workaround using the `const char*` arm (stringify the pointer to hex, parse with `strtoull` in the backend) is possible but ugly, error-prone, and unnecessary given how trivial the additive fix is. ## Scope of change - Add `int64_t` to the `OptionValue` variant alternatives. - Add a `set_option(const char (&key)[N], int64_t value)` overload on `BackendOptions`. - Extend `BackendInitContext::get_runtime_spec<T>` template's `static_assert` to allow `T = int64_t` in addition to `bool`, `int`, `const char*`. The internal `std::get_if<T>` already works with the new variant alternative — no other body changes needed. - Add `<cstdint>` include in both headers. ## Properties This change is purely additive: - **Existing consumers see no behavior change.** The `bool`, `int`, and `const char*` paths are untouched. Variant alternatives are appended, not reordered, so `std::variant::index()` semantics for existing values are preserved. - **No serialized format changes.** `runtime_specs` are passed in-process at `Method::load_method` and are never serialized into the .pte file. AOT `CompileSpec` lives in a separate Python schema (flatbuffer-backed) and is unchanged here. - **No public API removed or renamed.** The new `int64_t` overload is selected only when callers explicitly pass `int64_t`-typed values; integer literals continue to bind to the `int` overload. ## Caller usage The intended usage pattern, illustrated for the green-context case: // 1) User creates the opaque resource and owns its lifetime CUgreenCtx green = nullptr; cuGreenCtxCreate(&green, desc, device, CU_GREEN_CTX_DEFAULT_STREAM); // 2) Pass as a runtime spec at Method::load_method time BackendOption opt{}; std::strcpy(opt.key, "green_context"); opt.value = static_cast<int64_t>(reinterpret_cast<intptr_t>(green)); Span<const BackendOption> specs(&opt, 1); // ... thread `specs` through Module::load_program / load_method // 3) In the backend's init(): auto h = context.get_runtime_spec<int64_t>("green_context"); if (h.ok()) { auto green = reinterpret_cast<CUgreenCtx>(h.get()); CUstream raw_stream; cuGreenCtxStreamCreate( &raw_stream, green, CU_STREAM_NON_BLOCKING, /*priority=*/0); // bind raw_stream to the engine for enqueueV3... } Differential Revision: D103241865
1 parent e84a418 commit 6ab86f1

3 files changed

Lines changed: 53 additions & 8 deletions

File tree

runtime/backend/backend_init_context.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <executorch/runtime/core/result.h>
1616
#include <executorch/runtime/core/span.h>
1717

18+
#include <cstdint>
1819
#include <cstring>
1920

2021
#ifdef __GNUC__
@@ -97,7 +98,7 @@ class BackendInitContext final {
9798
/**
9899
* Get a runtime spec value by key and type.
99100
*
100-
* @tparam T The expected type (bool, int, or const char*)
101+
* @tparam T The expected type (bool, int, int64_t, or const char*)
101102
* @param key The option key to look up.
102103
* @return Result containing the value if found and type matches,
103104
* Error::NotFound if key doesn't exist,
@@ -107,8 +108,8 @@ class BackendInitContext final {
107108
Result<T> get_runtime_spec(const char* key) const {
108109
static_assert(
109110
std::is_same_v<T, bool> || std::is_same_v<T, int> ||
110-
std::is_same_v<T, const char*>,
111-
"get_runtime_spec<T> only supports bool, int, and const char*");
111+
std::is_same_v<T, int64_t> || std::is_same_v<T, const char*>,
112+
"get_runtime_spec<T> only supports bool, int, int64_t, and const char*");
112113

113114
for (size_t i = 0; i < runtime_specs_.size(); ++i) {
114115
const auto& opt = runtime_specs_[i];

runtime/backend/options.h

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <executorch/runtime/core/span.h>
1212
#include <array>
1313
#include <cstddef>
14+
#include <cstdint>
1415
#include <cstring>
1516
#include <variant>
1617

@@ -23,8 +24,10 @@ static constexpr size_t kMaxOptionValueLength = 256;
2324
// All string keys and values must have static storage duration (string
2425
// literals, static const char arrays, or global constants). The BackendOptions
2526
// class does NOT take ownership of strings.
27+
// int64_t is included so callers can pass pointer-sized opaque handles
28+
// (e.g., driver handles like CUgreenCtx) by reinterpret_cast to/from int64_t.
2629
using OptionValue =
27-
std::variant<bool, int, std::array<char, kMaxOptionValueLength>>;
30+
std::variant<bool, int, int64_t, std::array<char, kMaxOptionValueLength>>;
2831

2932
struct BackendOption {
3033
// key is the name of the backend option, like num_threads, enable_profiling,
@@ -40,7 +43,9 @@ struct BackendOption {
4043
*
4144
* This class provides a type-safe way to store key-value pairs for backend
4245
* configuration, with compile-time capacity limits and runtime type checking.
43-
* It supports bool, int, and const char* value types.
46+
* It supports bool, int, int64_t, and const char* value types. The int64_t
47+
* arm allows callers to pass pointer-sized opaque handles (e.g., driver
48+
* handles like CUgreenCtx) by reinterpret_cast to/from int64_t.
4449
*
4550
* @tparam MaxCapacity The maximum number of options that can be stored
4651
*/
@@ -113,6 +118,22 @@ class BackendOptions {
113118
return set_option_impl(key, value);
114119
}
115120

121+
/**
122+
* Sets an int64_t option value for the given key.
123+
* Useful for pointer-sized opaque handles (cast via reinterpret_cast).
124+
* If the key already exists, updates its value. Otherwise, adds a new option.
125+
*
126+
* @tparam N The length of the key string (automatically deduced)
127+
* @param key The option key (must be a string literal or array)
128+
* @param value The int64_t value to set
129+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
130+
*/
131+
template <size_t N>
132+
Error set_option(const char (&key)[N], int64_t value) noexcept {
133+
static_assert(N <= kMaxOptionKeyLength, "Option key is too long");
134+
return set_option_impl(key, value);
135+
}
136+
116137
/**
117138
* Sets a string option value for the given key.
118139
* If the key already exists, updates its value. Otherwise, adds a new option.
@@ -137,7 +158,8 @@ class BackendOptions {
137158
/**
138159
* Retrieves an option value by key and type.
139160
*
140-
* @tparam T The expected type of the option value (bool, int, or const char*)
161+
* @tparam T The expected type of the option value (bool, int, int64_t, or
162+
* const char*)
141163
* @tparam KeyLen The length of the key string (automatically deduced)
142164
* @param key The option key to look up
143165
* @param out Reference to store the retrieved value
@@ -157,7 +179,7 @@ class BackendOptions {
157179
return Error::Ok;
158180
}
159181
}
160-
// Default handling for bool/int
182+
// Default handling for bool/int/int64_t
161183
else if (auto* val = std::get_if<T>(&options_[i].value)) {
162184
out = *val;
163185
return Error::Ok;
@@ -176,7 +198,7 @@ class BackendOptions {
176198
* Internal implementation for setting option values.
177199
* Handles both updating existing options and adding new ones.
178200
*
179-
* @tparam T The type of the value (bool, int, or const char*)
201+
* @tparam T The type of the value (bool, int, int64_t, or const char*)
180202
* @param key The option key
181203
* @param value The value to set
182204
* @return Error::Ok on success, Error::InvalidArgument if storage is full

runtime/backend/test/backend_options_test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,28 @@ TEST_F(BackendOptionsTest, HandlesIntOptions) {
6161
EXPECT_EQ(num_threads, 256);
6262
}
6363

64+
// Test int64_t options - exercises the variant arm that holds pointer-sized
65+
// opaque handles. Uses a value with the high 32 bits set to ensure the value
66+
// is not silently routed through (or truncated by) the int arm.
67+
TEST_F(BackendOptionsTest, HandlesInt64Options) {
68+
constexpr int64_t kHandle = static_cast<int64_t>(0x123456789ABCDEF0LL);
69+
options.set_option("handle", kHandle);
70+
71+
int64_t handle = 0;
72+
EXPECT_EQ(options.get_option("handle", handle), Error::Ok);
73+
EXPECT_EQ(handle, kHandle);
74+
75+
// Reading the same key as int should fail because the variant arm differs.
76+
int as_int = 0;
77+
EXPECT_EQ(options.get_option("handle", as_int), Error::InvalidArgument);
78+
79+
// Update existing key with a new int64_t value.
80+
constexpr int64_t kHandle2 = static_cast<int64_t>(0xFEDCBA9876543210LL);
81+
options.set_option("handle", kHandle2);
82+
EXPECT_EQ(options.get_option("handle", handle), Error::Ok);
83+
EXPECT_EQ(handle, kHandle2);
84+
}
85+
6486
// Test error conditions
6587
TEST_F(BackendOptionsTest, HandlesErrors) {
6688
// Non-existent key

0 commit comments

Comments
 (0)