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));
+ }
+ }
+}