Skip to content

Commit eb71e48

Browse files
Adjust managed type system for new function pointer handling (#84819)
After #81006, the calling convention is no longer part of the type system identity of a function pointer type - it serves more like a modopt as far as the type system is concerned. The type system only cares whether the pointer is managed or not. Adjust the managed type system accordingly: * If we're reading/representing a standalone method signature, read it as usual. Calling convention is available in flags/modopts. * If we're reading/representing a function pointer type, collapse the calling convention information into the managed/unmanaged bit only.
1 parent b0ee1c8 commit eb71e48

File tree

8 files changed

+93
-2
lines changed

8 files changed

+93
-2
lines changed

src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public enum EmbeddedSignatureDataKind
2727
{
2828
RequiredCustomModifier = 0,
2929
OptionalCustomModifier = 1,
30-
ArrayShape = 2
30+
ArrayShape = 2,
31+
UnmanagedCallConv = 3,
3132
}
3233

3334
public struct EmbeddedSignatureData

src/coreclr/tools/Common/TypeSystem/Common/TypeSystemContext.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,10 @@ protected override FunctionPointerType CreateValueFromKey(MethodSignature key)
242242

243243
public FunctionPointerType GetFunctionPointerType(MethodSignature signature)
244244
{
245+
// The type system only distinguishes between unmanaged and managed signatures.
246+
// The caller should have normalized the signature by modifying flags and stripping modopts.
247+
Debug.Assert((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) is 0 or MethodSignatureFlags.UnmanagedCallingConvention);
248+
Debug.Assert(!signature.HasEmbeddedSignatureData);
245249
return _functionPointerTypes.GetOrCreateValue(signature);
246250
}
247251

src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,19 @@ private MethodSignature ParseMethodSignatureImpl(bool skipEmbeddedSignatureData)
371371
Debug.Assert((int)MethodSignatureFlags.CallingConventionVarargs == (int)SignatureCallingConvention.VarArgs);
372372
Debug.Assert((int)MethodSignatureFlags.UnmanagedCallingConvention == (int)SignatureCallingConvention.Unmanaged);
373373

374-
flags = (MethodSignatureFlags)signatureCallConv;
374+
// If skipEmbeddedSignatureData is true, we're building the signature for the purposes of building a type.
375+
// We normalize unmanaged calling convention into a single value - "unmanaged".
376+
if (skipEmbeddedSignatureData)
377+
{
378+
flags = MethodSignatureFlags.UnmanagedCallingConvention;
379+
380+
// But we still need to remember this signature is different, so add this to the EmbeddedSignatureData of the owner signature.
381+
_embeddedSignatureDataList?.Add(new EmbeddedSignatureData { index = string.Join(".", _indexStack) + "|" + ((int)signatureCallConv).ToString(), kind = EmbeddedSignatureDataKind.UnmanagedCallConv, type = null });
382+
}
383+
else
384+
{
385+
flags = (MethodSignatureFlags)signatureCallConv;
386+
}
375387
}
376388

377389
if (!header.IsInstance)

src/coreclr/tools/Common/TypeSystem/MetadataEmitter/TypeSystemMetadataEmitter.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,27 @@ public void Push()
514514
}
515515
}
516516

517+
public void UpdateSignatureCallingConventionAtCurrentIndexStack(ref SignatureCallingConvention callConv)
518+
{
519+
if (!Complete)
520+
{
521+
if (_embeddedDataIndex < _embeddedData.Length)
522+
{
523+
if (_embeddedData[_embeddedDataIndex].kind == EmbeddedSignatureDataKind.UnmanagedCallConv)
524+
{
525+
string indexData = string.Join(".", _indexStack);
526+
527+
var unmanagedCallConvPossibility = _embeddedData[_embeddedDataIndex].index.Split('|');
528+
if (unmanagedCallConvPossibility[0] == indexData)
529+
{
530+
callConv = (SignatureCallingConvention)int.Parse(unmanagedCallConvPossibility[1]);
531+
_embeddedDataIndex++;
532+
}
533+
}
534+
}
535+
}
536+
}
537+
517538
public void EmitArrayShapeAtCurrentIndexStack(BlobBuilder signatureBuilder, int rank)
518539
{
519540
var shapeEncoder = new ArrayShapeEncoder(signatureBuilder);
@@ -665,6 +686,9 @@ private void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature
665686
break;
666687
}
667688

689+
if (sigCallingConvention != SignatureCallingConvention.Default)
690+
signatureDataEmitter.UpdateSignatureCallingConventionAtCurrentIndexStack(ref sigCallingConvention);
691+
668692
signatureEncoder.MethodSignature(sigCallingConvention, genericParameterCount, isInstanceMethod);
669693
signatureBuilder.WriteCompressedInteger(sig.Length);
670694
EncodeType(signatureBuilder, sig.ReturnType, signatureDataEmitter);

src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/CoreTestAssembly.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<AssemblyName>CoreTestAssembly</AssemblyName>
55
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
66
<IsCoreAssembly>true</IsCoreAssembly>
7+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
78
<SkipTestRun>true</SkipTestRun>
89
<TargetFramework>netstandard2.0</TargetFramework>
910
<!-- Don't add references to the netstandard platform since this is a core assembly -->

src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/Platform.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ public struct RuntimeTypeHandle { }
6060
public struct RuntimeMethodHandle { }
6161
public struct RuntimeFieldHandle { }
6262

63+
public class Type
64+
{
65+
public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null;
66+
}
67+
6368
public class Attribute { }
6469
public class AttributeUsageAttribute : Attribute
6570
{
@@ -159,9 +164,14 @@ public sealed class IsByRefLikeAttribute : Attribute
159164
{
160165
}
161166

167+
public class CallConvCdecl { }
168+
public class CallConvStdcall { }
169+
public class CallConvSuppressGCTransition { }
170+
162171
public static class RuntimeFeature
163172
{
164173
public const string ByRefFields = nameof(ByRefFields);
174+
public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
165175
public const string VirtualStaticsInInterfaces = nameof(VirtualStaticsInInterfaces);
166176
}
167177

src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/VirtualFunctionOverride.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,22 @@ class ClassWithFinalizer
4242

4343
}
4444
}
45+
46+
unsafe class FunctionPointerOverloadBase
47+
{
48+
// Do not reorder these, the test assumes this order
49+
public virtual Type Method(delegate* unmanaged[Cdecl]<void> p) => typeof(delegate* unmanaged[Cdecl]<void>);
50+
public virtual Type Method(delegate* unmanaged[Stdcall]<void> p) => typeof(delegate* unmanaged[Stdcall]<void>);
51+
public virtual Type Method(delegate* unmanaged[Stdcall, SuppressGCTransition]<void> p) => typeof(delegate* unmanaged[Stdcall, SuppressGCTransition]<void>);
52+
public virtual Type Method(delegate*<void> p) => typeof(delegate*<void>);
53+
}
54+
55+
unsafe class FunctionPointerOverloadDerived : FunctionPointerOverloadBase
56+
{
57+
// Do not reorder these, the test assumes this order
58+
public override Type Method(delegate* unmanaged[Cdecl]<void> p) => null;
59+
public override Type Method(delegate* unmanaged[Stdcall]<void> p) => null;
60+
public override Type Method(delegate* unmanaged[Stdcall, SuppressGCTransition]<void> p) => null;
61+
public override Type Method(delegate*<void> p) => null;
62+
}
4563
}

src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/VirtualFunctionOverrideTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using Internal.TypeSystem;
78

@@ -268,5 +269,25 @@ public void TestExactMethodImplGenericDeclResolutionOnInterfaces()
268269
Assert.Contains("!0,!1", md1.Name);
269270
Assert.Contains("!1,!0", md2.Name);
270271
}
272+
273+
[Fact]
274+
public void TestFunctionPointerOverloads()
275+
{
276+
MetadataType baseClass = _testModule.GetType("VirtualFunctionOverride", "FunctionPointerOverloadBase");
277+
MetadataType derivedClass = _testModule.GetType("VirtualFunctionOverride", "FunctionPointerOverloadDerived");
278+
279+
var resolvedMethods = new List<MethodDesc>();
280+
foreach (MethodDesc baseMethod in baseClass.GetVirtualMethods())
281+
resolvedMethods.Add(derivedClass.FindVirtualFunctionTargetMethodOnObjectType(baseMethod));
282+
283+
var expectedMethods = new List<MethodDesc>();
284+
foreach (MethodDesc derivedMethod in derivedClass.GetVirtualMethods())
285+
expectedMethods.Add(derivedMethod);
286+
287+
Assert.Equal(expectedMethods, resolvedMethods);
288+
289+
Assert.Equal(expectedMethods[0].Signature[0], expectedMethods[1].Signature[0]);
290+
Assert.NotEqual(expectedMethods[0].Signature[0], expectedMethods[3].Signature[0]);
291+
}
271292
}
272293
}

0 commit comments

Comments
 (0)