Skip to content

Commit a66d14e

Browse files
[main] Source code updates from dotnet/runtime (#5150)
Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
1 parent 0f9b4fe commit a66d14e

File tree

121 files changed

+2128
-872
lines changed

Some content is hidden

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

121 files changed

+2128
-872
lines changed

src/runtime/docs/design/datacontracts/GC.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ private HandleData CreateHandleData(TargetPointer handleAddress, byte uBlock, ui
718718
{
719719
IObject obj = target.Contracts.Object;
720720
TargetPointer handle = target.ReadPointer(handleAddress);
721-
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw);
721+
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw, out _);
722722
if (ccw != TargetPointer.Null)
723723
{
724724
IBuiltInCOM builtInCOM = target.Contracts.BuiltInCOM;

src/runtime/docs/design/datacontracts/Object.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ string GetStringValue(TargetPointer address);
1414
// Get the pointer to the data corresponding to a managed array object. Error if address does not represent a array.
1515
TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds);
1616

17-
// Get built-in COM data for the object if available. Returns false, if address does not represent a COM object using built-in COM
18-
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);
17+
// Get built-in COM data for the object if available. Returns false if address does not represent a COM object using built-in COM.
18+
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf);
1919
```
2020

2121
## Version 1
@@ -26,6 +26,7 @@ Data descriptors used:
2626
| `Array` | `m_NumComponents` | Number of items in the array |
2727
| `InteropSyncBlockInfo` | `RCW` | Pointer to the RCW for the object (if it exists) |
2828
| `InteropSyncBlockInfo` | `CCW` | Pointer to the CCW for the object (if it exists) |
29+
| `InteropSyncBlockInfo` | `CCF` | Pointer to the COM class factory for the object (if it exists) |
2930
| `Object` | `m_pMethTab` | Method table for the object |
3031
| `String` | `m_FirstChar` | First character of the string - `m_StringLength` can be used to read the full string (encoded in UTF-16) |
3132
| `String` | `m_StringLength` | Length of the string in characters (encoded in UTF-16) |
@@ -105,7 +106,7 @@ TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPoin
105106
return address + dataOffset;
106107
}
107108

108-
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);
109+
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf)
109110
{
110111
uint syncBlockValue = target.Read<uint>(address - _target.ReadGlobal<ushort>("SyncBlockValueToObjectOffset"));
111112

@@ -125,8 +126,12 @@ bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetP
125126
if (interopInfo == TargetPointer.Null)
126127
return false;
127128

128-
rcw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::RCW offset */);
129-
ccw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
130-
return rcw != TargetPointer.Null && ccw != TargetPointer.Null;
129+
TargetPointer rcw1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::RCW offset */);
130+
rcw = rcw1 & ~1ul;
131+
TargetPointer ccw1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
132+
ccw = (ccw1 == 1) ? TargetPointer.Null : ccw1;
133+
TargetPointer ccf1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCF offset */);
134+
ccf = (ccf1 == 1) ? TargetPointer.Null : ccf1;
135+
return rcw != TargetPointer.Null || ccw != TargetPointer.Null || ccf != TargetPointer.Null;
131136
}
132137
```

src/runtime/docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ The contract depends on the following globals
372372
| --- | --- |
373373
| `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory
374374
| `StaticsPointerMask` | For masking out a bit of DynamicStaticsInfo pointer fields
375+
| `ArrayBaseSize` | The base size of an array object; used to compute multidimensional array rank from `MethodTable::BaseSize`
375376

376377
The contract additionally depends on these data descriptors
377378

@@ -403,7 +404,6 @@ The contract additionally depends on these data descriptors
403404
| `EEClass` | `NumStaticFields` | Count of static fields of the EEClass |
404405
| `EEClass` | `NumThreadStaticFields` | Count of threadstatic fields of the EEClass |
405406
| `EEClass` | `FieldDescList` | A list of fields in the type |
406-
| `ArrayClass` | `Rank` | Rank of the associated array MethodTable |
407407
| `TypeDesc` | `TypeAndFlags` | The lower 8 bits are the CorElementType of the `TypeDesc`, the upper 24 bits are reserved for flags |
408408
| `ParamTypeDesc` | `TypeArg` | Associated type argument |
409409
| `TypeVarTypeDesc` | `Module` | Pointer to module which defines the type variable |
@@ -695,8 +695,9 @@ Contracts used:
695695
switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
696696
{
697697
case WFLAGS_HIGH.Category_Array:
698-
TargetPointer clsPtr = GetClassPointer(typeHandle);
699-
rank = // Read Rank field from ArrayClass contract using address clsPtr
698+
// Multidim array: BaseSize = ArrayBaseSize + Rank * sizeof(uint) * 2
699+
uint arrayBaseSize = target.ReadGlobal<uint>("ArrayBaseSize");
700+
rank = (typeHandle.Flags.BaseSize - arrayBaseSize) / (sizeof(uint) * 2);
700701
return true;
701702

702703
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Contract SyncBlock
2+
3+
This contract is for reading sync block table entries and lock state.
4+
5+
## APIs of contract
6+
7+
``` csharp
8+
TargetPointer GetSyncBlock(uint index);
9+
TargetPointer GetSyncBlockObject(uint index);
10+
bool IsSyncBlockFree(uint index);
11+
uint GetSyncBlockCount();
12+
bool TryGetLockInfo(TargetPointer syncBlock, out uint owningThreadId, out uint recursion);
13+
uint GetAdditionalThreadCount(TargetPointer syncBlock);
14+
```
15+
16+
## Version 1
17+
18+
Data descriptors used:
19+
| Data Descriptor Name | Field | Meaning |
20+
| --- | --- | --- |
21+
| `SyncTableEntry` | `SyncBlock` | Pointer to the sync block for a sync table entry |
22+
| `SyncTableEntry` | `Object` | Pointer to the object associated with a sync table entry |
23+
| `SyncBlockCache` | `FreeSyncTableIndex` | One past the highest sync table entry index allocated |
24+
| `SyncBlock` | `Lock` | Optional pointer to a `System.Threading.Lock` object payload |
25+
| `SyncBlock` | `ThinLock` | Thin-lock state bits |
26+
| `SyncBlock` | `LinkNext` | Head pointer for additional waiting threads list |
27+
| `SLink` | `Next` | Next link for the additional waiting threads list |
28+
29+
Global variables used:
30+
| Global Name | Type | Purpose |
31+
| --- | --- | --- |
32+
| `SyncTableEntries` | TargetPointer | Pointer to the sync table entries array |
33+
| `SyncBlockCache` | TargetPointer | Pointer to the runtime sync block cache |
34+
| `SyncBlockMaskLockThreadId` | uint32 | Mask for extracting thread id from `SyncBlock.ThinLock` |
35+
| `SyncBlockMaskLockRecursionLevel` | uint32 | Mask for extracting recursion level from `SyncBlock.ThinLock` |
36+
| `SyncBlockRecursionLevelShift` | uint32 | Shift value for `SyncBlock.ThinLock` recursion level |
37+
38+
### Contract Constants:
39+
| Name | Type | Purpose | Value |
40+
| --- | --- | --- | --- |
41+
| `LockStateName` | string | Field name in `System.Threading.Lock` storing monitor-held state bits. | `_state` |
42+
| `LockOwningThreadIdName` | string | Field name in `System.Threading.Lock` storing owning thread id. | `_owningThreadId` |
43+
| `LockRecursionCountName` | string | Field name in `System.Threading.Lock` storing monitor recursion count. | `_recursionCount` |
44+
| `LockName` | string | Type name used to resolve `System.Threading.Lock`. | `Lock` |
45+
| `LockNamespace` | string | Namespace used to resolve `System.Threading.Lock`. | `System.Threading` |
46+
47+
Contracts used:
48+
| Contract Name |
49+
| --- |
50+
| `Loader` |
51+
| `RuntimeTypeSystem` |
52+
| `EcmaMetadata` |
53+
54+
``` csharp
55+
TargetPointer GetSyncBlock(uint index)
56+
{
57+
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
58+
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
59+
return target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::SyncBlock offset */);
60+
}
61+
62+
TargetPointer GetSyncBlockObject(uint index)
63+
{
64+
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
65+
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
66+
return target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::Object offset */);
67+
}
68+
69+
bool IsSyncBlockFree(uint index)
70+
{
71+
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
72+
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
73+
TargetPointer obj = target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::Object offset */);
74+
return (obj.Value & 1) != 0;
75+
}
76+
77+
uint GetSyncBlockCount()
78+
{
79+
TargetPointer syncBlockCache = target.ReadPointer(target.ReadGlobalPointer("SyncBlockCache"));
80+
uint freeSyncTableIndex = target.Read<uint>(syncBlockCache + /* SyncBlockCache::FreeSyncTableIndex offset */);
81+
return freeSyncTableIndex - 1;
82+
}
83+
84+
bool TryGetLockInfo(TargetPointer syncBlock, out uint owningThreadId, out uint recursion)
85+
{
86+
owningThreadId = 0;
87+
recursion = 0;
88+
89+
TargetPointer lockObject = target.ReadPointer(syncBlock + /* SyncBlock::Lock offset */);
90+
91+
if (lockObject != TargetPointer.Null)
92+
{
93+
// Resolve System.Threading.Lock in System.Private.CoreLib by name using RuntimeTypeSystem contract, LockName and LockNamespace.
94+
uint state = ReadUintField(/* Lock type */, "LockStateName", /* RuntimeTypeSystem contract */, /* MetadataReader for SPC */, lockObject);
95+
bool monitorHeld = (state & 1) != 0;
96+
if (monitorHeld)
97+
{
98+
owningThreadId = ReadUintField(/* Lock type */, "LockOwningThreadIdName", /* contracts */, lockObject);
99+
recursion = ReadUintField(/* Lock type */, "LockRecursionCountName", /* contracts */, lockObject);
100+
}
101+
102+
return monitorHeld;
103+
}
104+
105+
uint thinLock = target.Read<uint>(syncBlock + /* SyncBlock::ThinLock offset */);
106+
if (thinLock != 0)
107+
{
108+
owningThreadId = thinLock & target.ReadGlobal<uint>("SyncBlockMaskLockThreadId");
109+
bool monitorHeld = owningThreadId != 0;
110+
if (monitorHeld)
111+
{
112+
recursion = (thinLock & target.ReadGlobal<uint>("SyncBlockMaskLockRecursionLevel"))
113+
>> (int)target.ReadGlobal<uint>("SyncBlockRecursionLevelShift");
114+
}
115+
116+
return monitorHeld;
117+
}
118+
119+
return false;
120+
}
121+
122+
private uint ReadUintField(TypeHandle enclosingType, string fieldName, IRuntimeTypeSystem rts, MetadataReader mdReader, TargetPointer dataAddr)
123+
{
124+
TargetPointer field = rts.GetFieldDescByName(enclosingType, fieldName);
125+
uint token = rts.GetFieldDescMemberDef(field);
126+
FieldDefinitionHandle fieldHandle = (FieldDefinitionHandle)MetadataTokens.Handle((int)token);
127+
FieldDefinition fieldDef = mdReader.GetFieldDefinition(fieldHandle);
128+
uint offset = rts.GetFieldDescOffset(field, fieldDef);
129+
return _target.Read<uint>(dataAddr + offset);
130+
}
131+
132+
uint GetAdditionalThreadCount(TargetPointer syncBlock)
133+
{
134+
uint threadCount = 0;
135+
TargetPointer next = target.ReadPointer(syncBlock + /* SyncBlock::LinkNext offset */);
136+
while (next != TargetPointer.Null && threadCount < 1000)
137+
{
138+
threadCount++;
139+
next = target.ReadPointer(next + /* SLink::Next offset */);
140+
}
141+
142+
return threadCount;
143+
}
144+
```

src/runtime/src/coreclr/inc/corinfo.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,6 @@ enum CorInfoTokenKind
14851485

14861486
// token comes from runtime async awaiting pattern
14871487
CORINFO_TOKENKIND_Await = 0x2000 | CORINFO_TOKENKIND_Method,
1488-
CORINFO_TOKENKIND_AwaitVirtual = 0x4000 | CORINFO_TOKENKIND_Method,
14891488
};
14901489

14911490
struct CORINFO_RESOLVED_TOKEN
@@ -2288,6 +2287,15 @@ class ICorStaticInfo
22882287
CORINFO_CLASS_HANDLE* classArg
22892288
) = 0;
22902289

2290+
// Get the other variant of an async method, if possible.
2291+
// If this is a method with async calling convention: returns the corresponding task-returning method.
2292+
// If this is a task-returning method: returns the corresponding method with async calling convention.
2293+
// Otherwise returns null.
2294+
virtual CORINFO_METHOD_HANDLE getAsyncOtherVariant(
2295+
CORINFO_METHOD_HANDLE ftn,
2296+
bool* variantIsThunk
2297+
) = 0;
2298+
22912299
// Given T, return the type of the default Comparer<T>.
22922300
// Returns null if the type can't be determined exactly.
22932301
virtual CORINFO_CLASS_HANDLE getDefaultComparerClass(

src/runtime/src/coreclr/inc/icorjitinfoimpl_generated.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ CORINFO_METHOD_HANDLE getInstantiatedEntry(
104104
CORINFO_METHOD_HANDLE* methodArg,
105105
CORINFO_CLASS_HANDLE* classArg) override;
106106

107+
CORINFO_METHOD_HANDLE getAsyncOtherVariant(
108+
CORINFO_METHOD_HANDLE ftn,
109+
bool* variantIsThunk) override;
110+
107111
CORINFO_CLASS_HANDLE getDefaultComparerClass(
108112
CORINFO_CLASS_HANDLE elemType) override;
109113

src/runtime/src/coreclr/inc/jiteeversionguid.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737

3838
#include <minipal/guid.h>
3939

40-
constexpr GUID JITEEVersionIdentifier = { /* cd664ea4-e14e-4fd4-9cb8-e8fc1e1b5a0a */
41-
0xcd664ea4,
42-
0xe14e,
43-
0x4fd4,
44-
{0x9c, 0xb8, 0xe8, 0xfc, 0x1e, 0x1b, 0x5a, 0x0a}
40+
constexpr GUID JITEEVersionIdentifier = { /* 22511e72-5ac8-4fc8-83ef-0b61688c68bb */
41+
0x22511e72,
42+
0x5ac8,
43+
0x4fc8,
44+
{0x83, 0xef, 0x0b, 0x61, 0x68, 0x8c, 0x68, 0xbb}
4545
};
4646

4747
#endif // JIT_EE_VERSIONING_GUID_H

src/runtime/src/coreclr/interpreter/compiler.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4616,6 +4616,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
46164616
}
46174617
else
46184618
{
4619+
const uint8_t* origIP = m_ip;
46194620
if (!newObj && m_methodInfo->args.isAsyncCall() && AsyncCallPeeps.FindAndApplyPeep(this))
46204621
{
46214622
resolvedCallToken = m_resolvedAsyncCallToken;
@@ -4641,14 +4642,34 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re
46414642

46424643
m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo);
46434644

4644-
if (callInfo.sig.isVarArg())
4645+
if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall())
46454646
{
4646-
BADCODE("Vararg methods are not supported in interpreted code");
4647+
BADCODE("We're trying to emit an async call, but the async resolved context didn't find one");
46474648
}
46484649

4649-
if (continuationContextHandling != ContinuationContextHandling::None && !callInfo.sig.isAsyncCall())
4650+
if (continuationContextHandling != ContinuationContextHandling::None && callInfo.kind == CORINFO_CALL)
46504651
{
4651-
BADCODE("We're trying to emit an async call, but the async resolved context didn't find one");
4652+
bool isSyncCallThunk;
4653+
m_compHnd->getAsyncOtherVariant(callInfo.hMethod, &isSyncCallThunk);
4654+
if (!isSyncCallThunk)
4655+
{
4656+
// The async variant that we got is a thunk. Switch back to the
4657+
// non-async task-returning call. There is no reason to create
4658+
// and go through the thunk.
4659+
ResolveToken(token, CORINFO_TOKENKIND_Method, &resolvedCallToken);
4660+
4661+
// Reset back to the IP after the original call instruction.
4662+
// FindAndApplyPeep above modified it to be after the "Await"
4663+
// call, but now we want to process the await separately.
4664+
m_ip = origIP + 5;
4665+
continuationContextHandling = ContinuationContextHandling::None;
4666+
m_compHnd->getCallInfo(&resolvedCallToken, pConstrainedToken, m_methodInfo->ftn, flags, &callInfo);
4667+
}
4668+
}
4669+
4670+
if (callInfo.sig.isVarArg())
4671+
{
4672+
BADCODE("Vararg methods are not supported in interpreted code");
46524673
}
46534674

46544675
if (isJmp)
@@ -6813,9 +6834,7 @@ int InterpCompiler::ApplyLdftnDelegateCtorPeep(const uint8_t* ip, OpcodePeepElem
68136834

68146835
bool InterpCompiler::ResolveAsyncCallToken(const uint8_t* ip)
68156836
{
6816-
CorInfoTokenKind tokenKind =
6817-
ip[0] == CEE_CALL ? CORINFO_TOKENKIND_Await : CORINFO_TOKENKIND_AwaitVirtual;
6818-
ResolveToken(getU4LittleEndian(ip + 1), tokenKind, &m_resolvedAsyncCallToken);
6837+
ResolveToken(getU4LittleEndian(ip + 1), CORINFO_TOKENKIND_Await, &m_resolvedAsyncCallToken);
68196838
return m_resolvedAsyncCallToken.hMethod != NULL;
68206839
}
68216840

src/runtime/src/coreclr/jit/ICorJitInfo_names_generated.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ DEF_CLR_API(getMethodVTableOffset)
2323
DEF_CLR_API(resolveVirtualMethod)
2424
DEF_CLR_API(getUnboxedEntry)
2525
DEF_CLR_API(getInstantiatedEntry)
26+
DEF_CLR_API(getAsyncOtherVariant)
2627
DEF_CLR_API(getDefaultComparerClass)
2728
DEF_CLR_API(getDefaultEqualityComparerClass)
2829
DEF_CLR_API(getSZArrayHelperEnumeratorClass)

src/runtime/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,16 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getInstantiatedEntry(
202202
return temp;
203203
}
204204

205+
CORINFO_METHOD_HANDLE WrapICorJitInfo::getAsyncOtherVariant(
206+
CORINFO_METHOD_HANDLE ftn,
207+
bool* variantIsThunk)
208+
{
209+
API_ENTER(getAsyncOtherVariant);
210+
CORINFO_METHOD_HANDLE temp = wrapHnd->getAsyncOtherVariant(ftn, variantIsThunk);
211+
API_LEAVE(getAsyncOtherVariant);
212+
return temp;
213+
}
214+
205215
CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass(
206216
CORINFO_CLASS_HANDLE elemType)
207217
{

0 commit comments

Comments
 (0)