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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
|
#ifndef __MONO_MINI_INTERPRETER_INTERNALS_H__
#define __MONO_MINI_INTERPRETER_INTERNALS_H__
#include <setjmp.h>
#include <glib.h>
#include <mono/metadata/loader.h>
#include <mono/metadata/object.h>
#include <mono/metadata/domain-internals.h>
#include <mono/metadata/class-internals.h>
#include <mono/metadata/debug-internals.h>
#include "interp.h"
#define MINT_TYPE_I1 0
#define MINT_TYPE_U1 1
#define MINT_TYPE_I2 2
#define MINT_TYPE_U2 3
#define MINT_TYPE_I4 4
#define MINT_TYPE_I8 5
#define MINT_TYPE_R4 6
#define MINT_TYPE_R8 7
#define MINT_TYPE_O 8
#define MINT_TYPE_VT 9
#define INLINED_METHOD_FLAG 0xffff
#define TRACING_FLAG 0x1
#define PROFILING_FLAG 0x2
#define MINT_VT_ALIGNMENT 8
#define INTERP_STACK_SIZE (1024*1024)
enum {
VAL_I32 = 0,
VAL_DOUBLE = 1,
VAL_I64 = 2,
VAL_VALUET = 3,
VAL_POINTER = 4,
VAL_NATI = 0 + VAL_POINTER,
VAL_MP = 1 + VAL_POINTER,
VAL_TP = 2 + VAL_POINTER,
VAL_OBJ = 3 + VAL_POINTER
};
#if SIZEOF_VOID_P == 4
typedef guint32 mono_u;
typedef gint32 mono_i;
#define MINT_TYPE_I MINT_TYPE_I4
#elif SIZEOF_VOID_P == 8
typedef guint64 mono_u;
typedef gint64 mono_i;
#define MINT_TYPE_I MINT_TYPE_I8
#endif
/*
* GC SAFETY:
*
* The interpreter executes in gc unsafe (non-preempt) mode. On wasm, the C stack is
* scannable but the wasm stack is not, so to make the code GC safe, the following rules
* should be followed:
* - every objref handled by the code needs to either be stored volatile or stored
* into a volatile; volatile stores are stack packable, volatile values are not.
* Use either OBJREF or stackval->data.o.
* This will ensure the objects are pinned. A volatile local
* is on the stack and not in registers. Volatile stores ditto.
* - minimize the number of MonoObject* locals/arguments (or make them volatile).
*
* Volatile on a type/local forces all reads and writes to go to memory/stack,
* and each such local to have a unique address.
*
* Volatile absence on a type/local allows multiple locals to share storage,
* if their lifetimes do not overlap. This is called "stack packing".
*
* Volatile absence on a type/local allows the variable to live in
* both stack and register, for fast reads and "write through".
*/
#ifdef TARGET_WASM
#define WASM_VOLATILE volatile
static inline MonoObject * WASM_VOLATILE *
mono_interp_objref (MonoObject **o)
{
return o;
}
#define OBJREF(x) (*mono_interp_objref (&x))
#else
#define WASM_VOLATILE /* nothing */
#define OBJREF(x) x
#endif
/*
* Value types are represented on the eval stack as pointers to the
* actual storage. A value type cannot be larger than 16 MB.
*/
typedef struct {
union {
gint32 i;
gint64 l;
struct {
gint32 lo;
gint32 hi;
} pair;
float f_r4;
double f;
MonoObject * WASM_VOLATILE o;
/* native size integer and pointer types */
gpointer p;
mono_u nati;
gpointer vt;
} data;
} stackval;
typedef struct InterpFrame InterpFrame;
typedef void (*MonoFuncV) (void);
typedef void (*MonoPIFunc) (void *callme, void *margs);
typedef enum {
IMETHOD_CODE_INTERP,
IMETHOD_CODE_COMPILED,
IMETHOD_CODE_UNKNOWN
} InterpMethodCodeType;
/*
* Structure representing a method transformed for the interpreter
* This is domain specific
*/
typedef struct InterpMethod InterpMethod;
struct InterpMethod {
/* NOTE: These first two elements (method and
next_jit_code_hash) must be in the same order and at the
same offset as in MonoJitInfo, because of the jit_code_hash
internal hash table in MonoDomain. */
MonoMethod *method;
InterpMethod *next_jit_code_hash;
// Sort pointers ahead of integers to minimize padding for alignment.
unsigned short *code;
MonoPIFunc func;
MonoExceptionClause *clauses; // num_clauses
void **data_items;
guint32 *local_offsets;
guint32 *exvar_offsets;
gpointer jit_call_info;
gpointer jit_entry;
gpointer llvmonly_unbox_entry;
MonoType *rtype;
MonoType **param_types;
MonoJitInfo *jinfo;
MonoDomain *domain;
guint32 locals_size;
guint32 total_locals_size;
guint32 stack_size;
guint32 vt_stack_size;
guint32 alloca_size;
int num_clauses; // clauses
int transformed; // boolean
unsigned int param_count;
unsigned int hasthis; // boolean
MonoProfilerCallInstrumentationFlags prof_flags;
InterpMethodCodeType code_type;
#ifdef ENABLE_EXPERIMENT_TIERED
MiniTieredCounter tiered_counter;
#endif
unsigned int init_locals : 1;
unsigned int vararg : 1;
unsigned int needs_thread_attach : 1;
};
/* Used for localloc memory allocation */
typedef struct _FrameDataFragment FrameDataFragment;
struct _FrameDataFragment {
guint8 *pos, *end;
struct _FrameDataFragment *next;
#if SIZEOF_VOID_P == 4
/* Align data field to MINT_VT_ALIGNMENT */
gint32 pad;
#endif
double data [MONO_ZERO_LEN_ARRAY];
};
typedef struct {
InterpFrame *frame;
/*
* frag and pos hold the current allocation position when the stored frame
* starts allocating memory. This is used for restoring the localloc stack
* when frame returns.
*/
FrameDataFragment *frag;
guint8 *pos;
} FrameDataInfo;
typedef struct {
FrameDataFragment *first, *current;
FrameDataInfo *infos;
int infos_len, infos_capacity;
/* For GC sync */
int inited;
} FrameDataAllocator;
/* Arguments that are passed when invoking only a finally/filter clause from the frame */
typedef struct FrameClauseArgs FrameClauseArgs;
/* State of the interpreter main loop */
typedef struct {
stackval *sp;
unsigned char *vt_sp;
const unsigned short *ip;
GSList *finally_ips;
} InterpState;
struct InterpFrame {
InterpFrame *parent; /* parent */
InterpMethod *imethod; /* parent */
stackval *stack_args; /* parent */
stackval *retval; /* parent */
stackval *stack;
InterpFrame *next_free;
/* State saved before calls */
/* This is valid if state.ip != NULL */
InterpState state;
};
#define frame_locals(frame) ((guchar*)(frame)->stack)
typedef struct {
/* Lets interpreter know it has to resume execution after EH */
gboolean has_resume_state;
/* Frame to resume execution at */
InterpFrame *handler_frame;
/* IP to resume execution at */
const guint16 *handler_ip;
/* Clause that we are resuming to */
MonoJitExceptionInfo *handler_ei;
/* Exception that is being thrown. Set with rest of resume state */
MonoGCHandle exc_gchandle;
/* This is a contiguous space allocated for interp execution stack */
guchar *stack_start;
/*
* This stack pointer is the highest stack memory that can be used by the current frame. This does not
* change throughout the execution of a frame and it is essentially the upper limit of the execution
* stack pointer. It is needed when re-entering interp, to know from which address we can start using
* stack, and also needed for the GC to be able to scan the stack.
*/
guchar *stack_pointer;
/* Used for allocation of localloc regions */
FrameDataAllocator data_stack;
} ThreadContext;
typedef struct {
gint64 transform_time;
gint64 methods_transformed;
gint64 cprop_time;
gint64 super_instructions_time;
gint32 stloc_nps;
gint32 movlocs;
gint32 copy_propagations;
gint32 constant_folds;
gint32 ldlocas_removed;
gint32 killed_instructions;
gint32 emitted_instructions;
gint32 super_instructions;
gint32 added_pop_count;
gint32 inlined_methods;
gint32 inline_failures;
} MonoInterpStats;
extern MonoInterpStats mono_interp_stats;
extern int mono_interp_traceopt;
extern int mono_interp_opt;
extern GSList *mono_interp_jit_classes;
void
mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, MonoError *error);
void
mono_interp_transform_init (void);
InterpMethod *
mono_interp_get_imethod (MonoDomain *domain, MonoMethod *method, MonoError *error);
void
mono_interp_print_code (InterpMethod *imethod);
gboolean
mono_interp_jit_call_supported (MonoMethod *method, MonoMethodSignature *sig);
static inline int
mint_type(MonoType *type_)
{
MonoType *type = mini_native_type_replace_type (type_);
if (type->byref)
return MINT_TYPE_I;
enum_type:
switch (type->type) {
case MONO_TYPE_I1:
return MINT_TYPE_I1;
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
return MINT_TYPE_U1;
case MONO_TYPE_I2:
return MINT_TYPE_I2;
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
return MINT_TYPE_U2;
case MONO_TYPE_I4:
case MONO_TYPE_U4:
return MINT_TYPE_I4;
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_PTR:
return MINT_TYPE_I;
case MONO_TYPE_R4:
return MINT_TYPE_R4;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
return MINT_TYPE_I8;
case MONO_TYPE_R8:
return MINT_TYPE_R8;
case MONO_TYPE_STRING:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_CLASS:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
return MINT_TYPE_O;
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (type->data.klass)) {
type = mono_class_enum_basetype_internal (type->data.klass);
goto enum_type;
} else
return MINT_TYPE_VT;
case MONO_TYPE_TYPEDBYREF:
return MINT_TYPE_VT;
case MONO_TYPE_GENERICINST:
type = m_class_get_byval_arg (type->data.generic_class->container_class);
goto enum_type;
default:
g_warning ("got type 0x%02x", type->type);
g_assert_not_reached ();
}
return -1;
}
#endif /* __MONO_MINI_INTERPRETER_INTERNALS_H__ */
|