From 95d94250b3fe5f24792ccb503aa56f19fa9478fb Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Wed, 20 Jan 2021 20:13:59 +0200 Subject: [PATCH] Classify type with empty base as non-blittable (#46769) * Add tests for MarshalUtils.IsBillitableType * Classify type with empty base as non-blittable * Add tests for IsZeroSizedReferenceType * Rename local variables in tests --- .../TypeSystem/Common/DefType.FieldLayout.cs | 21 ++ .../TypeSystem/Interop/IL/MarshalUtils.cs | 5 +- .../CoreTestAssembly/Marshalling.cs | 183 ++++++++++++++++++ .../DefType.FieldLayoutTests.cs | 54 ++++++ ...ompiler.TypeSystem.ReadyToRun.Tests.csproj | 3 + .../MarshalUtilsTests.cs | 83 ++++++++ 6 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CoreTestAssembly/Marshalling.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/DefType.FieldLayoutTests.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs diff --git a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs index 6c6c7ddecd9e6d..a92cca26699994 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs @@ -161,6 +161,27 @@ public LayoutInt InstanceByteAlignment } } + public bool IsZeroSizedReferenceType + { + get + { + if (Category != TypeFlags.Class) + { + throw new InvalidOperationException("Only reference types are allowed."); + } + + if (!_fieldLayoutFlags.HasFlags(FieldLayoutFlags.ComputedInstanceTypeLayout)) + { + ComputeInstanceLayout(InstanceLayoutKind.TypeOnly); + } + + // test that size without padding is zero: + // _instanceByteCountUnaligned - _instanceByteAlignment == LayoutInt.Zero + // simplified to: + return _instanceByteCountUnaligned == _instanceByteAlignment; + } + } + /// /// The type has stable Abi layout /// diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs index f713f9cb0c29f6..84dcc80be1323e 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalUtils.cs @@ -18,12 +18,13 @@ public static bool IsBlittableType(TypeDesc type) return false; } - TypeDesc baseType = type.BaseType; + DefType baseType = type.BaseType; bool hasNonTrivialParent = baseType != null && !baseType.IsWellKnownType(WellKnownType.Object) && !baseType.IsWellKnownType(WellKnownType.ValueType); - if (hasNonTrivialParent && !IsBlittableType(baseType)) + // Type is blittable only if parent is also blittable and is not empty. + if (hasNonTrivialParent && (!IsBlittableType(baseType) || baseType.IsZeroSizedReferenceType)) { return false; } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CoreTestAssembly/Marshalling.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CoreTestAssembly/Marshalling.cs new file mode 100644 index 00000000000000..4ddf5702b2b46c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CoreTestAssembly/Marshalling.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace Marshalling +{ + + #region Simple classes + + public class SimpleEmptyClass + { + } + + public class SimpleByteClass + { + public byte x; + } + + public class SimpleInt16Class + { + public short x; + } + + public class SimpleInt32Class + { + public int x; + } + + public class SimpleInt64Class + { + public long x; + } + + #endregion + + #region LayoutKind.Explicit classes + + [StructLayout(LayoutKind.Explicit)] + public class ExplicitEmptyBase + { + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitEmptyBase : ExplicitEmptyBase + { + [FieldOffset(0)] + public int i; + } + + [StructLayout(LayoutKind.Explicit, Size = 0)] + public class ExplicitEmptySizeZeroBase + { + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitEmptySizeZeroBase : ExplicitEmptySizeZeroBase + { + [FieldOffset(0)] + public int i; + } + + [StructLayout(LayoutKind.Explicit)] + public class ExplicitByteBase + { + [FieldOffset(0)] + public byte x; + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitByteBase : ExplicitByteBase + { + [FieldOffset(1)] + public int i; + } + + [StructLayout(LayoutKind.Explicit)] + public class ExplicitInt16Base + { + [FieldOffset(0)] + public short x; + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitInt16Base : ExplicitInt16Base + { + [FieldOffset(1)] + public int i; + } + + [StructLayout(LayoutKind.Explicit)] + public class ExplicitInt32Base + { + [FieldOffset(0)] + public int x; + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitInt32Base : ExplicitInt32Base + { + [FieldOffset(1)] + public int i; + } + + [StructLayout(LayoutKind.Explicit)] + public class ExplicitInt64Base + { + [FieldOffset(0)] + public long x; + } + + [StructLayout(LayoutKind.Explicit)] + public class ClassWithExplicitInt64Base : ExplicitInt64Base + { + [FieldOffset(1)] + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + public class SequentialEmptyBase + { + } + + #endregion + + #region LayoutKind.Sequential classes + + [StructLayout(LayoutKind.Sequential)] + public class ClassWithSequentialEmptyBase : SequentialEmptyBase + { + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + public class SequentialByteBase + { + public byte x; + } + + [StructLayout(LayoutKind.Sequential)] + public class ClassWithSequentialByteBase : SequentialByteBase + { + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + public class SequentialInt16Base + { + public short x; + } + + [StructLayout(LayoutKind.Sequential)] + public class ClassWithSequentialInt16Base : SequentialInt16Base + { + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + public class SequentialInt32Base + { + public int x; + } + + [StructLayout(LayoutKind.Sequential)] + public class ClassWithSequentialInt32Base : SequentialInt32Base + { + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + public class SequentialInt64Base + { + public long x; + } + + [StructLayout(LayoutKind.Sequential)] + public class ClassWithSequentialInt64Base : SequentialInt64Base + { + public int i; + } + + #endregion +} diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/DefType.FieldLayoutTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/DefType.FieldLayoutTests.cs new file mode 100644 index 00000000000000..c82f6c21a85be8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/DefType.FieldLayoutTests.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +using Xunit; + +namespace TypeSystemTests +{ + public partial class DefTypeTests + { + private ModuleDesc _testModule; + + public DefTypeTests() + { + var context = new TestTypeSystemContext(TargetArchitecture.X64); + var systemModule = context.CreateModuleForSimpleName("CoreTestAssembly"); + context.SetSystemModule(systemModule); + + _testModule = systemModule; + } + + [Theory] + [InlineData("SimpleByteClass")] + [InlineData("SimpleInt16Class")] + [InlineData("SimpleInt32Class")] + [InlineData("SimpleInt64Class")] + [InlineData("ExplicitByteBase")] + [InlineData("ExplicitInt16Base")] + [InlineData("ExplicitInt32Base")] + [InlineData("ExplicitInt64Base")] + [InlineData("SequentialByteBase")] + [InlineData("SequentialInt16Base")] + [InlineData("SequentialInt32Base")] + [InlineData("SequentialInt64Base")] + public void IsZeroSizedReferenceType_NonEmptyType_ReturnsFalse(string className) + { + DefType nonEmptyClass = _testModule.GetType("Marshalling", className); + Assert.False(nonEmptyClass.IsZeroSizedReferenceType); + } + + [Theory] + [InlineData("SimpleEmptyClass")] + [InlineData("ExplicitEmptyBase")] + [InlineData("ExplicitEmptySizeZeroBase")] + [InlineData("SequentialEmptyBase")] + public void IsZeroSizedReferenceType_EmptyType_ReturnsTrue(string className) + { + DefType emptyClass = _testModule.GetType("Marshalling", className); + Assert.True(emptyClass.IsZeroSizedReferenceType); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj index 21f6f8a1370edd..9b5fec9b380651 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/ILCompiler.TypeSystem.ReadyToRun.Tests.csproj @@ -21,6 +21,7 @@ + @@ -41,6 +42,7 @@ + @@ -58,5 +60,6 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs new file mode 100644 index 00000000000000..c3befa87af72d4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/MarshalUtilsTests.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +using Xunit; + +namespace TypeSystemTests +{ + public class MarshalUtilsTests + { + private TestTypeSystemContext _context; + private ModuleDesc _testModule; + + public MarshalUtilsTests() + { + _context = new TestTypeSystemContext(TargetArchitecture.X64); + var systemModule = _context.CreateModuleForSimpleName("CoreTestAssembly"); + _context.SetSystemModule(systemModule); + + _testModule = systemModule; + } + + [Theory] + [InlineData(WellKnownType.Void)] + [InlineData(WellKnownType.Boolean)] + [InlineData(WellKnownType.Char)] + [InlineData(WellKnownType.SByte)] + [InlineData(WellKnownType.Byte)] + [InlineData(WellKnownType.Int16)] + [InlineData(WellKnownType.UInt16)] + [InlineData(WellKnownType.Int32)] + [InlineData(WellKnownType.UInt32)] + [InlineData(WellKnownType.Int64)] + [InlineData(WellKnownType.UInt64)] + [InlineData(WellKnownType.IntPtr)] + [InlineData(WellKnownType.UIntPtr)] + [InlineData(WellKnownType.Single)] + [InlineData(WellKnownType.Double)] + [InlineData(WellKnownType.RuntimeFieldHandle)] + [InlineData(WellKnownType.RuntimeTypeHandle)] + [InlineData(WellKnownType.RuntimeMethodHandle)] + public void IsBlittableType_BilittableWellKnownTypes_ReturnsTrue(WellKnownType type) => + Assert.True(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type))); + + [Theory] + [InlineData(WellKnownType.String)] + [InlineData(WellKnownType.ValueType)] + [InlineData(WellKnownType.Enum)] + [InlineData(WellKnownType.Array)] + [InlineData(WellKnownType.MulticastDelegate)] + [InlineData(WellKnownType.Exception)] + [InlineData(WellKnownType.Object)] + public void IsBlittableType_NonBilittableWellKnownTypes_ReturnsFalse(WellKnownType type) => + Assert.False(MarshalUtils.IsBlittableType(_context.GetWellKnownType(type))); + + [Theory] + [InlineData("ClassWithExplicitByteBase")] + [InlineData("ClassWithExplicitInt16Base")] + [InlineData("ClassWithExplicitInt32Base")] + [InlineData("ClassWithExplicitInt64Base")] + [InlineData("ClassWithSequentialByteBase")] + [InlineData("ClassWithSequentialInt16Base")] + [InlineData("ClassWithSequentialInt32Base")] + [InlineData("ClassWithSequentialInt64Base")] + public void IsBlittableType_TypeWithBlittableBase_ReturnsTrue(string className) + { + TypeDesc classWithBlittableBase = _testModule.GetType("Marshalling", className); + Assert.True(MarshalUtils.IsBlittableType(classWithBlittableBase)); + } + + [Theory] + [InlineData("ClassWithExplicitEmptyBase")] + [InlineData("ClassWithExplicitEmptySizeZeroBase")] + [InlineData("ClassWithSequentialEmptyBase")] + public void IsBlittableType_TypeWithEmptyBase_ReturnsFalse(string className) + { + TypeDesc classWithEmptyBase = _testModule.GetType("Marshalling", className); + Assert.False(MarshalUtils.IsBlittableType(classWithEmptyBase)); + } + } +}