Skip to content

Commit d4f81be

Browse files
committed
Merge branch 'main' into empty-struct-passing
2 parents 3389bcf + d901213 commit d4f81be

File tree

730 files changed

+32907
-17701
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

730 files changed

+32907
-17701
lines changed

.config/dotnet-tools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
]
1616
},
1717
"microsoft.dotnet.xharness.cli": {
18-
"version": "9.0.0-prerelease.24270.4",
18+
"version": "9.0.0-prerelease.24311.2",
1919
"commands": [
2020
"xharness"
2121
]

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ csharp_new_line_between_query_expression_clauses = true
3131
csharp_indent_block_contents = true
3232
csharp_indent_braces = false
3333
csharp_indent_case_contents = true
34-
csharp_indent_case_contents_when_block = true
34+
csharp_indent_case_contents_when_block = false
3535
csharp_indent_switch_labels = true
3636
csharp_indent_labels = one_less_than_current
3737

.github/workflows/bump-chrome-version.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,6 @@ jobs:
7070
owner: context.repo.owner,
7171
repo: context.repo.repo,
7272
pull_number: pullRequest.number,
73-
reviewers: ["lewing", "pavelsavara", "maraf", "ilonatommy", "radical"]
73+
reviewers: ["lewing", "pavelsavara", "maraf", "ilonatommy", "akoeplinger"]
7474
});
7575
return pullRequest.number;

.vsconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
4444
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
4545
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
46-
"Microsoft.VisualStudio.Component.Windows10SDK.20348",
46+
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
4747
"Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
4848
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
4949
"Microsoft.VisualStudio.Workload.CoreEditor",

docs/design/coreclr/botr/type-system.md

Lines changed: 138 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,143 @@ This is enforced via an extensive and complicated set of enforcements within the
193193
- **ISSUE:** The signature walks performed are done with the normal signature walking code. This code is designed to load types as it walks the signature, but in this case the type load functionality is used with the assumption that no type load will actually be triggered.
194194
- **ISSUE:** Stackwalker requirements require support from not just the type system, but also the assembly loader. The Loader has had a number of issues meeting the needs of the type system here.
195195

196-
Type System and NGEN
197-
--------------------
198-
199-
The type system data structures are a core part of what is saved into NGEN images. Unfortunately, these data structures logically have pointers within them that point to other NGEN images. In order to handle this situation, the type system data structures implement a concept known as restoration.
200-
201-
In restoration, when a type system data structure is first needed, the data structure is fixed up with correct pointers. This is tied into the type loading levels described in the [Type Loader](type-loader.md) Book of the Runtime chapter.
202-
203-
There also exists the concept of pre-restored data structures. This means that the data structure is sufficiently correct at NGEN image load time (after intra-module pointer fixups and eager load type fixups), that the data structure may be used as is. This optimization requires that the NGEN image be "hard bound" to its dependent assemblies. See NGEN documentation for further details.
204-
205-
Type System and Domain Neutral Loading
206-
--------------------------------------
207-
208-
The type system is a core part of the implementation of domain neutral loading. This is exposed to customers through the LoaderOptimization options available at AppDomain creation. Mscorlib is always loaded as domain neutral. The core requirement of this feature is that the type system data structures must not require pointers to domain specific state. Primarily this manifests itself in requirements around static fields and class constructors. In particular, whether or not a class constructor has been run is not a part of the core MethodTable data structure for this reason, and there is a mechanism for storing static data attached to the DomainFile data structure instead of the MethodTable data structure.
196+
## Static variables
197+
198+
Static variables in CoreCLR are handled by a combination of getting the "static base", and then adjusting it by an offset to get a pointer to the actual value.
199+
We define the statics base as either non-gc or gc for each field.
200+
Currently non-gc statics are any statics which are represented by primitive types (byte, sbyte, char, int, uint, long, ulong, float, double, pointers of various forms), and enums.
201+
GC statics are any statics which are represented by classes or by non-primitive valuetypes.
202+
For valuetype statics which are GC statics, the static variable is actually a pointer to a boxed instance of the valuetype.
203+
204+
### Per type static variable information
205+
As of .NET 9, the static variable bases are now all associated with their particular type.
206+
As you can see from this diagram, the data for statics can be acquired by starting at a `MethodTable` and then getting either the `DynamicStaticsInfo` to get a statics pointer, or by getting a `ThreadStaticsInfo` to get a TLSIndex, which then can be used with the thread static variable system to get the actual thread static base.
207+
208+
```mermaid
209+
classDiagram
210+
MethodTable : MethodTableAuxiliaryData* m_pAuxData
211+
MethodTable --> MethodTableAuxiliaryData
212+
MethodTableAuxiliaryData --> DynamicStaticsInfo : If has static variables
213+
MethodTableAuxiliaryData --> GenericStaticsInfo : If is generic and has static variables
214+
MethodTableAuxiliaryData --> ThreadStaticsInfo : If has thread local static variables
215+
216+
DynamicStaticsInfo : StaticsPointer m_pGCStatics
217+
DynamicStaticsInfo : StaticsPointer m_pNonGCStatics
218+
219+
GenericStaticsInfo : FieldDesc* m_pFieldDescs
220+
221+
ThreadStaticsInfo : TLSIndex NonGCTlsIndex
222+
ThreadStaticsInfo : TLSIndex GCTlsIndex
223+
```
224+
225+
```mermaid
226+
classDiagram
227+
228+
note for StaticsPointer "StaticsPointer is a pointer sized integer"
229+
StaticsPointer : void* PointerToStaticBase
230+
StaticsPointer : bool HasClassConstructorBeenRun
231+
232+
note for TLSIndex "TLSIndex is a 32bit integer"
233+
TLSIndex : TLSIndexType indexType
234+
TLSIndex : 24bit int indexOffset
235+
```
236+
237+
In the above diagram, you can see that we have separate fields for non-gc and gc statics, as well as thread and normal statics.
238+
For normal statics, we use a single pointer sized field, which also happens to encode whether or not the class constructor has been run.
239+
This is done to allow lock free atomic access to both get the static field address as well as determine if the class constructor needs to be triggered.
240+
For TLS statics, handling of detecting whether or not the class constructor has been run is a more complex process described as part of the thread statics infrastructure.
241+
The `DynamicStaticsInfo` and `ThreadStaticsInfo` structures are accessed without any locks, so it is important to ensure that access to fields on these structures can be done with a single memory access, to avoid memory order tearing issues.
242+
243+
Also, notably, for generic types, each field has a `FieldDesc` which is allocated per type instance, and is not shared by multiple canonical instances.
244+
245+
#### Lifetime management for collectible statics
246+
247+
Finally we have a concept of collectible assemblies in the CoreCLR runtime, so we need to handle lifetime management for static variables.
248+
The approach chosen was to build a special GC handle type which will allow the runtime to have a pointer in the runtime data structures to the interior of a managed object on the GC heap.
249+
250+
The requirement of behavior here is that a static variable cannot keep its own collectible assembly alive, and so collectible statics have the peculiar property that they can exist and be finalized before the collectible assembly is finally collected.
251+
If there is some resurrection scenario, this can lead to very surprising behavior.
252+
253+
### Thread Statics
254+
255+
Thread statics are static variables which have a lifetime which is defined to be the shorter of the lifetime of the type containing the static, and the lifetime of the thread on which the static variable is accessed.
256+
They are created by having a static variable on a type which is attributed with `[System.Runtime.CompilerServices.ThreadStaticAttribute]`.
257+
The general scheme of how this works is to assign an "index" to the type which is the same on all threads, and then on each thread hold a data structure which is efficiently accessed by means of this index.
258+
However, we have a few peculiarities in our approach.
259+
260+
1. We segregate collectible and non-collectible thread statics (`TLSIndexType::NonCollectible` and `TLSIndexType::Collectible`)
261+
2. We provide an ability to share a non-gc thread static between native CoreCLR code and managed code (Subset of `TLSIndexType::DirectOnThreadLocalData`)
262+
3. We provide an extremely efficient means to access a small number of non-gc thread statics. (The rest of the usage of `TLSIndexType::DirectOnThreadLocalData`)
263+
264+
#### Per-Thread Statics Data structures
265+
```mermaid
266+
classDiagram
267+
268+
note for ThreadLocalInfo "There is 1 of these per thread, and it is managed by the C++ compiler/OS using standard mechanisms.
269+
It can be found as the t_ThreadStatics variable in a C++ compiler, and is also pointed at by the native Thread class."
270+
ThreadLocalInfo : int cNonCollectibleTlsData
271+
ThreadLocalInfo : void** pNonCollectibleTlsArrayData
272+
ThreadLocalInfo : int cCollectibleTlsData
273+
ThreadLocalInfo : void** pCollectibleTlsArrayData
274+
ThreadLocalInfo : InFlightTLSData *pInFightData
275+
ThreadLocalInfo : Thread* pThread
276+
ThreadLocalInfo : Special Thread Statics Shared Between Native and Managed code
277+
ThreadLocalInfo : byte[N] ExtendedDirectThreadLocalTLSData
278+
279+
InFlightTLSData : InFlightTLSData* pNext
280+
InFlightTLSData : TLSIndex tlsIndex
281+
InFlightTLSData : OBJECTHANDLE hTLSData
282+
283+
ThreadLocalInfo --> InFlightTLSData : For TLS statics which have their memory allocated, but have not been accessed since the class finished running its class constructor
284+
InFlightTLSData --> InFlightTLSData : linked list
285+
```
286+
287+
#### Access patterns for getting the thread statics address
288+
289+
This is the pattern that the JIT will use to access a thread static which is not `DirectOnThreadLocalData`.
290+
291+
0. Get the TLS index somehow
292+
1. Get TLS pointer to OS managed TLS block for the current thread ie. `pThreadLocalData = &t_ThreadStatics`
293+
2. Read 1 integer value `pThreadLocalData->cCollectibleTlsData OR pThreadLocalData->cNonCollectibleTlsData`
294+
3. Compare cTlsData against the index we're looking up `if (cTlsData < index.GetIndexOffset())`
295+
4. If the index is not within range, jump to step 11.
296+
5. Read 1 pointer value from TLS block `pThreadLocalData->pCollectibleTlsArrayData` OR `pThreadLocalData->pNonCollectibleTlsArrayData`
297+
6. Read 1 pointer from within the TLS Array. `pTLSBaseAddress = *(intptr_t*)(((uint8_t*)pTlsArrayData) + index.GetIndexOffset()`
298+
7. If pointer is NULL jump to step 11 `if pTLSBaseAddress == NULL`
299+
8. If TLS index not a Collectible index, return pTLSBaseAddress
300+
9. if `ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress)` is NULL, jump to step 11
301+
10. Return `ObjectFromHandle((OBJECTHANDLE)pTLSBaseAddress)`
302+
11. Tail-call a helper `return GetThreadLocalStaticBase(index)`
303+
304+
This is the pattern that the JIT will use to access a thread static which is on `DirectOnThreadLocalData`
305+
0. Get the TLS index somehow
306+
1. Get TLS pointer to OS managed TLS block for the current thread ie. `pThreadLocalData = &t_ThreadStatics`
307+
2. Add the index offset to the start of the ThreadLocalData structure `pTLSBaseAddress = ((uint8_t*)pThreadLocalData) + index.GetIndexOffset()`
308+
309+
#### Lifetime management for thread static variables
310+
We distinguish between collectible and non-collectible thread static variables for efficiency purposes.
311+
312+
A non-collectible thread static is a thread static defined on a type which cannot be collected by the runtime.
313+
This describes most thread statics in actual observed practice.
314+
The `DirectOnThreadLocalData` statics are a subset of this category which has a speical optimized form and does not need any GC reporting.
315+
For non-collectible thread statics, the pointer (`pNonCollectibleTlsArrayData`) in the `ThreadLocalData` is a pointer to a managed `object[]` which points at either `object[]`, `byte[]`, or `double[]` arrays.
316+
At GC scan time, the pointer to the initial object[] is the only detail which needs to be reported to the GC.
317+
318+
A collectible thread static is a thread static which can be collected by the runtime.
319+
This describes the static variables defined on types which can be collected by the runtime.
320+
The pointer (`pCollectibleTlsArrayData`) in the `ThreadLocalData` is a pointer to a chunk of memory allocated via `malloc`, and holds pointers to `object[]`, `byte[]`, or `double[]` arrays.
321+
At GC scan time, each managed object must individually be kept alive only if the type and thread is still alive. This requires properly handling several situations.
322+
1. If a collectible assembly becomes unreferenced, but a thread static variable associated with it has a finalizer, the object must move to the finalization queue.
323+
2. If a thread static variable associated with a collectible assembly refers to the collectible assembly `LoaderAllocator` via a series of object references, it must not provide a reason for the collectible assembly to be considered referenced.
324+
3. If a collectible assembly is collected, then the associated static variables no longer exist, and the TLSIndex values associated with that collectible assembly becomes re-useable.
325+
4. If a thread is no longer executing, then all thread statics associated with that thread are no longer kept alive.
326+
327+
The approach chosen is to use a pair of different handle types.
328+
For efficient access, the handle type stored in the dynamically adjusted array is a WeakTrackResurrection GCHandle.
329+
This handle instance is associated with the slot in the TLS data, not with the exact instantiation, so it can be re-used when the if the associated collectible assembly is collected, and then the slot is re-used.
330+
In addition, each slot that is in use will have a `LOADERHANDLE` which will keep the object alive until the `LoaderAllocator` is freed.
331+
This `LOADERHANDLE` will be abandoned if the `LoaderAllocator` is collected, but that's ok, as `LOADERHANDLE` only needs to be cleaned up if the `LoaderAllocator` isn't collected.
332+
On thread destroy, for each collectible slot in the tls array, we will explicitly free the `LOADERHANDLE` on the correct `LoaderAllocator`.
209333

210334
Physical Architecture
211335
=====================
@@ -221,6 +345,7 @@ Major parts of the type system are found in:
221345
- Array – Code for handling the special cases required for array processing
222346
- VirtualStubDispatch.cpp/h/inl – Code for virtual stub dispatch
223347
- VirtualCallStubCpu.hpp – Processor specific code for virtual stub dispatch.
348+
- threadstatics.cpp/h - Handling for thread static variables.
224349

225350
Major entry points are BuildMethodTable, LoadTypeHandleThrowing, CanCastTo\*, GetMethodDescFromMemberDefOrRefOrSpecThrowing, GetFieldDescFromMemberRefThrowing, CompareSigs, and VirtualCallStubManager::ResolveWorkerStatic.
226351

eng/Subsets.props

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@
121121
<!-- CLR NativeAot only builds in a subset of the matrix -->
122122
<_NativeAotSupportedOS Condition="'$(TargetOS)' == 'windows' or '$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'freebsd'">true</_NativeAotSupportedOS>
123123
<_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true</_NativeAotSupportedArch>
124-
<NativeAotSupported Condition="'$(_NativeAotSupportedOS)' == 'true' and $(_NativeAotSupportedArch) == 'true'">true</NativeAotSupported>
124+
<NativeAotSupported Condition="'$(_NativeAotSupportedOS)' == 'true' and '$(_NativeAotSupportedArch)' == 'true'">true</NativeAotSupported>
125+
<UseNativeAotForComponents Condition="'$(NativeAotSupported)' == 'true' and '$(TargetOS)' == '$(HostOS)' and ('$(TargetOS)' != 'windows' or '$(TargetArchitecture)' != 'x86') and '$(TargetsLinuxBionic)' != 'true'">true</UseNativeAotForComponents>
125126

126127
<!-- If we're building clr.nativeaotlibs and not building the CLR runtime, compile libraries against NativeAOT CoreLib -->
127128
<UseNativeAotCoreLib Condition="'$(TestNativeAot)' == 'true' or ($(_subset.Contains('+clr.nativeaotlibs+')) and !$(_subset.Contains('+clr.native+')) and !$(_subset.Contains('+clr.runtime+')) and !$(_subset.Contains('+clr.corelib+')))">true</UseNativeAotCoreLib>
@@ -286,7 +287,10 @@
286287
AdditionalProperties="%(AdditionalProperties);
287288
ClrCrossComponentsSubset=true;
288289
HostArchitecture=$(BuildArchitecture);
290+
TargetArchitecture=$(TargetArchitecture);
289291
HostCrossOS=$(HostOS);
292+
HostOS=$(HostOS);
293+
TargetOS=$(TargetOS);
290294
PgoInstrument=false;
291295
NoPgoOptimize=true;
292296
CrossBuild=false;

0 commit comments

Comments
 (0)