Skip to content

Commit 11a3f4c

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Intermediate commit for 1777572697
Differential Revision: D103241865
1 parent e84a418 commit 11a3f4c

4 files changed

Lines changed: 104 additions & 15 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: 37 additions & 11 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

@@ -20,11 +21,15 @@ namespace runtime {
2021
static constexpr size_t kMaxOptionKeyLength = 64;
2122
static constexpr size_t kMaxOptionValueLength = 256;
2223

23-
// All string keys and values must have static storage duration (string
24-
// literals, static const char arrays, or global constants). The BackendOptions
25-
// class does NOT take ownership of strings.
24+
// String keys and string values are COPIED into fixed-size internal buffers
25+
// (key into `BackendOption::key`, value into the `std::array<char, ...>`
26+
// variant arm) and truncated at kMaxOptionKeyLength / kMaxOptionValueLength
27+
// respectively. Callers do NOT need to keep the source strings alive after
28+
// the set_option() call returns.
29+
// The int64_t arm lets callers pass pointer-sized opaque handles (e.g.,
30+
// driver handles like CUgreenCtx) by reinterpret_cast to/from int64_t.
2631
using OptionValue =
27-
std::variant<bool, int, std::array<char, kMaxOptionValueLength>>;
32+
std::variant<bool, int, int64_t, std::array<char, kMaxOptionValueLength>>;
2833

2934
struct BackendOption {
3035
// key is the name of the backend option, like num_threads, enable_profiling,
@@ -40,7 +45,9 @@ struct BackendOption {
4045
*
4146
* This class provides a type-safe way to store key-value pairs for backend
4247
* configuration, with compile-time capacity limits and runtime type checking.
43-
* It supports bool, int, and const char* value types.
48+
* It supports bool, int, int64_t, and const char* value types. The int64_t
49+
* arm allows callers to pass pointer-sized opaque handles (e.g., driver
50+
* handles like CUgreenCtx) by reinterpret_cast to/from int64_t.
4451
*
4552
* @tparam MaxCapacity The maximum number of options that can be stored
4653
*/
@@ -113,16 +120,34 @@ class BackendOptions {
113120
return set_option_impl(key, value);
114121
}
115122

123+
/**
124+
* Sets an int64_t option value for the given key.
125+
* Useful for pointer-sized opaque handles (cast via reinterpret_cast).
126+
* If the key already exists, updates its value. Otherwise, adds a new option.
127+
*
128+
* @tparam N The length of the key string (automatically deduced)
129+
* @param key The option key (must be a string literal or array)
130+
* @param value The int64_t value to set
131+
* @return Error::Ok on success, Error::InvalidArgument if storage is full
132+
*/
133+
template <size_t N>
134+
Error set_option(const char (&key)[N], int64_t value) noexcept {
135+
static_assert(N <= kMaxOptionKeyLength, "Option key is too long");
136+
return set_option_impl(key, value);
137+
}
138+
116139
/**
117140
* Sets a string option value for the given key.
118141
* If the key already exists, updates its value. Otherwise, adds a new option.
119142
*
120-
* Note: The string value must have static storage duration. This class does
121-
* NOT take ownership of the string - it only stores the pointer.
143+
* The string value is copied into an internal fixed-size buffer (truncated
144+
* at kMaxOptionValueLength - 1 characters and null-terminated). The caller
145+
* does NOT need to keep the source string alive after this call returns.
122146
*
123147
* @tparam N The length of the key string (automatically deduced)
124148
* @param key The option key (must be a string literal or array)
125-
* @param value The string value to set (must have static storage duration)
149+
* @param value The string value to set (copied; truncated at
150+
* kMaxOptionValueLength - 1 characters)
126151
* @return Error::Ok on success, Error::InvalidArgument if storage is full
127152
*/
128153
template <size_t N>
@@ -137,7 +162,8 @@ class BackendOptions {
137162
/**
138163
* Retrieves an option value by key and type.
139164
*
140-
* @tparam T The expected type of the option value (bool, int, or const char*)
165+
* @tparam T The expected type of the option value (bool, int, int64_t, or
166+
* const char*)
141167
* @tparam KeyLen The length of the key string (automatically deduced)
142168
* @param key The option key to look up
143169
* @param out Reference to store the retrieved value
@@ -157,7 +183,7 @@ class BackendOptions {
157183
return Error::Ok;
158184
}
159185
}
160-
// Default handling for bool/int
186+
// Default handling for bool/int/int64_t
161187
else if (auto* val = std::get_if<T>(&options_[i].value)) {
162188
out = *val;
163189
return Error::Ok;
@@ -176,7 +202,7 @@ class BackendOptions {
176202
* Internal implementation for setting option values.
177203
* Handles both updating existing options and adding new ones.
178204
*
179-
* @tparam T The type of the value (bool, int, or const char*)
205+
* @tparam T The type of the value (bool, int, int64_t, or const char*)
180206
* @param key The option key
181207
* @param value The value to set
182208
* @return Error::Ok on success, Error::InvalidArgument if storage is full

runtime/backend/test/backend_init_context_test.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ TEST_F(BackendInitContextTest, GetRuntimeSpecIntValid) {
9797
EXPECT_EQ(result2.get(), 32);
9898
}
9999

100+
// Test get_runtime_spec<int64_t> with valid key (covers pointer-sized handle
101+
// use case, e.g., CUDA driver handles like CUgreenCtx).
102+
TEST_F(BackendInitContextTest, GetRuntimeSpecInt64Valid) {
103+
BackendOptions<2> opts;
104+
// Use a value with the top bit clear so the literal is well-defined as a
105+
// signed int64_t across toolchains.
106+
constexpr int64_t kHandle = static_cast<int64_t>(0x123456789ABCDEF0LL);
107+
constexpr int64_t kLargeOffset = static_cast<int64_t>(0x7FFFFFFFFFFFFFFFLL);
108+
opts.set_option("opaque_handle", kHandle);
109+
opts.set_option("large_offset", kLargeOffset);
110+
111+
auto view = opts.view();
112+
Span<const BackendOption> const_span(view.data(), view.size());
113+
114+
BackendInitContext context(nullptr, nullptr, nullptr, nullptr, const_span);
115+
116+
auto result1 = context.get_runtime_spec<int64_t>("opaque_handle");
117+
EXPECT_TRUE(result1.ok());
118+
EXPECT_EQ(result1.get(), kHandle);
119+
120+
auto result2 = context.get_runtime_spec<int64_t>("large_offset");
121+
EXPECT_TRUE(result2.ok());
122+
EXPECT_EQ(result2.get(), kLargeOffset);
123+
}
124+
100125
// Test get_runtime_spec<const char*> with valid key
101126
TEST_F(BackendInitContextTest, GetRuntimeSpecStringValid) {
102127
BackendOptions<2> opts;
@@ -142,9 +167,10 @@ TEST_F(BackendInitContextTest, GetRuntimeSpecNotFound) {
142167

143168
// Test get_runtime_spec<T> with wrong type returns InvalidArgument
144169
TEST_F(BackendInitContextTest, GetRuntimeSpecTypeMismatch) {
145-
BackendOptions<3> opts;
170+
BackendOptions<4> opts;
146171
opts.set_option("bool_opt", true);
147172
opts.set_option("int_opt", 42);
173+
opts.set_option("int64_opt", static_cast<int64_t>(0x123456789ABCDEF0LL));
148174
opts.set_option("string_opt", "hello");
149175

150176
auto view = opts.view();
@@ -166,6 +192,17 @@ TEST_F(BackendInitContextTest, GetRuntimeSpecTypeMismatch) {
166192
auto result3 = context.get_runtime_spec<bool>("string_opt");
167193
EXPECT_FALSE(result3.ok());
168194
EXPECT_EQ(result3.error(), Error::InvalidArgument);
195+
196+
// int and int64_t are distinct variant arms; reading an int as int64_t
197+
// (and vice versa) must fail with InvalidArgument rather than implicit
198+
// narrowing/widening.
199+
auto result4 = context.get_runtime_spec<int64_t>("int_opt");
200+
EXPECT_FALSE(result4.ok());
201+
EXPECT_EQ(result4.error(), Error::InvalidArgument);
202+
203+
auto result5 = context.get_runtime_spec<int>("int64_opt");
204+
EXPECT_FALSE(result5.ok());
205+
EXPECT_EQ(result5.error(), Error::InvalidArgument);
169206
}
170207

171208
// Test empty runtime specs

runtime/backend/test/backend_options_test.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ 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. Use a value with the top
80+
// bit clear so the literal is well-defined as a signed int64_t across all
81+
// toolchains (0xFEDCBA98... would be treated as an unsigned literal on many
82+
// compilers and the cast back to int64_t would be implementation-defined).
83+
constexpr int64_t kHandle2 = static_cast<int64_t>(0x7EDCBA9876543210LL);
84+
options.set_option("handle", kHandle2);
85+
EXPECT_EQ(options.get_option("handle", handle), Error::Ok);
86+
EXPECT_EQ(handle, kHandle2);
87+
}
88+
6489
// Test error conditions
6590
TEST_F(BackendOptionsTest, HandlesErrors) {
6691
// Non-existent key

0 commit comments

Comments
 (0)