-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
generic-sharing
182 lines (142 loc) · 6.86 KB
/
generic-sharing
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
-*- outline -*-
Generic code sharing
====================
* Porting
** Generic class init trampoline
The generic class init trampoline is very similar to the class init
trampoline, with the exception that the VTable of the class to be
inited is passed in a register, MONO_ARCH_VTABLE_REG. That can be the
same as the IMT register.
The call to the generic class init trampoline is never patched because
the VTable is not constant - it depends on the type arguments of the
class/method doing the call to the trampoline. For that reason, the
trampoline needs a fast path for the case that the class is already
inited. See tramp-x86.c for how to portably figure out the number of
the bit in the bit-field that needs to be checked.
** RGCTX register
Generic shared code needs access to type information. This
information is contained in a RGCTX for non-generic methods and in an
MRGCTX for generic methods. It is passed in one of several ways,
depending on the type of the called method:
1. Non-generic non-static methods of reference types have access to
the RGCTX via the "this" argument (this->vtable->rgctx).
2. Non-generic static methods of reference types and non-generic
methods of value types need to be passed a pointer to the
caller's class's VTable in the MONO_ARCH_RGCTX_REG register.
3. Generic methods need to be passed a pointer to the MRGCTX in the
MONO_ARCH_RGCTX_REG register.
The MONO_ARCH_RGCTX_REG must not be clobbered by trampolines.
MONO_ARCH_RGCTX_REG can be the same as the IMT register for now, but
this might change in the future when we implement virtual generic
method calls (more) efficiently.
** Dealing with types
Types passed to arch functions might be type parameters
(MONO_TYPE_(M)VAR) if the MonoGenericSharingContext* argument is
non-NULL. For example, an argument or return type in a method passed
to mono_arch_find_this_argument() could be a MONO_TYPE_VAR. To guard
for that case use mini_get_basic_type_from_generic() on the type. See
get_call_info() in mini-x86.c, for example.
** (M)RGCTX lazy fetch trampoline
The purpose of the lazy fetch trampoline is to fetch a slot from an
(M)RGCTX which might not be inited, yet. In the latter case, it needs
to go make a transition to unmanaged code to fill the slot. This is
the layout of a RGCTX:
+---------------------------------+
| next | slot 0 | slot 1 | slot 2 |
+--|------------------------------+
|
+-----+
| +---------------------------------
+->| next | slot 3 | slot 4 | slot 5 ....
+--|------------------------------
|
+-----+
| +------------------------------------
+->| next | slot 10 | slot 11 | slot 12 ....
+--|---------------------------------
.
.
.
For fetching a slot from a RGCTX the trampoline is passed a pointer to
the VTable. From there it has to fetch the pointer to the RGCTX,
which might be null. Then it has to traverse the correct number of
"next" links, each of which might be NULL. Arriving at the right
array it needs to fetch the slot, which might also be NULL. If any of
the NULL cases, the trampoline must transition to unmanaged code to
potentially setup the RGCTX and fill the slot. Here is pseudo-code
for fetching slot 11:
; vtable ptr in r1
; fetch RGCTX array 0
r2 = *(r1 + offsetof(MonoVTable, runtime_generic_context))
if r2 == NULL goto unmanaged
; fetch RGCTX array 1
r2 = *r2
if r2 == NULL goto unmanaged
; fetch RGCTX array 2
r2 = *r2
if r2 == NULL goto unmanaged
; fetch slot 11
r2 = *(r2 + 2 * sizeof (gpointer))
if r2 == NULL goto unmanaged
return r2
unmanaged:
jump unmanaged_fetch_code
The number of slots in the arrays must be obtained from the function
mono_class_rgctx_get_array_size().
The MRGCTX case is different in two aspects. First, the trampoline is
not passed a pointer to a VTable, but a pointer directly to the
MRGCTX, which is guaranteed not to be NULL (any of the next pointers
and any of the slots can be NULL, though). Second, the layout of the
first array is slightly different, in that the first two slots are
occupied by a pointers to the class's VTable and to the method's
method_inst. The next pointer is in the third slot and the first
actual slot, "slot 0", in the fourth:
+--------------------------------------------------------+
| vtable | method_inst | next | slot 0 | slot 1 | slot 2 |
+-------------------------|------------------------------+
.
.
All other arrays have the same layout as the RGCTX ones, except
possibly for their length.
The function to create the trampoline,
mono_arch_create_rgctx_lazy_fetch_trampoline(), gets passed an encoded
slot number. Use the macro MONO_RGCTX_SLOT_IS_MRGCTX to query whether
a trampoline for an MRGCTX is needed, as opposed to one for a RGCTX.
Use MONO_RGCTX_SLOT_INDEX to get the index of the slot (like 2 for
"slot 2" as above).
* Getting generics information about a stack frame
If a method is compiled with generic sharing, its MonoJitInfo has
has_generic_jit_info set. In that case, the MonoJitInfo is followed
(after the MonoJitExceptionInfo array) by a MonoGenericJitInfo.
The MonoGenericJitInfo contains information about the location of the
this/vtable/MRGCTX variable, if the has_this flag is set. If that is
the case, there are two possibilities:
1. this_in_reg is set. this_reg is the number of the register where
the variable is stored.
2. this_in_reg is not set. The variable is stored at offset
this_offset from the address in the register with number
this_reg.
The variable can either point to the "this" object, to a vtable or to
an MRGCTX:
1. If the method is a non-generic non-static method of a reference
type, the variable points to the "this" object.
2. If the method is a non-generic static method or a non-generic
method of a value type, the variable points to the vtable of the
class.
3. If the method is a generic method, the variable points to the
MRGCTX of the method.
* Layout of the MRGCTX
The MRGCTX is a structure that starts with
MonoMethodRuntimeGenericContext, which contains a pointer to the
vtable of the class and a pointer to the MonoGenericInst with the type
arguments for the method.
* Blog posts about generic code sharing
http://schani.wordpress.com/2007/09/22/generics-sharing-in-mono/
http://schani.wordpress.com/2007/10/12/the-trouble-with-shared-generics/
http://schani.wordpress.com/2007/10/15/a-quick-generics-sharing-update/
http://schani.wordpress.com/2008/01/29/other-types/
http://schani.wordpress.com/2008/02/25/generic-types-are-lazy/
http://schani.wordpress.com/2008/03/10/sharing-static-methods/
http://schani.wordpress.com/2008/04/22/sharing-everything-and-saving-memory/
http://schani.wordpress.com/2008/06/02/sharing-generic-methods/
http://schani.wordpress.com/2008/06/27/another-generic-sharing-update/