Copy ctor handling for crossgen2 (#40113)
authorDavid Wrighton <davidwr@microsoft.com>
Thu, 30 Jul 2020 19:04:45 +0000 (12:04 -0700)
committerGitHub <noreply@github.com>
Thu, 30 Jul 2020 19:04:45 +0000 (12:04 -0700)
- Add IL based test for copy ctor to allow testing in Crossgen2 which doesn't yet support IJW
- Add api to determine proper EmbeddedSignatureData index for these
  - And tests to ensure this remains functional
- Marshaller changes to recognize copy constructor cases
  - Note that there isn't actual marshaller support. Instead the compiler can now able to detect when copy ctors should be used, and falls back to the runtime interop layer

src/coreclr/src/tools/Common/TypeSystem/Common/MethodDesc.cs
src/coreclr/src/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs
src/coreclr/src/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs
src/coreclr/src/tools/Common/TypeSystem/Interop/IL/Marshaller.cs
src/coreclr/src/tools/aot/ILCompiler.ReadyToRun/Interop/IL/Marshaller.ReadyToRun.cs
src/coreclr/src/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILTestAssembly/Signature.il
src/coreclr/src/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/SignatureTests.cs
src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.cs [new file with mode: 0644]
src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.csproj [new file with mode: 0644]
src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.il [new file with mode: 0644]
src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.ilproj [new file with mode: 0644]

index 0545857397ba1ec9b152d118303a5669b54328f9..1da52dccd3bd0a0d6f7b0d3cedb90c4df8907087 100644 (file)
@@ -51,6 +51,14 @@ namespace Internal.TypeSystem
         // Value of <see cref="EmbeddedSignatureData.index" /> for any custom modifiers on the return type
         public const string IndexOfCustomModifiersOnReturnType = "0.1.1.1";
 
+        // Value of <see cref="EmbeddedSignatureData.index" /> for any custom modifiers on
+        // SomeStruct when SomeStruct *, or SomeStruct & is the type of a parameter or return type
+        // Parameter index 0 represents the return type, and indices 1-n represent the parameters to the signature
+        public static string GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(int parameterIndex)
+        {
+            return $"0.1.1.2.{(parameterIndex + 1).ToStringInvariant()}.1";
+        }
+
         public MethodSignature(MethodSignatureFlags flags, int genericParameterCount, TypeDesc returnType, TypeDesc[] parameters, EmbeddedSignatureData[] embeddedSignatureData = null)
         {
             _flags = flags;
index 956e4fc8e274dfbe16c56730c1003d7cc56590d6..7b05a9b4ed90bb8169e6bdc20ece3d774d9ade02 100644 (file)
@@ -166,13 +166,44 @@ namespace Internal.TypeSystem.Interop
             }
         }
 
+        private static bool HasCopyConstructorCustomModifier(int? parameterIndex,
+            EmbeddedSignatureData[] customModifierData)
+        {
+            if (!parameterIndex.HasValue || customModifierData == null)
+                return false;
+
+            string customModifierIndex = MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(parameterIndex.Value);
+            foreach (var customModifier in customModifierData)
+            {
+                if (customModifier.kind != EmbeddedSignatureDataKind.RequiredCustomModifier)
+                    continue;
+
+                if (customModifier.index != customModifierIndex)
+                    continue;
+
+                var customModifierType = customModifier.type as DefType;
+                if (customModifierType == null)
+                    continue;
+
+                if ((customModifierType.Namespace == "System.Runtime.CompilerServices" && customModifierType.Name == "IsCopyConstructed") ||
+                    (customModifierType.Namespace == "Microsoft.VisualC" && customModifierType.Name == "NeedsCopyConstructorModifier"))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         internal static MarshallerKind GetMarshallerKind(
-             TypeDesc type,
-             MarshalAsDescriptor marshalAs,
-             bool isReturn,
-             bool isAnsi,
-             MarshallerType marshallerType,
-             out MarshallerKind elementMarshallerKind)
+            TypeDesc type,
+            int? parameterIndex,
+            EmbeddedSignatureData[] customModifierData,
+            MarshalAsDescriptor marshalAs,
+            bool isReturn,
+            bool isAnsi,
+            MarshallerType marshallerType,
+            out MarshallerKind elementMarshallerKind)
         {
             elementMarshallerKind = MarshallerKind.Invalid;
 
@@ -183,6 +214,12 @@ namespace Internal.TypeSystem.Interop
 
                 type = type.GetParameterType();
 
+                if (!type.IsPrimitive && type.IsValueType && marshallerType != MarshallerType.Field
+                    && HasCopyConstructorCustomModifier(parameterIndex, customModifierData))
+                {
+                    return MarshallerKind.BlittableValueClassWithCopyCtor;
+                }
+
                 // Compat note: CLR allows ref returning blittable structs for IJW
                 if (isReturn)
                     return MarshallerKind.Invalid;
@@ -444,7 +481,15 @@ namespace Internal.TypeSystem.Interop
             else if (type.IsPointer)
             {
                 if (nativeType == NativeTypeKind.Default)
+                {
+                    var pointedAtType = type.GetParameterType();
+                    if (!pointedAtType.IsPrimitive && !type.IsEnum && marshallerType != MarshallerType.Field
+                        && HasCopyConstructorCustomModifier(parameterIndex, customModifierData))
+                    {
+                        return MarshallerKind.BlittableValueClassWithCopyCtor;
+                    }
                     return MarshallerKind.BlittableValue;
+                }
                 else
                     return MarshallerKind.Invalid;
             }
index 65f0956943fbbf62d46e4ab1ec3e5c5245e884b8..f713f9cb0c29f6266085755d0d8749258430022d 100644 (file)
@@ -44,6 +44,8 @@ namespace Internal.TypeSystem.Interop
 
                 MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
                     field.FieldType,
+                    parameterIndex : null,
+                    customModifierData: null,
                     field.GetMarshalAsDescriptor(),
                     isReturn: false,
                     isAnsi: mdType.PInvokeStringFormat == PInvokeStringFormat.AnsiClass,
index 31c869463025086bc6ecf177584c8e2890c4b9cc..ecd7b0df46808550a41725ddd481377c25f7955a 100644 (file)
@@ -50,6 +50,7 @@ namespace Internal.TypeSystem.Interop
         AsAnyA,
         AsAnyW,
         ComInterface,
+        BlittableValueClassWithCopyCtor,
         Invalid
     }
     public enum MarshalDirection
@@ -271,6 +272,8 @@ namespace Internal.TypeSystem.Interop
         /// <param name="parameterType">type of the parameter to marshal</param>
         /// <returns>The created Marshaller</returns>
         public static Marshaller CreateMarshaller(TypeDesc parameterType,
+            int? parameterIndex,
+            EmbeddedSignatureData[] customModifierData,
             MarshallerType marshallerType,
             MarshalAsDescriptor marshalAs,
             MarshalDirection direction,
@@ -286,6 +289,8 @@ namespace Internal.TypeSystem.Interop
         {
             MarshallerKind elementMarshallerKind;
             MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(parameterType,
+                                                parameterIndex,
+                                                customModifierData,
                                                 marshalAs,
                                                 isReturn,
                                                 flags.CharSet == CharSet.Ansi,
index 4ca7a6f95965050095d146317194c35fe9bc6b88..04b939334a21e121bb53c4a8664b5c7664dfb87c 100644 (file)
@@ -70,6 +70,8 @@ namespace Internal.TypeSystem.Interop
 
                 TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1];  //first item is the return type
                 marshallers[i] = CreateMarshaller(parameterType,
+                                                    parameterIndex,
+                                                    methodSig.GetEmbeddedSignatureData(),
                                                     MarshallerType.Argument,
                                                     parameterMetadata.MarshalAsDescriptor,
                                                     direction,
@@ -121,6 +123,8 @@ namespace Internal.TypeSystem.Interop
 
                 MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
                     parameterType,
+                    parameterIndex: i,
+                    customModifierData: methodSig.GetEmbeddedSignatureData(),
                     parameterMetadata.MarshalAsDescriptor,
                     parameterMetadata.Return,
                     isAnsi: true,
index b895be9b24f48499c23bfa8591eeb05d98661f52..02f33b81f04731cd2d6e233bacb85bd85fa54501 100644 (file)
   {
      ret
   }
+
+  .method public hidebysig instance int32 modopt([CoreTestAssembly]System.Void) & Method3(int32 modopt(FooModifier)*, int32 modopt(FooModifier)*) cil managed
+  {
+     ret
+  }
 }
 
 .class private auto ansi beforefieldinit Atom
index e6a21525ee52c4fbe7f966853acec2e80f724ce8..60d717291196a0ca57313dbf99b1ce170c5c5fc9 100644 (file)
@@ -69,6 +69,16 @@ namespace TypeSystemTests
             Assert.Equal("OptionalCustomModifier0.1.1.1CharOptionalCustomModifier0.1.1.2.1.1VoidOptionalCustomModifier0.1.2.1FooModifier", GetModOptMethodSignatureInfo(methodWithModOptAtStartOfSigAndAfterByRef));
         }
 
+        [Fact]
+        public void TestSignatureMatchesModoptOnPointerOrRefModifiedType()
+        {
+            MetadataType modOptTester = _testModule.GetType("", "ModOptTester");
+            MethodSignature methodWithModOpt = modOptTester.GetMethods().Single(m => string.Equals(m.Name, "Method3")).Signature;
+            Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(0), methodWithModOpt.GetEmbeddedSignatureData()[0].index);
+            Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(1), methodWithModOpt.GetEmbeddedSignatureData()[1].index);
+            Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(2), methodWithModOpt.GetEmbeddedSignatureData()[2].index);
+        }
+
         [Fact]
         public void TestSignatureMatches()
         {
diff --git a/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.cs b/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.cs
new file mode 100644 (file)
index 0000000..764e790
--- /dev/null
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+static unsafe class CopyCtor
+{
+    public static unsafe int StructWithCtorTest(StructWithCtor* ptrStruct, ref StructWithCtor refStruct)
+    {
+        if (ptrStruct->_instanceField != 1)
+            return 1;
+        if (refStruct._instanceField != 2)
+            return 2;
+
+        if (StructWithCtor.CopyCtorCallCount != 2)
+            return 3;
+        if (StructWithCtor.DtorCallCount != 2)
+            return 4;
+
+
+        return 100;
+    }
+
+    [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
+    public delegate int TestDelegate(StructWithCtor* ptrStruct, ref StructWithCtor refStruct);
+
+    public static unsafe int Main()
+    {
+        TestDelegate del = (TestDelegate)StructWithCtorTest;
+        StructWithCtor s1 = new StructWithCtor();
+        StructWithCtor s2 = new StructWithCtor();
+        s1._instanceField = 1;
+        s2._instanceField = 2;
+        int returnVal = FunctionPointer.Call_FunctionPointer(Marshal.GetFunctionPointerForDelegate(del), &s1, ref s2);
+
+        GC.KeepAlive(del);
+
+        return returnVal;
+    }
+}
diff --git a/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.csproj b/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorTest.csproj
new file mode 100644 (file)
index 0000000..60abf16
--- /dev/null
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="CopyCtorTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="CopyCtorUtil.ilproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.il b/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.il
new file mode 100644 (file)
index 0000000..d77a1ee
--- /dev/null
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern mscorlib { }
+.assembly extern System.Runtime.CompilerServices.VisualC { }
+.assembly CopyCtorUtil { }
+
+.class public sealed sequential ansi beforefieldinit StructWithCtor
+       extends [mscorlib]System.ValueType
+{
+    .field public int32 _instanceField
+    .field public static int32 CopyCtorCallCount
+    .field public static int32 DtorCallCount
+
+    .method public specialname static void  '<MarshalCopy>'(valuetype StructWithCtor* A_0,
+                                                            valuetype StructWithCtor* A_1) cil managed
+    {
+        ldarg.0
+        ldarg.1
+        ldfld int32 StructWithCtor::_instanceField
+        stfld int32 StructWithCtor::_instanceField
+
+        ldsfld int32 StructWithCtor::CopyCtorCallCount
+        ldc.i4.1
+        add
+        stsfld int32 StructWithCtor::CopyCtorCallCount
+        ret
+    }
+
+    .method public specialname static void  '<MarshalDestroy>'(valuetype StructWithCtor* A_0) cil managed
+    {
+        ldsfld int32 StructWithCtor::DtorCallCount
+        ldc.i4.1
+        add
+        stsfld int32 StructWithCtor::DtorCallCount
+        ret
+    }
+}
+
+.class public auto ansi beforefieldinit FunctionPointer
+       extends [mscorlib]System.Object
+{
+    .method hidebysig specialname rtspecialname 
+            instance void .ctor() cil managed
+    {
+        ldarg.0
+        call instance void [mscorlib]System.Object::.ctor()
+        ret
+    }
+
+    .method public hidebysig static int32  Call_FunctionPointer(native int fptr,
+                                                 valuetype StructWithCtor* arg1,
+                                                 valuetype StructWithCtor& arg2) cil managed
+    {
+        .maxstack  8
+        ldarg.1
+        ldarg.2
+        ldarg.0
+        calli unmanaged stdcall int32 (valuetype StructWithCtor modreq([mscorlib]System.Runtime.CompilerServices.IsCopyConstructed)*, valuetype StructWithCtor modreq([mscorlib]System.Runtime.CompilerServices.IsCopyConstructed)&)
+        ret
+    }
+}
+
+
diff --git a/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.ilproj b/src/tests/Interop/PInvoke/Miscellaneous/CopyCtor/CopyCtorUtil.ilproj
new file mode 100644 (file)
index 0000000..7eb71ce
--- /dev/null
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk.IL">
+  <PropertyGroup>
+    <OutputType>library</OutputType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="CopyCtorUtil.il" />
+  </ItemGroup>
+</Project>