Skip to content

Commit 118349f

Browse files
Handle RunClassConstructor with nonreflectable cctor (#62947)
This is now getting hit in #62927, so it's somewhat more urgent. (The feature switches from the SDK put us into the situation that triggers this bug around `RunClassConstructor` on an otherwise unused type.) Fixes dotnet/runtimelab#987. Remember what class constructor contexts we saw during scanning phase and if the owning type is also generated, assume `RunClassConstructor` could be used and ensure the cctor context is also generated in the compilation phase. This is somewhat less precise, but introducing a new node type for "a type used with `RunClassConstructor`" that dataflow analysis could report doesn't seem worth it.
1 parent 5e9381c commit 118349f

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace ILCompiler
2222
public sealed class AnalysisBasedMetadataManager : GeneratingMetadataManager, ICompilationRootProvider
2323
{
2424
private readonly List<ModuleDesc> _modulesWithMetadata;
25+
private readonly List<MetadataType> _typesWithRootedCctorContext;
2526

2627
private readonly Dictionary<TypeDesc, MetadataCategory> _reflectableTypes = new Dictionary<TypeDesc, MetadataCategory>();
2728
private readonly Dictionary<MethodDesc, MetadataCategory> _reflectableMethods = new Dictionary<MethodDesc, MetadataCategory>();
@@ -33,7 +34,8 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext)
3334
new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(),
3435
new NoDynamicInvokeThunkGenerationPolicy(), Array.Empty<ModuleDesc>(),
3536
Array.Empty<ReflectableEntity<TypeDesc>>(), Array.Empty<ReflectableEntity<MethodDesc>>(),
36-
Array.Empty<ReflectableEntity<FieldDesc>>(), Array.Empty<ReflectableCustomAttribute>())
37+
Array.Empty<ReflectableEntity<FieldDesc>>(), Array.Empty<ReflectableCustomAttribute>(),
38+
Array.Empty<MetadataType>())
3739
{
3840
}
3941

@@ -48,10 +50,12 @@ public AnalysisBasedMetadataManager(
4850
IEnumerable<ReflectableEntity<TypeDesc>> reflectableTypes,
4951
IEnumerable<ReflectableEntity<MethodDesc>> reflectableMethods,
5052
IEnumerable<ReflectableEntity<FieldDesc>> reflectableFields,
51-
IEnumerable<ReflectableCustomAttribute> reflectableAttributes)
53+
IEnumerable<ReflectableCustomAttribute> reflectableAttributes,
54+
IEnumerable<MetadataType> rootedCctorContexts)
5255
: base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy)
5356
{
5457
_modulesWithMetadata = new List<ModuleDesc>(modulesWithMetadata);
58+
_typesWithRootedCctorContext = new List<MetadataType>(rootedCctorContexts);
5559

5660
foreach (var refType in reflectableTypes)
5761
{
@@ -209,6 +213,11 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr
209213
}
210214
}
211215
}
216+
217+
foreach (var type in _typesWithRootedCctorContext)
218+
{
219+
rootProvider.RootNonGCStaticBaseForType(type, reason);
220+
}
212221
}
213222

214223
private struct Policy : IMetadataPolicy

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,10 +794,28 @@ public MetadataManager ToAnalysisBasedMetadataManager()
794794
}
795795
}
796796

797+
var rootedCctorContexts = new List<MetadataType>();
798+
foreach (NonGCStaticsNode cctorContext in GetCctorContextMapping())
799+
{
800+
// If we generated a static constructor and the owning type, this might be something
801+
// that gets fed to RuntimeHelpers.RunClassConstructor. RunClassConstructor
802+
// also works on reflection blocked types and there is a possibility that we
803+
// wouldn't have generated the cctor otherwise.
804+
//
805+
// This is a heuristic and we'll possibly root more cctor contexts than
806+
// strictly necessary, but it's not worth introducing a new node type
807+
// in the compiler just so we can propagate this knowledge from dataflow analysis
808+
// (that detects RunClassConstructor usage) and this spot.
809+
if (!TypeGeneratesEEType(cctorContext.Type))
810+
continue;
811+
812+
rootedCctorContexts.Add(cctorContext.Type);
813+
}
814+
797815
return new AnalysisBasedMetadataManager(
798816
_typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy,
799817
_modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(),
800-
reflectableFields.ToEnumerable(), _customAttributesWithMetadata);
818+
reflectableFields.ToEnumerable(), _customAttributesWithMetadata, rootedCctorContexts);
801819
}
802820

803821
private struct ReflectableEntityBuilder<T>

src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ private static int Main()
2727
//
2828
// Tests for dependency graph in the compiler
2929
//
30+
TestRunClassConstructor.Run();
3031
#if !OPTIMIZED_MODE_WITHOUT_SCANNER
3132
TestContainment.Run();
3233
TestInterfaceMethod.Run();
@@ -1655,6 +1656,26 @@ public static void Run()
16551656
}
16561657
}
16571658

1659+
class TestRunClassConstructor
1660+
{
1661+
static class TypeWithNoStaticFieldsButACCtor
1662+
{
1663+
static TypeWithNoStaticFieldsButACCtor()
1664+
{
1665+
s_cctorRan = true;
1666+
}
1667+
}
1668+
1669+
private static bool s_cctorRan;
1670+
1671+
public static void Run()
1672+
{
1673+
RuntimeHelpers.RunClassConstructor(typeof(TypeWithNoStaticFieldsButACCtor).TypeHandle);
1674+
if (!s_cctorRan)
1675+
throw new Exception();
1676+
}
1677+
}
1678+
16581679
#region Helpers
16591680

16601681
private static Type GetTestType(string testName, string typeName)

0 commit comments

Comments
 (0)