Skip to content

Commit f4ee321

Browse files
Fix compilation of Synchronized method combined with generics (#94203)
Fixes #77093. As I was looking at the bug, I realized it got miscategorized as native AOT, but the customer filed it for ReadyToRun. The bug is in both native AOT and ReadyToRun. I'm fixing it for both. We clearly didn't have any pre-existing test coverage, but as I was looking around the test tree, getclassfrommethodparam.cs was obviously testing this. For unexplained reasons, the Synchronized part got commented out. When I compared the file with the original in TFS at `$/DevDiv/FX/Feature/NetFXDev1/QA/CLR/testsrc/jit/Generics/Fields/getclassfrommethodparam.cs`, the commented out line is not commented out. So I'm fixing it. I've also added dedicated native aot test because this has dependency analysis implications that we need to be careful about.
1 parent c11e684 commit f4ee321

File tree

9 files changed

+91
-2
lines changed

9 files changed

+91
-2
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/TypeLoaderCallbacks.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Internal.Runtime.Augments
1414
[CLSCompliant(false)]
1515
public abstract class TypeLoaderCallbacks
1616
{
17+
public abstract bool TryGetOwningTypeForMethodDictionary(IntPtr dictionary, out RuntimeTypeHandle owningType);
1718
public abstract TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader);
1819
public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle);
1920
public abstract IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle handle, int index);

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
using System;
55
using System.Threading;
66

7+
using Internal.Runtime.Augments;
8+
9+
using Debug = System.Diagnostics.Debug;
10+
711
namespace Internal.Runtime.CompilerHelpers
812
{
913
/// <summary>
@@ -73,5 +77,14 @@ private static unsafe RuntimeType GetStaticLockObject(MethodTable* pMT)
7377
{
7478
return Type.GetTypeFromMethodTable(pMT);
7579
}
80+
81+
private static unsafe MethodTable* GetSyncFromClassHandle(MethodTable* pMT) => pMT;
82+
83+
private static unsafe MethodTable* GetClassFromMethodParam(IntPtr pDictionary)
84+
{
85+
bool success = RuntimeAugments.TypeLoaderCallbacks.TryGetOwningTypeForMethodDictionary(pDictionary, out RuntimeTypeHandle th);
86+
Debug.Assert(success);
87+
return th.ToMethodTable();
88+
}
7689
}
7790
}

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ namespace Internal.Runtime.TypeLoader
2222
{
2323
internal class Callbacks : TypeLoaderCallbacks
2424
{
25+
public override bool TryGetOwningTypeForMethodDictionary(IntPtr dictionary, out RuntimeTypeHandle owningType)
26+
{
27+
// PERF: computing NameAndSignature and the instantiation (that we discard) was useless
28+
return TypeLoaderEnvironment.Instance.TryGetGenericMethodComponents(dictionary, out owningType, out _, out _);
29+
}
30+
2531
public override TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader)
2632
{
2733
return ModuleList.Instance.GetModuleForMetadataReader(reader);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ public void GetDependenciesDueToGenericDictionary(ref DependencyList dependencie
391391
dependencies ??= new DependencyList();
392392
dependencies.Add(factory.GenericMethodsHashtableEntry(method), "Reflection visible dictionary");
393393
}
394+
395+
if (method.Signature.IsStatic && method.IsSynchronized)
396+
{
397+
dependencies ??= new DependencyList();
398+
dependencies.Add(factory.GenericMethodsHashtableEntry(method), "Will need to look up owning type from dictionary");
399+
}
394400
}
395401

396402
public IEnumerable<CombinedDependencyListEntry> GetConditionalDependenciesDueToGenericDictionary(NodeFactory factory, MethodDesc method)

src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ public DependencyList Import()
153153
{
154154
_dependencies.Add(_factory.NecessaryTypeSymbol(method.OwningType), reason);
155155
}
156+
157+
if (_canonMethod.IsCanonicalMethod(CanonicalFormKind.Any))
158+
{
159+
_dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetHelperEntryPoint("SynchronizedMethodHelpers", "GetSyncFromClassHandle")), reason);
160+
161+
if (_canonMethod.RequiresInstMethodDescArg())
162+
_dependencies.Add(_compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetHelperEntryPoint("SynchronizedMethodHelpers", "GetClassFromMethodParam")), reason);
163+
}
156164
}
157165
else
158166
{

src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,8 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
12571257

12581258
case CorInfoHelpFunc.CORINFO_HELP_INITCLASS:
12591259
case CorInfoHelpFunc.CORINFO_HELP_INITINSTCLASS:
1260+
case CorInfoHelpFunc.CORINFO_HELP_GETSYNCFROMCLASSHANDLE:
1261+
case CorInfoHelpFunc.CORINFO_HELP_GETCLASSFROMMETHODPARAM:
12601262
case CorInfoHelpFunc.CORINFO_HELP_THROW_ARGUMENTEXCEPTION:
12611263
case CorInfoHelpFunc.CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION:
12621264
case CorInfoHelpFunc.CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED:

src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,11 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum)
726726
id = ReadyToRunHelper.MonitorExitStatic;
727727
break;
728728

729+
case CorInfoHelpFunc.CORINFO_HELP_GETSYNCFROMCLASSHANDLE:
730+
return _compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetHelperEntryPoint("SynchronizedMethodHelpers", "GetSyncFromClassHandle"));
731+
case CorInfoHelpFunc.CORINFO_HELP_GETCLASSFROMMETHODPARAM:
732+
return _compilation.NodeFactory.MethodEntrypoint(_compilation.NodeFactory.TypeSystemContext.GetHelperEntryPoint("SynchronizedMethodHelpers", "GetClassFromMethodParam"));
733+
729734
case CorInfoHelpFunc.CORINFO_HELP_GVMLOOKUP_FOR_SLOT:
730735
id = ReadyToRunHelper.GVMLookupForSlot;
731736
break;

src/tests/JIT/Generics/Fields/getclassfrommethodparam.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ public class Foo<F>
1111
{
1212
public static string Value;
1313

14-
// [MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.NoInlining)]
15-
[MethodImpl(MethodImplOptions.NoInlining)]
14+
[MethodImpl(MethodImplOptions.Synchronized | MethodImplOptions.NoInlining)]
1615
public static void Action<T>(T value)
1716
{
1817
Value = value.ToString();

src/tests/nativeaot/SmokeTests/UnitTests/Generics.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static int Run()
1313
TestDictionaryDependencyTracking.Run();
1414
TestStaticBaseLookups.Run();
1515
TestInitThisClass.Run();
16+
TestSynchronizedMethods.Run();
1617
TestDelegateFatFunctionPointers.Run();
1718
TestDelegateToCanonMethods.Run();
1819
TestVirtualMethodUseTracking.Run();
@@ -602,6 +603,54 @@ public static void Run()
602603
}
603604
}
604605

606+
class TestSynchronizedMethods
607+
{
608+
static class Gen<T>
609+
{
610+
[MethodImpl(MethodImplOptions.Synchronized)]
611+
public static int Synchronize() => 42;
612+
}
613+
614+
static class NonGen
615+
{
616+
[MethodImpl(MethodImplOptions.Synchronized)]
617+
public static int Synchronize<T>() => 42;
618+
}
619+
620+
static class GenReflected<T>
621+
{
622+
[MethodImpl(MethodImplOptions.Synchronized)]
623+
public static int Synchronize() => 42;
624+
}
625+
626+
static class NonGenReflected
627+
{
628+
[MethodImpl(MethodImplOptions.Synchronized)]
629+
public static int Synchronize<T>() => 42;
630+
}
631+
632+
static Type s_genReflectedType = typeof(GenReflected<>);
633+
static MethodInfo s_nonGenReflectedSynchronizeMethod = typeof(NonGenReflected).GetMethod("Synchronize");
634+
635+
public static void Run()
636+
{
637+
Gen<object>.Synchronize();
638+
NonGen.Synchronize<object>();
639+
Gen<int>.Synchronize();
640+
NonGen.Synchronize<int>();
641+
642+
{
643+
var mi = (MethodInfo)s_genReflectedType.MakeGenericType(typeof(object)).GetMemberWithSameMetadataDefinitionAs(typeof(GenReflected<>).GetMethod("Synchronize"));
644+
mi.Invoke(null, Array.Empty<object>());
645+
}
646+
647+
{
648+
var mi = s_nonGenReflectedSynchronizeMethod.MakeGenericMethod(typeof(object));
649+
mi.Invoke(null, Array.Empty<object>());
650+
}
651+
}
652+
}
653+
605654
/// <summary>
606655
/// Tests that lazily built vtables for canonically equivalent types have the same shape.
607656
/// </summary>

0 commit comments

Comments
 (0)