/
program.h
323 lines (292 loc) · 13.1 KB
/
program.h
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/* A dispatch program is set of operations that we execute in order to perform
* a dispatch. A dispatch callsite goes through a number of states:
*
* 1. Unlinked. This means that we will unconditionally run the dispatch
* callback. This can set guards, do capture transformations, delegate,
* and eventually ends up at a dispatch terminal. This set of steps make
* up the dispatch program; we say that the dispatch callback "records" a
* dispatch program.
* 2. Monomorphic. This means that we have a single dispatch program recorded
* and we try to play it back. The dispatch program is a series of linear
* steps.
* 3. Polymorphic. This means that we have recorded multiple dispatch programs
* at this callsite. We store each program, but may also store a tree or
* other prefix-lifting form of the programs. (However, the first cut will
* probably just run them in order.)
* 4. Megamorphic. This means that we have reached the maximum number of
* dispatch programs we're willing to record here. Now we either call a
* megamorphic handler callback if one is registered, or otherwise run
* the dispatch program but without building up any guards.
*
* These states are modelled as different callbacks in the inline cache at the
* callsite.
*/
/* Kinds of outcome of a dispatch program. */
typedef enum {
/* Indicates we failed to produce an outcome, probably due to an exception. */
MVM_DISP_OUTCOME_FAILED,
/* Indicates we expect the userspace dispatch we're running to specify another
* dispatcher to delegate to. */
MVM_DISP_OUTCOME_EXPECT_DELEGATE,
/* Return a value (produced by the dispatch program). */
MVM_DISP_OUTCOME_VALUE,
/* Invoke bytecode. */
MVM_DISP_OUTCOME_BYTECODE,
/* Invoke a C function. */
MVM_DISP_OUTCOME_CFUNCTION
} MVMDispProgramOutcomeKind;
/* The outcome of a dispatch program. Used for both the record and run case. */
struct MVMDispProgramOutcome {
/* The kind of outcome we have. */
MVMDispProgramOutcomeKind kind;
/* Data that goes with each outcome kind. */
union {
/* A value to return, along with the kind of value it is. Marked. */
struct {
MVMRegister result_value;
MVMuint8 result_kind;
};
/* A invocation of either bytecode or a C function. */
struct {
union {
/* The code object to invoke. Marked. */
MVMCode *code;
/* The C function to be invoked. */
void (*c_func) (MVMThreadContext *tc, MVMArgs arg_info);
};
/* Arguments for an invocation (must point into otherwise marked
* areas). */
MVMArgs args;
};
/* A dispatcher delegation. */
struct {
MVMDispDefinition *delegate_disp;
MVMObject *delegate_capture;
};
};
};
/* A value that is involved in the dispatch. It may come from the initial
* arguments, it may be an inserted constant, or it may be from an attribute
* read out of another value. */
typedef enum {
/* A value from the initial capture. */
MVMDispProgramRecordingCaptureValue,
/* A literal constant value. */
MVMDispProgramRecordingLiteralValue,
/* A read of an attribute from a dependent value. */
MVMDispProgramRecordingAttributeValue
} MVMDispProgramRecordingValueSource;
struct MVMDispProgramRecordingValue {
/* The source of the value, which determines which part of the union below
* that we shall look at. */
MVMDispProgramRecordingValueSource source;
/* Details (depends on source). */
union {
struct {
/* The index within the initial arguments capture. */
MVMuint32 index;
} capture;
struct {
/* The literal value and its kind. */
MVMRegister value;
MVMCallsiteFlags kind;
} literal;
// TODO struct { } attribute;
};
/* The tracking object, if any. */
MVMObject *tracked;
/* Basic guards that have been applied. When we compile the guard program,
* we'll often simplify this; for example, if the incoming argument was a
* literal string then we'd drop the literal value guard, or if it has a
* literal guard then the type and concreteness guards are implicitly
* covered anyway. */
MVMuint8 guard_type;
MVMuint8 guard_concreteness;
MVMuint8 guard_literal;
/* A list of objects that this value must *not* be. */
MVM_VECTOR_DECL(MVMObject *, not_literal_guards);
};
/* A derived capture. */
typedef enum {
MVMDispProgramRecordingInitial,
MVMDispProgramRecordingDrop,
MVMDispProgramRecordingInsert
} MVMDispProgramRecordingTransformation;
struct MVMDispProgramRecordingCapture {
/* The capture object we produced and handed back. */
MVMObject *capture;
/* The kind of transformation that it did. */
MVMDispProgramRecordingTransformation transformation;
/* The index involved in the insert or drop. */
MVMuint32 index;
/* For inserts, the index of the value that was involved. */
MVMuint32 value_index;
/* Tree of captures further derived from the this one. */
MVM_VECTOR_DECL(MVMDispProgramRecordingCapture, captures);
};
/* Recording state of a dispatch program, updated as we move through the record
* phase. */
struct MVMDispProgramRecording {
/* The initial argument capture, top of the tree of captures. */
MVMDispProgramRecordingCapture initial_capture;
/* The values that we have encountered while recording, and maybe added
* guards against. */
MVM_VECTOR_DECL(MVMDispProgramRecordingValue, values);
/* The index of the value that is the outcome of the dispatch. For a value
* outcome, it's the value we'll produce. For the invocations, it's the
* code or C function value. */
MVMuint32 outcome_value;
/* For an invocation outcome, this is the capture that we shall invoke the
* arguments with. Will be located within the capture tree somewhere. */
MVMObject *outcome_capture;
};
/* A "compiled" dispatch program, which is what we interpret at a callsite
* (at least, until specialization, where more exciting things happen). */
struct MVMDispProgram {
/* GC-managed constants (objects, strings, and STables) used in the
* dispatch. */
MVMCollectable **gc_constants;
/* Non-GC-managed constants used in the dispatch. */
MVMDispProgramConstant *constants;
/* The number of temporaries. Temporaries are used while evaluating
* the dispatch program, and may also be used as an argument buffer
* when we cannot just point into a tail of the original one. */
MVMuint32 num_temporaries;
/* The first of the temporaries that is used in order to store an
* argument buffer. These live long enough that we need to mark
* them using the callsite. This is set to -1 if there aren't any. */
MVMint32 first_args_temporary;
/* Ops we execute to evaluate the dispatch program. */
MVMDispProgramOp *ops;
MVMuint32 num_ops; // TODO probably don't need since end is result op
};
/* Various kinds of constant we use during a dispatch program, to let us keep
* the ops part more compact. */
union MVMDispProgramConstant {
MVMCallsite *cs;
MVMint64 i64;
MVMnum64 n64;
};
/* Opcodes we may execute in a dispatch program. */
typedef enum {
/* Guard that the type of an incoming argument is as expected. */
MVMDispOpcodeGuardArgType,
/* Guard that the type of an incoming argument is as expected and also
* we have a concrete object. */
MVMDispOpcodeGuardArgTypeConc,
/* Guard that the type of an incoming argument is as expected and also
* we have a type object. */
MVMDispOpcodeGuardArgTypeTypeObject,
/* Guard that the incoming argument is concrete. */
MVMDispOpcodeGuardArgConc,
/* Guard that the incoming argument is a type object. */
MVMDispOpcodeGuardArgTypeObject,
/* Guard that the type of an incoming argument is an expected object
* literal. */
MVMDispOpcodeGuardArgLiteralObj,
/* Guard that the type of an incoming argument is an expected string
* literal. */
MVMDispOpcodeGuardArgLiteralStr,
/* Guard that the type of an incoming argument is an expected int
* literal. */
MVMDispOpcodeGuardArgLiteralInt,
/* Guard that the type of an incoming argument is an expected num
* literal. */
MVMDispOpcodeGuardArgLiteralNum,
/* Guard that the type of an incoming argument is not an unexpected
* literal. */
MVMDispOpcodeGuardArgNotLiteralObj,
/* Load a capture value into a temporary. */
MVMDispOpcodeLoadCaptureValue,
/* Load a constant object or string into a temporary. */
MVMDispOpcodeLoadConstantObjOrStr,
/* Load a constant int into a temporary. */
MVMDispOpcodeLoadConstantInt,
/* Load a constant num into a temporary. */
MVMDispOpcodeLoadConstantNum,
/* Set an object result outcome from a temporary. */
MVMDispOpcodeResultValueObj,
/* Set a string result outcome from a temporary. */
MVMDispOpcodeResultValueStr,
/* Set an inte result outcome from a temporary. */
MVMDispOpcodeResultValueInt,
/* Set a num result outcome from a temporary. */
MVMDispOpcodeResultValueNum,
/* Set the args buffer to use in the call to be the tail of that of
* the initial capture. (Actually it's really the map that we take
* the tail of.) The argument is how many of the args to skip from
* the start. This is used when the dispatch should use everything
* except the first args. */
MVMDispOpcodeUseArgsTail,
/* When we have to really produce a new set of arguments, we use this
* op. Before it, we use load instructions to build the start of the
* argument list. We then pass in a number of arguments to copy from
* the incoming argument list, which may be 0. The arguments are
* sourced by looking at the trailing N arguments of the initial
* capture. They are copied into the last N temporaries. The args
* are set to come from the argument temporaries, using the identity
* map. */
MVMDispOpcodeCopyArgsTail,
/* Set a bytecode object result, specifying invokee and callsite.
* The args should already have been set up. */
MVMDispOpcodeResultBytecode,
/* Set a C function object result, specifying invokee and callsite.
* The args should already have been set up. */
MVMDispOpcodeResultCFunction
} MVMDispProgramOpcode;
/* An operation, with its operands, in a dispatch program. */
struct MVMDispProgramOp {
/* The opcode. */
MVMDispProgramOpcode code;
/* Operands. */
union {
struct {
/* The argument index in the incoming capture. */
MVMuint16 arg_idx;
/* The thing to check it against (looked up in one of the constant
* tables). */
MVMuint32 checkee;
} arg_guard;
struct {
/* The temporary to load into. */
MVMuint32 temp;
/* The index to load from. The thing we're indexing is part of
* the instruction. */
MVMuint32 idx;
} load;
struct {
/* The temporary holding the result. */
MVMuint32 temp;
} res_value;
struct {
/* The temporary holding the thing to invoke. */
MVMuint32 temp_invokee;
/* The callsite index. */
MVMuint32 callsite_idx;
} res_code;
};
};
/* Functions called during the recording. */
void MVM_disp_program_run_dispatch(MVMThreadContext *tc, MVMDispDefinition *disp, MVMObject *capture);
MVMObject * MVM_disp_program_record_track_arg(MVMThreadContext *tc, MVMObject *capture,
MVMuint32 index);
void MVM_disp_program_record_guard_type(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_concreteness(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_literal(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_not_literal_obj(MVMThreadContext *tc,
MVMObject *tracked, MVMObject *object);
MVMObject * MVM_disp_program_record_capture_drop_arg(MVMThreadContext *tc, MVMObject *capture,
MVMuint32 index);
MVMObject * MVM_disp_program_record_capture_insert_constant_arg(MVMThreadContext *tc,
MVMObject *capture, MVMuint32 index, MVMCallsiteFlags kind, MVMRegister value);
MVMObject * MVM_disp_program_record_capture_insert_arg(MVMThreadContext *tc,
MVMObject *capture, MVMuint32 index, MVMObject *tracked);
void MVM_disp_program_record_delegate(MVMThreadContext *tc, MVMString *dispatcher_id,
MVMObject *capture);
void MVM_disp_program_record_result_constant(MVMThreadContext *tc, MVMObject *result);
void MVM_disp_program_record_result_tracked_value(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_code_constant(MVMThreadContext *tc, MVMCode *result, MVMObject *capture);
void MVM_disp_program_record_c_code_constant(MVMThreadContext *tc, MVMCFunction *result,
MVMObject *capture);
MVMuint32 MVM_disp_program_record_end(MVMThreadContext *tc, MVMCallStackDispatchRecord* record,
MVMuint32 *thunked);