Skip to content

Commit c4cf7c8

Browse files
authored
Merge branch 'main' into move-invariant-nodes-async
2 parents 585eab1 + b540c36 commit c4cf7c8

98 files changed

Lines changed: 2785 additions & 971 deletions

File tree

Some content is hidden

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

docs/design/datacontracts/GC.md

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public readonly struct GCOomData
125125
IReadOnlyList<GCMemoryRegionData> GetGCBookkeepingMemoryRegions();
126126
// Gets GC free regions (free region lists and freeable segments)
127127
IReadOnlyList<GCMemoryRegionData> GetGCFreeRegions();
128+
129+
// Enumerates every GC heap segment for the supplied heap data. Each yielded GCHeapSegmentInfo
130+
// describes a single segment with the inclusive start and exclusive end of its memory range
131+
// and its generation tag (or Ephemeral).
132+
IEnumerable<GCHeapSegmentInfo> EnumerateHeapSegments(GCHeapData heapData);
128133
```
129134

130135
```csharp
@@ -145,6 +150,26 @@ public readonly struct GCMemoryRegionData
145150
public ulong ExtraData { get; init; }
146151
public int Heap { get; init; }
147152
}
153+
154+
public enum GCSegmentClassification
155+
{
156+
Unknown,
157+
Gen0,
158+
Gen1,
159+
Gen2,
160+
LOH,
161+
POH,
162+
NonGC,
163+
// Segments-GC only: marker used by IGC.EnumerateHeapSegments to denote the ephemeral
164+
// segment on the gen2 list. The caller is responsible for splitting it into the Gen1
165+
// piece and an optional Gen2 prefix.
166+
Ephemeral,
167+
}
168+
169+
public readonly record struct GCHeapSegmentInfo(
170+
TargetPointer Start,
171+
TargetPointer End,
172+
GCSegmentClassification Generation);
148173
```
149174

150175
## Version 1
@@ -286,6 +311,7 @@ Constants used:
286311
| Name | Type | Purpose | Value |
287312
| --- | --- | --- | --- |
288313
| `WRK_HEAP_COUNT` | uint | The number of heaps in the `workstation` GC type | `1` |
314+
| `HEAP_SEGMENT_FLAGS_READONLY` | ulong | `HeapSegment.Flags` bit identifying a readonly (e.g. frozen, non-GC) segment. | `1` |
289315

290316
```csharp
291317
GCHeapType IGC.GetGCIdentifiers()
@@ -858,7 +884,7 @@ IReadOnlyList<GCMemoryRegionData> IGC.GetHandleTableMemoryRegions()
858884
// Safety caps matching native DAC
859885
const int MaxHandleTableRegions = 8192;
860886
const int MaxBookkeepingRegions = 32;
861-
const int MaxSegmentListIterations = 2048;
887+
const int MaxSegmentListIterations = 65536;
862888

863889
int maxRegions = MaxHandleTableRegions;
864890
TargetPointer handleTableMap = target.ReadGlobalPointer("HandleTableMap");
@@ -1027,3 +1053,79 @@ TargetNUInt IGC.GetHandleExtraInfo(TargetPointer handle)
10271053
return target.ReadNUInt(extraInfoAddr);
10281054
}
10291055
```
1056+
1057+
EnumerateHeapSegments
1058+
1059+
Returns the raw GC heap segments for a single heap by walking the per-generation segment
1060+
lists.
1061+
```csharp
1062+
IEnumerable<GCHeapSegmentInfo> IGC.EnumerateHeapSegments(GCHeapData heapData)
1063+
{
1064+
// The generation table is laid out as gen0, gen1, gen2, LOH, POH (plus optional extras).
1065+
var gens = heapData.GenerationTable;
1066+
bool regions = GetGCIdentifiers().Contains("regions");
1067+
1068+
TargetPointer ephemeralSegment = heapData.EphemeralHeapSegment;
1069+
TargetPointer allocAllocated = heapData.AllocAllocated;
1070+
1071+
if (regions)
1072+
{
1073+
// In regions mode each generation has its own segment list. Readonly entries on
1074+
// the gen2 list represent non-GC (e.g. frozen) regions and are reported as NonGC.
1075+
foreach (var (seg, _) in WalkSegmentList(gens[2].StartSegment))
1076+
{
1077+
var type = (seg.Flags & HEAP_SEGMENT_FLAGS_READONLY) != 0
1078+
? GCSegmentClassification.NonGC
1079+
: GCSegmentClassification.Gen2;
1080+
yield return new GCHeapSegmentInfo(seg.Mem, seg.Allocated, type);
1081+
}
1082+
foreach (var (seg, _) in WalkSegmentList(gens[1].StartSegment))
1083+
yield return new GCHeapSegmentInfo(seg.Mem, seg.Allocated, GCSegmentClassification.Gen1);
1084+
foreach (var (seg, segAddr) in WalkSegmentList(gens[0].StartSegment))
1085+
{
1086+
// For the gen0 segment that matches the ephemeral_heap_segment, end is alloc_allocated.
1087+
TargetPointer end = segAddr == ephemeralSegment ? allocAllocated : seg.Allocated;
1088+
yield return new GCHeapSegmentInfo(seg.Mem, end, GCSegmentClassification.Gen0);
1089+
}
1090+
}
1091+
else
1092+
{
1093+
// In segments mode the gen2 list contains every SOH segment. The ephemeral
1094+
// segment is tagged Ephemeral as the layer-2 split marker; non-ephemeral entries
1095+
// are reported with their true generation (Gen2 or NonGC for readonly).
1096+
foreach (var (seg, segAddr) in WalkSegmentList(gens[2].StartSegment))
1097+
{
1098+
GCSegmentClassification type;
1099+
if (segAddr == ephemeralSegment)
1100+
type = GCSegmentClassification.Ephemeral;
1101+
else if ((seg.Flags & HEAP_SEGMENT_FLAGS_READONLY) != 0)
1102+
type = GCSegmentClassification.NonGC;
1103+
else
1104+
type = GCSegmentClassification.Gen2;
1105+
TargetPointer end = segAddr == ephemeralSegment ? allocAllocated : seg.Allocated;
1106+
yield return new GCHeapSegmentInfo(seg.Mem, end, type);
1107+
}
1108+
}
1109+
1110+
// LOH and POH segments are always reported as-is regardless of GC mode.
1111+
foreach (var (seg, _) in WalkSegmentList(gens[3].StartSegment))
1112+
yield return new GCHeapSegmentInfo(seg.Mem, seg.Allocated, GCSegmentClassification.LOH);
1113+
foreach (var (seg, _) in WalkSegmentList(gens[4].StartSegment))
1114+
yield return new GCHeapSegmentInfo(seg.Mem, seg.Allocated, GCSegmentClassification.POH);
1115+
}
1116+
1117+
IEnumerable<(HeapSegment Segment, TargetPointer Address)> WalkSegmentList(TargetPointer startSegment)
1118+
{
1119+
// Bounded traversal of the singly-linked HeapSegment list, guarding against cycles or
1120+
// corrupt links via a fixed iteration cap (MaxSegmentListIterations = 65536).
1121+
int iterationMax = MaxSegmentListIterations;
1122+
TargetPointer current = startSegment;
1123+
while (current != TargetPointer.Null)
1124+
{
1125+
HeapSegment seg = /* read HeapSegment at current */;
1126+
yield return (seg, current);
1127+
current = seg.Next;
1128+
if (iterationMax-- <= 0) throw /* cycle detected */;
1129+
}
1130+
}
1131+
```

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ public enum OptimizationTier
157157
}
158158
```
159159

160+
```csharp
161+
public enum GenericContextLoc
162+
{
163+
None,
164+
InstArg,
165+
ThisPtr,
166+
}
167+
```
168+
160169
```csharp
161170
partial interface IRuntimeTypeSystem : IContract
162171
{
@@ -199,9 +208,9 @@ partial interface IRuntimeTypeSystem : IContract
199208
// Return true if a MethodDesc represents an IL stub with a special MethodDesc context arg
200209
public virtual bool HasMDContextArg(MethodDescHandle);
201210

202-
// Return true if the method requires a hidden instantiation argument (generic context parameter).
203-
// Corresponds to native MethodDesc::RequiresInstArg().
204-
public virtual bool RequiresInstArg(MethodDescHandle methodDesc);
211+
// Determine where a shared generic method obtains its generic context.
212+
// Returns None if not shared, InstArg if via a hidden parameter, or ThisPtr if via the 'this' pointer's MethodTable.
213+
public virtual GenericContextLoc GetGenericContextLoc(MethodDescHandle methodDesc);
205214

206215
// Return true if the method uses the async calling convention.
207216
// Corresponds to native MethodDesc::IsAsyncMethod().
@@ -1637,26 +1646,36 @@ Determining if a method is an async thunk method:
16371646
}
16381647
```
16391648

1640-
Determining if a method requires a hidden instantiation argument (generic context parameter):
1649+
Determining where a shared generic method obtains its generic context:
16411650

16421651
```csharp
1643-
public bool RequiresInstArg(MethodDescHandle methodDescHandle)
1652+
public GenericContextLoc GetGenericContextLoc(MethodDescHandle methodDescHandle)
16441653
{
16451654
MethodDesc md = _methodDescs[methodDescHandle.Address];
1646-
1647-
// RequiresInstArg = IsSharedByGenericInstantiations && (HasMethodInstantiation || IsStatic || IsValueType || IsInterface)
16481655
if (!IsSharedByGenericInstantiations(md))
1649-
return false;
1656+
return GenericContextLoc.None;
1657+
else if (RequiresInstArg(md))
1658+
return GenericContextLoc.InstArg;
1659+
else
1660+
return GenericContextLoc.ThisPtr;
1661+
}
16501662

1663+
private bool RequiresInstArg(MethodDesc md)
1664+
{
16511665
if (HasMethodInstantiation(md))
16521666
return true;
16531667

1654-
// md.IsStatic reads from MethodDescFlags.Static (0x0080)
16551668
if (md.IsStatic)
16561669
return true;
16571670

16581671
MethodTable mt = _methodTables[md.MethodTable];
1659-
return mt.Flags.IsInterface || mt.Flags.IsValueType;
1672+
if (mt.Flags.IsValueType)
1673+
return true;
1674+
1675+
if (mt.Flags.IsInterface && !IsAbstract(md) /* checked from md classification and metadata attributes */)
1676+
return true;
1677+
1678+
return false;
16601679
}
16611680

16621681
private bool IsSharedByGenericInstantiations(MethodDesc md)

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\GenericsHelpers.cs" />
207207
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\InitHelpers.cs" />
208208
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\AsyncHelpers.CoreCLR.cs" />
209+
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\AsyncHelpers.ValueTaskContinuation.cs" />
209210
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\AsyncProfiler.CoreCLR.cs" />
210211
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
211212
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\StaticsHelpers.cs" />

0 commit comments

Comments
 (0)