Skip to content

Commit 2bcadad

Browse files
Allow using UTF-8 in CompilerGeneratedNames (#119605)
The managed type system switched to UTF-8, The `CompilerGeneratedNames` class is forcing us to materialize hundreds of thousands of UTF-16 strings (in a `dotnet new webapiaot` sample). Add overloads that take UTF-8. I was thinking to do some code reuse with clever use of structs and interfaces but this is one of the cases where simpler is just better, so a copy it is.
1 parent a3bdddc commit 2bcadad

File tree

6 files changed

+111
-26
lines changed

6 files changed

+111
-26
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedCallGraph.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,24 @@ public void TrackCall(MethodDesc fromMethod, MethodDesc toMethod)
3030
{
3131
Debug.Assert(fromMethod.IsTypicalMethodDefinition);
3232
Debug.Assert(toMethod.IsTypicalMethodDefinition);
33-
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.GetName()));
33+
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.Name));
3434
TrackCallInternal(fromMethod, toMethod);
3535
}
3636

3737
public void TrackCall(MethodDesc fromMethod, DefType toType)
3838
{
3939
Debug.Assert(fromMethod.IsTypicalMethodDefinition);
4040
Debug.Assert(toType.IsTypeDefinition);
41-
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(toType.GetName()));
41+
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(toType.Name));
4242
TrackCallInternal(fromMethod, toType);
4343
}
4444

4545
public void TrackCall(DefType fromType, MethodDesc toMethod)
4646
{
4747
Debug.Assert(fromType.IsTypeDefinition);
4848
Debug.Assert(toMethod.IsTypicalMethodDefinition);
49-
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(fromType.GetName()));
50-
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.GetName()));
49+
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(fromType.Name));
50+
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(toMethod.Name));
5151
TrackCallInternal(fromType, toMethod);
5252
}
5353

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/CompilerGeneratedState.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ internal void LogWarnings(Logger? logger)
111111
internal TypeCache(MetadataType type, ILProvider ilProvider)
112112
{
113113
Debug.Assert(type == type.GetTypeDefinition());
114-
Debug.Assert(!CompilerGeneratedNames.IsStateMachineOrDisplayClass(type.GetName()));
114+
Debug.Assert(!CompilerGeneratedNames.IsStateMachineOrDisplayClass(type.Name));
115115

116116
Type = type;
117117

@@ -132,8 +132,8 @@ void ProcessMethod(MethodDesc method)
132132
{
133133
Debug.Assert(method == method.GetTypicalMethodDefinition());
134134

135-
bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(((MetadataType)method.OwningType).GetName());
136-
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.GetName()))
135+
bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(((MetadataType)method.OwningType).Name);
136+
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name))
137137
{
138138
if (!isStateMachineMember)
139139
{
@@ -177,7 +177,7 @@ void ProcessMethod(MethodDesc method)
177177
referencedMethod.OwningType is MetadataType generatedType &&
178178
// Don't consider calls in the same/nested type, like inside a static constructor
179179
!IsSameOrNestedType(method.OwningType, generatedType) &&
180-
CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.GetName()))
180+
CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.Name))
181181
{
182182
Debug.Assert(generatedType.IsTypeDefinition);
183183

@@ -190,7 +190,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
190190
continue;
191191
}
192192

193-
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(referencedMethod.GetName()))
193+
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(referencedMethod.Name))
194194
continue;
195195

196196
if (isStateMachineMember)
@@ -218,7 +218,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
218218
if (field.OwningType is MetadataType generatedType &&
219219
// Don't consider field accesses in the same/nested type, like inside a static constructor
220220
!IsSameOrNestedType(method.OwningType, generatedType) &&
221-
CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.GetName()))
221+
CompilerGeneratedNames.IsLambdaDisplayClass(generatedType.Name))
222222
{
223223
Debug.Assert(generatedType.IsTypeDefinition);
224224

@@ -243,7 +243,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
243243
if (TryGetStateMachineType(method, out MetadataType? stateMachineType))
244244
{
245245
Debug.Assert(stateMachineType.ContainingType == type ||
246-
(CompilerGeneratedNames.IsStateMachineOrDisplayClass(stateMachineType.ContainingType.GetName()) &&
246+
(CompilerGeneratedNames.IsStateMachineOrDisplayClass(stateMachineType.ContainingType.Name) &&
247247
stateMachineType.ContainingType.ContainingType == type));
248248
Debug.Assert(stateMachineType == stateMachineType.GetTypeDefinition());
249249

@@ -319,7 +319,7 @@ static bool IsSameOrNestedType(TypeDesc type, TypeDesc potentialOuterType)
319319
switch (compilerGeneratedMember)
320320
{
321321
case MethodDesc nestedFunction:
322-
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.GetName()));
322+
Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.Name));
323323
// Nested functions get suppressions from the user method only.
324324
_compilerGeneratedMethodToUserCodeMethod ??= new Dictionary<MethodDesc, MethodDesc>();
325325
if (!_compilerGeneratedMethodToUserCodeMethod.TryAdd(nestedFunction, userDefinedMethod))
@@ -334,7 +334,7 @@ static bool IsSameOrNestedType(TypeDesc type, TypeDesc potentialOuterType)
334334
// are represented by the state machine type itself.
335335
// We are already tracking the association of the state machine type to the user code method
336336
// above, so no need to track it here.
337-
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.GetName()));
337+
Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.Name));
338338
break;
339339
default:
340340
throw new InvalidOperationException();
@@ -386,7 +386,7 @@ void MapGeneratedTypeTypeParameters(
386386
MetadataType generatedType,
387387
Dictionary<MetadataType, TypeArgumentInfo> generatedTypeToTypeArgs)
388388
{
389-
Debug.Assert(CompilerGeneratedNames.IsStateMachineOrDisplayClass(generatedType.GetName()));
389+
Debug.Assert(CompilerGeneratedNames.IsStateMachineOrDisplayClass(generatedType.Name));
390390
Debug.Assert(generatedType == generatedType.GetTypeDefinition());
391391

392392
var typeInfo = generatedTypeToTypeArgs[generatedType];
@@ -426,7 +426,7 @@ void MapGeneratedTypeTypeParameters(
426426
else
427427
{
428428
// Must be a type ref
429-
if (method.OwningType is not MetadataType owningType || !CompilerGeneratedNames.IsStateMachineOrDisplayClass(owningType.GetName()))
429+
if (method.OwningType is not MetadataType owningType || !CompilerGeneratedNames.IsStateMachineOrDisplayClass(owningType.Name))
430430
{
431431
userAttrs = param;
432432
}
@@ -567,7 +567,7 @@ private static IEnumerable<MetadataType> GetCompilerGeneratedNestedTypes(Metadat
567567
{
568568
foreach (var nestedType in type.GetNestedTypes())
569569
{
570-
if (!CompilerGeneratedNames.IsStateMachineOrDisplayClass(nestedType.GetName()))
570+
if (!CompilerGeneratedNames.IsStateMachineOrDisplayClass(nestedType.Name))
571571
continue;
572572

573573
yield return nestedType;
@@ -579,14 +579,14 @@ private static IEnumerable<MetadataType> GetCompilerGeneratedNestedTypes(Metadat
579579

580580
public static bool IsHoistedLocal(FieldDesc field)
581581
{
582-
if (CompilerGeneratedNames.IsLambdaDisplayClass(field.OwningType.GetName()))
582+
if (CompilerGeneratedNames.IsLambdaDisplayClass(field.OwningType.Name))
583583
return true;
584584

585-
if (CompilerGeneratedNames.IsStateMachineType(field.OwningType.GetName()))
585+
if (CompilerGeneratedNames.IsStateMachineType(field.OwningType.Name))
586586
{
587587
// Don't track the "current" field which is used for state machine return values,
588588
// because this can be expensive to track.
589-
return !CompilerGeneratedNames.IsStateMachineCurrentField(field.GetName());
589+
return !CompilerGeneratedNames.IsStateMachineCurrentField(field.Name);
590590
}
591591

592592
return false;
@@ -595,13 +595,13 @@ public static bool IsHoistedLocal(FieldDesc field)
595595
// "Nested function" refers to lambdas and local functions.
596596
public static bool IsNestedFunctionOrStateMachineMember(TypeSystemEntity member)
597597
{
598-
if (member is MethodDesc method && CompilerGeneratedNames.IsLambdaOrLocalFunction(method.GetName()))
598+
if (member is MethodDesc method && CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name))
599599
return true;
600600

601601
if (member.GetOwningType() is not MetadataType declaringType)
602602
return false;
603603

604-
return CompilerGeneratedNames.IsStateMachineType(declaringType.GetName());
604+
return CompilerGeneratedNames.IsStateMachineType(declaringType.Name);
605605
}
606606

607607
public static bool TryGetStateMachineType(MethodDesc method, [NotNullWhen(true)] out MetadataType? stateMachineType)
@@ -631,7 +631,7 @@ public static bool TryGetStateMachineType(MethodDesc method, [NotNullWhen(true)]
631631
// State machines can be emitted into display classes, so we may also need to go one more level up.
632632
// To avoid depending on implementation details, we go up until we see a non-compiler-generated type.
633633
// This is the counterpart to GetCompilerGeneratedNestedTypes.
634-
while (userType != null && CompilerGeneratedNames.IsStateMachineOrDisplayClass(userType.GetName()))
634+
while (userType != null && CompilerGeneratedNames.IsStateMachineOrDisplayClass(userType.Name))
635635
userType = userType.ContainingType as MetadataType;
636636

637637
if (userType is null)
@@ -676,7 +676,7 @@ public bool TryGetCompilerGeneratedCalleesForUserMethod(MethodDesc method, [NotN
676676
public IReadOnlyList<GenericParameterDesc?>? GetGeneratedTypeAttributes(MetadataType type)
677677
{
678678
MetadataType generatedType = (MetadataType)type.GetTypeDefinition();
679-
Debug.Assert(CompilerGeneratedNames.IsStateMachineOrDisplayClass(generatedType.GetName()));
679+
Debug.Assert(CompilerGeneratedNames.IsStateMachineOrDisplayClass(generatedType.Name));
680680

681681
// Avoid the heuristics for .NET10+, where DynamicallyAccessedMembers flows to generated code
682682
// because it is annotated with CompilerLoweringPreserveAttribute.

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key)
655655

656656
private IReadOnlyList<GenericParameterDesc?>? GetGeneratedTypeAttributes(EcmaType typeDef)
657657
{
658-
if (!CompilerGeneratedNames.IsStateMachineOrDisplayClass(typeDef.GetName()))
658+
if (!CompilerGeneratedNames.IsStateMachineOrDisplayClass(typeDef.Name))
659659
{
660660
return null;
661661
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/InterproceduralState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public void TrackMethod(MethodIL methodBody)
7878
{
7979
foreach (var stateMachineMethod in stateMachineType.GetMethods())
8080
{
81-
Debug.Assert(!CompilerGeneratedNames.IsLambdaOrLocalFunction(stateMachineMethod.GetName()));
81+
Debug.Assert(!CompilerGeneratedNames.IsLambdaOrLocalFunction(stateMachineMethod.Name));
8282
if (TryGetMethodBody(stateMachineMethod, out MethodIL? stateMachineMethodBody))
8383
{
8484
stateMachineMethodBody = GetInstantiatedMethodIL(stateMachineMethodBody);

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ private static void TrackNestedFunctionReference(MethodDesc referencedMethod, re
335335
{
336336
MethodDesc method = referencedMethod.GetTypicalMethodDefinition();
337337

338-
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.GetName()))
338+
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name))
339339
return;
340340

341341
interproceduralState.TrackMethod(method);

src/tools/illink/src/ILLink.Shared/DataFlow/CompilerGeneratedNames.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System;
5+
using System.Diagnostics;
6+
47
namespace ILLink.Shared.DataFlow
58
{
69
internal sealed class CompilerGeneratedNames
@@ -10,6 +13,11 @@ private static bool IsGeneratedMemberName(string memberName)
1013
return memberName.Length > 0 && memberName[0] == '<';
1114
}
1215

16+
private static bool IsGeneratedMemberName(ReadOnlySpan<byte> memberName)
17+
{
18+
return memberName.Length > 0 && memberName[0] == '<';
19+
}
20+
1321
internal static bool IsLambdaDisplayClass(string className)
1422
{
1523
if (!IsGeneratedMemberName(className))
@@ -20,6 +28,16 @@ internal static bool IsLambdaDisplayClass(string className)
2028
return className.StartsWith("<>c");
2129
}
2230

31+
internal static bool IsLambdaDisplayClass(ReadOnlySpan<byte> className)
32+
{
33+
if (!IsGeneratedMemberName(className))
34+
return false;
35+
36+
// This is true for static lambdas (which are emitted into a class like <>c)
37+
// and for instance lambdas (which are emitted into a class like <>c__DisplayClass1_0)
38+
return className.StartsWith("<>c"u8);
39+
}
40+
2341
internal static bool IsStateMachineType(string typeName)
2442
{
2543
if (!IsGeneratedMemberName(typeName))
@@ -34,6 +52,20 @@ internal static bool IsStateMachineType(string typeName)
3452
return typeName.Length > i + 1 && typeName[i + 1] == 'd';
3553
}
3654

55+
internal static bool IsStateMachineType(ReadOnlySpan<byte> typeName)
56+
{
57+
if (!IsGeneratedMemberName(typeName))
58+
return false;
59+
60+
// State machines are generated into types with names like <OwnerMethodName>d__0
61+
// Or if its nested in a local function the name will look like <<OwnerMethodName>g__Local>d and so on
62+
int i = typeName.LastIndexOf('>');
63+
if (i == -1)
64+
return false;
65+
66+
return typeName.Length > i + 1 && typeName[i + 1] == 'd';
67+
}
68+
3769
internal static bool IsStateMachineCurrentField(string fieldName)
3870
{
3971
if (!IsGeneratedMemberName(fieldName))
@@ -47,10 +79,27 @@ internal static bool IsStateMachineCurrentField(string fieldName)
4779
return fieldName.Length > i + 1 && fieldName[i + 1] == '2';
4880
}
4981

82+
internal static bool IsStateMachineCurrentField(ReadOnlySpan<byte> fieldName)
83+
{
84+
if (!IsGeneratedMemberName(fieldName))
85+
return false;
86+
87+
int i = fieldName.LastIndexOf('>');
88+
if (i == -1)
89+
return false;
90+
91+
// Current field is <>2__current
92+
return fieldName.Length > i + 1 && fieldName[i + 1] == '2';
93+
}
94+
5095
internal static bool IsStateMachineOrDisplayClass(string name) => IsStateMachineType(name) || IsLambdaDisplayClass(name);
5196

97+
internal static bool IsStateMachineOrDisplayClass(ReadOnlySpan<byte> name) => IsStateMachineType(name) || IsLambdaDisplayClass(name);
98+
5299
internal static bool IsLambdaOrLocalFunction(string methodName) => IsLambdaMethod(methodName) || IsLocalFunction(methodName);
53100

101+
internal static bool IsLambdaOrLocalFunction(ReadOnlySpan<byte> methodName) => IsLambdaMethod(methodName) || IsLocalFunction(methodName);
102+
54103
// Lambda methods have generated names like "<UserMethod>b__0_1" where "UserMethod" is the name
55104
// of the original user code that contains the lambda method declaration.
56105
// The user method name may include a (potentially generic) interface type name for
@@ -68,6 +117,19 @@ internal static bool IsLambdaMethod(string methodName)
68117
return methodName.Length > i + 1 && methodName[i + 1] == 'b';
69118
}
70119

120+
internal static bool IsLambdaMethod(ReadOnlySpan<byte> methodName)
121+
{
122+
if (!IsGeneratedMemberName(methodName))
123+
return false;
124+
125+
int i = methodName.LastIndexOf('>');
126+
if (i == -1)
127+
return false;
128+
129+
// Ignore the method ordinal/generation and lambda ordinal/generation.
130+
return methodName.Length > i + 1 && methodName[i + 1] == 'b';
131+
}
132+
71133
// Local functions have generated names like "<UserMethod>g__LocalFunction|0_1" where "UserMethod" is the name
72134
// of the original user code that contains the lambda method declaration, and "LocalFunction" is the name of
73135
// the local function.
@@ -85,5 +147,28 @@ internal static bool IsLocalFunction(string methodName)
85147
// Ignore the method ordinal/generation and local function ordinal/generation.
86148
return methodName.Length > i + 1 && methodName[i + 1] == 'g';
87149
}
150+
151+
internal static bool IsLocalFunction(ReadOnlySpan<byte> methodName)
152+
{
153+
if (!IsGeneratedMemberName(methodName))
154+
return false;
155+
156+
int i = methodName.LastIndexOf('>');
157+
if (i == -1)
158+
return false;
159+
160+
// Ignore the method ordinal/generation and local function ordinal/generation.
161+
return methodName.Length > i + 1 && methodName[i + 1] == 'g';
162+
}
163+
}
164+
165+
file static class Extensions
166+
{
167+
public static int LastIndexOf(this ReadOnlySpan<byte> span, char ch)
168+
{
169+
// UTF-8 is byte-accurate, so searching for characters that fit under 0x80 means searching for the byte in valid UTF-8 strings.
170+
Debug.Assert(ch < 0x80);
171+
return span.LastIndexOf((byte)ch);
172+
}
88173
}
89174
}

0 commit comments

Comments
 (0)