You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/design/coreclr/botr/shared-generics.md
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,31 +12,31 @@ Shared generics is a runtime+JIT feature aimed at reducing the amount of code th
12
12
Consider the following C# code sample:
13
13
14
14
```c#
15
-
stringFunc<T>()
15
+
stringMethod<T>()
16
16
{
17
17
returntypeof(List<T>).ToString();
18
18
}
19
19
```
20
20
21
-
Without shared generics, the code for instantiations like `Func<object>` or `Func<string>` would look identical except for one single instruction: the one that loads the correct TypeHandle of type `List<T>`:
21
+
Without shared generics, the code for instantiations like `Method<object>` or `Method<string>` would look identical except for one single instruction: the one that loads the correct TypeHandle of type `List<T>`:
22
22
```asm
23
23
mov rcx, type handle of List<string> or List<object>
24
24
call ToString()
25
25
ret
26
26
```
27
27
28
-
With shared generics, the canonical code will not have any hard-coded versions of the type handle of List<T>, but instead looks up the exact type handle either through a call to a runtime helper API, or by loading it up from the *generic dictionary* of the instantiation of Func<T> that is executing. The code would look more like the following:
28
+
With shared generics, the canonical code will not have any hard-coded versions of the type handle of List<T>, but instead looks up the exact type handle either through a call to a runtime helper API, or by loading it up from the *generic dictionary* of the instantiation of Method<T> that is executing. The code would look more like the following:
29
29
```asm
30
-
mov rcx, generic context // MethodDesc of Func<string> or Func<object>
30
+
mov rcx, generic context // MethodDesc of Method<string> or Method<object>
31
31
mov rcx, [rcx + offset of InstantiatedMethodDesc::m_pPerInstInfo] // This is the generic dictionary
32
32
mov rcx, [rcx + dictionary slot containing type handle of List<T>]
33
33
call ToString()
34
34
ret
35
35
```
36
36
37
-
The generic context in this example is the InstantiatedMethodDesc of `Func<object>` or `Func<string>`. The generic dictionary is a data structure used by shared generic code to fetch instantiation-specific information. It is basically an array where the entries are instantiation-specific type handles, method handles, field handles, method entry points, etc... The "PerInstInfo" fields on MethodTable and InstantiatedMethodDesc structures point at the generic dictionary structure for a generic type and method respectively.
37
+
The generic context in this example is the InstantiatedMethodDesc of `Method<object>` or `Method<string>`. The generic dictionary is a data structure used by shared generic code to fetch instantiation-specific information. It is basically an array where the entries are instantiation-specific type handles, method handles, field handles, method entry points, etc... The "PerInstInfo" fields on MethodTable and InstantiatedMethodDesc structures point at the generic dictionary structure for a generic type and method respectively.
38
38
39
-
In this example, the generic dictionary for Func<object> will contain a slot with the type handle for type List<object>, and the generic dictionary for Func<string> will contain a slot with the type handle for type List<string>.
39
+
In this example, the generic dictionary for Method<object> will contain a slot with the type handle for type List<object>, and the generic dictionary for Method<string> will contain a slot with the type handle for type List<string>.
40
40
41
41
This feature is currently only supported for instantiations over reference types because they all have the same size/properties/layout/etc... For instantiations over primitive types or value types, the runtime will generate separate code bodies for each instantiation.
42
42
@@ -93,9 +93,9 @@ As described earlier, a generic dictionary is an array of multiple slots contain
93
93
94
94
The first N slots in an instantiation of N arguments are always going to be the type handles of the instantiation type arguments (this is kind of an optimization as well). The slots that follow contain instantiation-based information.
95
95
96
-
For instance, here is an example of the contents of the generic dictionary for our `Func<string>` example:
96
+
For instance, here is an example of the contents of the generic dictionary for our `Method<string>` example:
97
97
98
-
|`Func<string>'s dicionary`|
98
+
|`Method<string>'s dicionary`|
99
99
|--------------------------|
100
100
| slot[0]: TypeHandle(`string`) |
101
101
| slot[1]: Total dictionary size |
@@ -116,7 +116,7 @@ When generating shared generic code, the JIT knows which slots to use for the va
116
116
### Dictionary Layouts
117
117
118
118
The `DictionaryLayout` structure is what tells the JIT which slot to use when performing a dictionary lookup. This `DictionaryLayout` structure has a couple of important properties:
119
-
- It is shared across all compatible instantiations of a certain type of method. In other words, a dictionary layout is associated with the canonical instantiation of a type or a method. For instance, in our example above, `Func<object>` and `Func<string>` are compatible instantiations, each with their own **separate dictionaries**, however they all share the **same dictionary layout**, which is associated with the canonical instantiation `Func<__Canon>`.
119
+
- It is shared across all compatible instantiations of a certain type of method. In other words, a dictionary layout is associated with the canonical instantiation of a type or a method. For instance, in our example above, `Method<object>` and `Method<string>` are compatible instantiations, each with their own **separate dictionaries**, however they all share the **same dictionary layout**, which is associated with the canonical instantiation `Method<__Canon>`.
120
120
- The dictionaries of generic types or methods have the same number of slots as their dictionary layouts. Note: historically before the introduction of the dynamic dictionary expansion feature, the generic dictionaries could be smaller than their layouts, meaning that for certain lookups, we had to use invoke some runtime helper APIs (slow path).
121
121
122
122
When a generic type or method is first created, its dictionary layout contains 'unassigned' slots. Assignments happen as part of code generation, whenever the JIT needs to emit a dictionary lookup sequence. This assignment happens during the calls to the `DictionaryLayout::FindToken(...)` APIs. Once a slot has been assigned, it becomes associated with a certain signature, which describes the kind of value that will go in every instantiated dictionary at that slot index.
@@ -160,7 +160,7 @@ The feature is simple in concept: change dictionary layouts from a linked list o
160
160
- For types, the generic dictionary is part of the `MethodTable` structure, which can't be reallocated (already in use by managed code)
161
161
- For methods, the generic dictionary is not part of the `MethodDesc` structure, but can still be in use by some generic code.
162
162
- We can't have multiple MethodTables or MethodDescs for the same type or method anyways, so reallocations are not an option.
163
-
- We can't just resize the generic dictionary for a single instantiation. For instance, in our example above, let's say we wanted to expand the dictionary for `Func<string>`. The resizing of the layout would have an impact on the shared canonical code that the JIT generates for `Func<__Canon>`. If we only resized the dictionary of `Func<string>`, the shared generic code would work for that instantiation only, but when we attempt to use it with another instantiation like `Func<object>`, the jitted instructions would no longer match the size of the dictionary structure, and would cause access violations.
163
+
- We can't just resize the generic dictionary for a single instantiation. For instance, in our example above, let's say we wanted to expand the dictionary for `Method<string>`. The resizing of the layout would have an impact on the shared canonical code that the JIT generates for `Method<__Canon>`. If we only resized the dictionary of `Method<string>`, the shared generic code would work for that instantiation only, but when we attempt to use it with another instantiation like `Method<object>`, the jitted instructions would no longer match the size of the dictionary structure, and would cause access violations.
164
164
- The runtime is multi-threaded, which adds to the complexity.
165
165
166
166
The current implementation expands the dictionary layout and the actual dictionaries separately to keep things simple:
0 commit comments