@@ -95,6 +95,50 @@ class QualType;
95
95
X (double , Double) \
96
96
X (long double , LongDouble)
97
97
98
+ class Value ;
99
+
100
+ // / \struct ValueCleanup
101
+ // / \brief Encapsulates destructor invocation for REPL values.
102
+ // /
103
+ // / `ValueCleanup` provides the logic to run object destructors in the JIT
104
+ // / process. It captures the runtime addresses of the destructor wrapper
105
+ // / functions and the object destructor itself.
106
+ // /
107
+ // / Typical usage:
108
+ // / - Constructed when a JIT'd type requires cleanup.
109
+ // / - Attached to a `Value` via `setValueCleanup`.
110
+ // / - Invoked through `operator()(Value&)` to run the destructor on demand.
111
+ struct ValueCleanup {
112
+ using DtorLookupFn =
113
+ std::function<llvm::Expected<llvm::orc::ExecutorAddr>(QualType Ty)>;
114
+ llvm::orc::ExecutionSession *ES;
115
+ llvm::orc::ExecutorAddr DtorWrapperFn;
116
+ llvm::orc::ExecutorAddr DtorFn;
117
+ DtorLookupFn ObjDtor;
118
+ ValueCleanup () = default ;
119
+ ValueCleanup (llvm::orc::ExecutionSession *ES,
120
+ llvm::orc::ExecutorAddr WrapperFn,
121
+ llvm::orc::ExecutorAddr DtorFn, DtorLookupFn Dtor)
122
+ : ES(ES), DtorWrapperFn(WrapperFn), DtorFn(DtorFn),
123
+ ObjDtor (std::move(Dtor)) {}
124
+ ~ValueCleanup () = default ;
125
+ void operator ()(Value &V);
126
+ };
127
+
128
+ // / \class Value
129
+ // / \brief Represents a dynamically typed value in the REPL.
130
+ // /
131
+ // / `Value` provides a type-erased container for runtime values that can be
132
+ // / produced or consumed by the REPL. It supports multiple storage kinds:
133
+ // /
134
+ // / - Builtin scalars (int, float, etc.)
135
+ // / - Arrays of `Value`
136
+ // / - Pointers (with optional pointee tracking)
137
+ // / - Strings
138
+ // / - An empty state (`K_None`)
139
+ // /
140
+ // / `Value` also integrates with `ValueCleanup`, which holds runtime
141
+ // / destructor logic for objects that require cleanup.
98
142
class REPL_EXTERNAL_VISIBILITY Value final {
99
143
public:
100
144
enum BuiltinKind {
@@ -105,6 +149,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
105
149
};
106
150
107
151
private:
152
+ // / Storage for builtin scalar values.
108
153
struct Builtins {
109
154
private:
110
155
BuiltinKind BK = K_Unspecified;
@@ -137,6 +182,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
137
182
#undef X
138
183
};
139
184
185
+ // / Represents an array of `Value` elements.
140
186
struct ArrValue {
141
187
std::vector<Value> Elements;
142
188
uint64_t ArrSize;
@@ -147,6 +193,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
147
193
}
148
194
};
149
195
196
+ // / Represents a pointer. Holds the address and optionally a pointee `Value`.
150
197
struct PtrValue {
151
198
uint64_t Addr = 0 ;
152
199
Value *Pointee; // optional for str
@@ -157,6 +204,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
157
204
}
158
205
};
159
206
207
+ // / Represents a string value (wrapper over std::string).
160
208
struct StrValue {
161
209
std::string StringBuf;
162
210
StrValue (std::string str) : StringBuf(std::move(str)) {}
@@ -173,11 +221,16 @@ class REPL_EXTERNAL_VISIBILITY Value final {
173
221
ValKind VKind = K_None;
174
222
DataType Data;
175
223
224
+ // / Optional cleanup action (e.g. call dtor in JIT runtime).
225
+ std::optional<ValueCleanup> Cleanup = std::nullopt ;
226
+
176
227
public:
177
228
Value () = default ;
178
229
explicit Value (QualType Ty, ValKind K) : Ty(Ty), VKind(K) {}
179
230
Value (const Value &RHS);
180
- Value (Value &&RHS) : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data) {
231
+ Value (Value &&RHS)
232
+ : Ty(RHS.Ty), VKind(RHS.VKind), Data(RHS.Data),
233
+ Cleanup (std::move(RHS.Cleanup)) {
181
234
RHS.VKind = K_None;
182
235
}
183
236
@@ -206,6 +259,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
206
259
destroy ();
207
260
}
208
261
262
+ // ---- Raw buffer conversion ----
209
263
template <typename T> static T as (std::vector<uint8_t > &raw) {
210
264
T v{};
211
265
// assert(raw.size() >= sizeof(T) && "Buffer too small for type!");
@@ -266,12 +320,14 @@ class REPL_EXTERNAL_VISIBILITY Value final {
266
320
const StrValue &asStr () const { return const_cast <Value *>(this )->asStr (); }
267
321
268
322
public:
323
+ // ---- Query helpers ----
269
324
bool hasBuiltinThis (BuiltinKind K) const {
270
325
if (isBuiltin ())
271
326
return asBuiltin ().getKind () == K;
272
327
return false ;
273
328
}
274
329
330
+ // ---- String accessors ----
275
331
void setStrVal (const char *buf) {
276
332
assert (isStr () && " Not a Str" );
277
333
asStr ().StringBuf = buf;
@@ -287,9 +343,10 @@ class REPL_EXTERNAL_VISIBILITY Value final {
287
343
return StringRef (asStr ().StringBuf );
288
344
}
289
345
346
+ // ---- Array accessors ----
290
347
uint64_t getArraySize () const { return asArray ().ArrSize ; }
291
348
292
- uint64_t getArrayInitializedElts () const { return asArray ().ArrSize ; }
349
+ uint64_t getArrayInitializedElts () const { return asArray ().Elements . size () ; }
293
350
294
351
Value &getArrayInitializedElt (unsigned I) {
295
352
assert (isArray () && " Invalid accessor" );
@@ -301,6 +358,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
301
358
return const_cast <Value *>(this )->getArrayInitializedElt (I);
302
359
}
303
360
361
+ // ---- Pointer accessors ----
304
362
bool HasPointee () const {
305
363
assert (isPointer () && " Invalid accessor" );
306
364
return !(asPointer ().Pointee ->isAbsent ());
@@ -317,6 +375,7 @@ class REPL_EXTERNAL_VISIBILITY Value final {
317
375
318
376
uint64_t getAddr () const { return asPointer ().Addr ; }
319
377
378
+ // ---- Builtin setters/getters ----
320
379
#define X (type, name ) \
321
380
void set##name(type Val) { asBuiltin ().set ##name (Val); } \
322
381
type get##name() const { return asBuiltin ().get ##name (); }
@@ -329,10 +388,19 @@ class REPL_EXTERNAL_VISIBILITY Value final {
329
388
void print (llvm::raw_ostream &Out, ASTContext &Ctx) const ;
330
389
void dump (ASTContext &Ctx) const ;
331
390
332
- // ---- clear ----
333
- void clear () { destroy (); }
391
+ // ---- Cleanup & destruction ----
392
+ void setValueCleanup (ValueCleanup VC) {
393
+ assert (!Cleanup.has_value ());
394
+ Cleanup.emplace (std::move (VC));
395
+ }
396
+ void clear () {
397
+ if (Cleanup.has_value ())
398
+ (*Cleanup)(*this );
399
+ destroy ();
400
+ }
334
401
335
402
private:
403
+ // ---- Constructors for each kind ----
336
404
void MakeBuiltIns () {
337
405
assert (isAbsent () && " Bad state change" );
338
406
new ((void *)(char *)&Data) Builtins (BuiltinKind::K_Unspecified);
@@ -408,6 +476,13 @@ class ValueToString {
408
476
std::string ArrayToString (const Value &A);
409
477
};
410
478
479
+ // / \class ValueResultManager
480
+ // / \brief Manages values returned from JIT code.
481
+ // /
482
+ // / Each result is registered with a unique ID and its `QualType`.
483
+ // / The JIT code later calls back into the runtime with that ID, and
484
+ // / `deliverResult` uses it to look up the type, read the value from memory,
485
+ // / and attach any destructor cleanup before making it available to the host.
411
486
class ValueResultManager {
412
487
public:
413
488
using ValueId = uint64_t ;
@@ -418,9 +493,12 @@ class ValueResultManager {
418
493
static std::unique_ptr<ValueResultManager>
419
494
Create (llvm::orc::LLJIT &EE, ASTContext &Ctx, bool IsOutOfProcess = false );
420
495
421
- ValueId registerPendingResult (QualType QT) {
496
+ ValueId registerPendingResult (QualType QT,
497
+ std::optional<ValueCleanup> VC = std::nullopt ) {
422
498
ValueId NewID = NextID.fetch_add (1 , std::memory_order_relaxed);
423
499
IdToType.insert ({NewID, QT});
500
+ if (VC)
501
+ IdToValCleanup.insert ({NewID, std::move (*VC)});
424
502
return NewID;
425
503
}
426
504
@@ -439,6 +517,7 @@ class ValueResultManager {
439
517
llvm::orc::MemoryAccess &MemAcc;
440
518
Value LastVal;
441
519
llvm::DenseMap<ValueId, clang::QualType> IdToType;
520
+ llvm::DenseMap<ValueId, ValueCleanup> IdToValCleanup;
442
521
};
443
522
444
523
} // namespace clang
0 commit comments