Use `MethodTable.Of` in generated valuetype methods (#83325)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Mon, 13 Mar 2023 21:17:14 +0000 (06:17 +0900)
committerGitHub <noreply@github.com>
Mon, 13 Mar 2023 21:17:14 +0000 (06:17 +0900)
I thought it might improve codegen, but the improvements are marginal. Still, contributes to dotnet/runtimelab#232.

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
src/coreclr/nativeaot/System.Private.CoreLib/src/System/ValueType.cs
src/coreclr/tools/Common/TypeSystem/IL/Stubs/ValueTypeGetFieldHelperMethodOverride.cs

index bb20336..650d992 100644 (file)
@@ -344,7 +344,7 @@ namespace System.Runtime
         //
         [MethodImpl(MethodImplOptions.InternalCall)]
         [RuntimeImport(RuntimeLibrary, "RhBoxAny")]
-        private static extern unsafe object RhBoxAny(ref byte pData, MethodTable* pEEType);
+        internal static extern unsafe object RhBoxAny(ref byte pData, MethodTable* pEEType);
 
         internal static unsafe object RhBoxAny(ref byte pData, EETypePtr pEEType)
             => RhBoxAny(ref pData, pEEType.ToPointer());
@@ -372,7 +372,7 @@ namespace System.Runtime
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [RuntimeImport(RuntimeLibrary, "RhBox")]
-        private static extern unsafe object RhBox(MethodTable* pEEType, ref byte data);
+        internal static extern unsafe object RhBox(MethodTable* pEEType, ref byte data);
 
         internal static unsafe object RhBox(EETypePtr pEEType, ref byte data)
             => RhBox(pEEType.ToPointer(), ref data);
index f51c4b3..a445c6d 100644 (file)
@@ -14,7 +14,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Runtime;
 using System.Runtime.CompilerServices;
 
-using Internal.Runtime.Augments;
+using Internal.Runtime;
 
 using Debug = System.Diagnostics.Debug;
 
@@ -39,15 +39,15 @@ namespace System
         // This API is a bit awkward because we want to avoid burning more than one vtable slot on this.
         // When index == GetNumFields, this method is expected to return the number of fields of this
         // valuetype. Otherwise, it returns the offset and type handle of the index-th field on this type.
-        internal virtual int __GetFieldHelper(int index, out EETypePtr eeType)
+        internal virtual unsafe int __GetFieldHelper(int index, out MethodTable* mt)
         {
             // Value types that don't override this method will use the fast path that looks at bytes, not fields.
             Debug.Assert(index == GetNumFields);
-            eeType = default;
+            mt = default;
             return UseFastHelper;
         }
 
-        public override bool Equals([NotNullWhen(true)] object? obj)
+        public override unsafe bool Equals([NotNullWhen(true)] object? obj)
         {
             if (obj == null || obj.GetEETypePtr() != this.GetEETypePtr())
                 return false;
@@ -71,7 +71,7 @@ namespace System
                 // Foreach field, box and call the Equals method.
                 for (int i = 0; i < numFields; i++)
                 {
-                    int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);
+                    int fieldOffset = __GetFieldHelper(i, out MethodTable* fieldType);
 
                     // Fetch the value of the field on both types
                     object thisField = RuntimeImports.RhBoxAny(ref Unsafe.Add(ref thisRawData, fieldOffset), fieldType);
@@ -102,22 +102,22 @@ namespace System
             return hashCode;
         }
 
-        private int GetHashCodeImpl()
+        private unsafe int GetHashCodeImpl()
         {
             int numFields = __GetFieldHelper(GetNumFields, out _);
 
             if (numFields == UseFastHelper)
-                return FastGetValueTypeHashCodeHelper(this.GetEETypePtr(), ref this.GetRawData());
+                return FastGetValueTypeHashCodeHelper(this.GetMethodTable(), ref this.GetRawData());
 
             return RegularGetValueTypeHashCode(ref this.GetRawData(), numFields);
         }
 
-        private static int FastGetValueTypeHashCodeHelper(EETypePtr type, ref byte data)
+        private static unsafe int FastGetValueTypeHashCodeHelper(MethodTable* type, ref byte data)
         {
             // Sanity check - if there are GC references, we should not be hashing bytes
-            Debug.Assert(!type.HasPointers);
+            Debug.Assert(!type->HasGCPointers);
 
-            int size = (int)type.ValueTypeSize;
+            int size = (int)type->ValueTypeSize;
             int hashCode = 0;
 
             for (int i = 0; i < size / 4; i++)
@@ -128,31 +128,31 @@ namespace System
             return hashCode;
         }
 
-        private int RegularGetValueTypeHashCode(ref byte data, int numFields)
+        private unsafe int RegularGetValueTypeHashCode(ref byte data, int numFields)
         {
             int hashCode = 0;
 
             // We only take the hashcode for the first non-null field. That's what the CLR does.
             for (int i = 0; i < numFields; i++)
             {
-                int fieldOffset = __GetFieldHelper(i, out EETypePtr fieldType);
+                int fieldOffset = __GetFieldHelper(i, out MethodTable* fieldType);
                 ref byte fieldData = ref Unsafe.Add(ref data, fieldOffset);
 
-                Debug.Assert(!fieldType.IsPointer);
+                Debug.Assert(!fieldType->IsPointerType);
 
-                if (fieldType.ElementType == Internal.Runtime.EETypeElementType.Single)
+                if (fieldType->ElementType == EETypeElementType.Single)
                 {
                     hashCode = Unsafe.As<byte, float>(ref fieldData).GetHashCode();
                 }
-                else if (fieldType.ElementType == Internal.Runtime.EETypeElementType.Double)
+                else if (fieldType->ElementType == EETypeElementType.Double)
                 {
                     hashCode = Unsafe.As<byte, double>(ref fieldData).GetHashCode();
                 }
-                else if (fieldType.IsPrimitive)
+                else if (fieldType->IsPrimitive)
                 {
                     hashCode = FastGetValueTypeHashCodeHelper(fieldType, ref fieldData);
                 }
-                else if (fieldType.IsValueType)
+                else if (fieldType->IsValueType)
                 {
                     // We have no option but to box since this value type could have
                     // GC pointers (we could find out if we want though), or fields of type Double/Single (we can't
index 9bb60e3..5320875 100644 (file)
@@ -8,7 +8,7 @@ using Internal.TypeSystem;
 namespace Internal.IL.Stubs
 {
     /// <summary>
-    /// Synthetic method override of "int ValueType.__GetFieldHelper(Int32, out EETypePtr)". This method is injected
+    /// Synthetic method override of "int ValueType.__GetFieldHelper(Int32, out MethodTable*)". This method is injected
     /// into all value types that cannot have their Equals(object) and GetHashCode() methods operate on individual
     /// bytes. The purpose of the override is to provide access to the value types' fields and their types.
     /// </summary>
@@ -46,7 +46,7 @@ namespace Internal.IL.Stubs
                 {
                     TypeSystemContext context = _owningType.Context;
                     TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32);
-                    TypeDesc eeTypePtrType = context.SystemModule.GetKnownType("System", "EETypePtr");
+                    TypeDesc eeTypePtrType = context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable").MakePointerType();
 
                     _signature = new MethodSignature(0, 0, int32Type, new[] {
                         int32Type,
@@ -64,9 +64,8 @@ namespace Internal.IL.Stubs
 
             ILEmitter emitter = new ILEmitter();
 
-            TypeDesc eeTypePtrType = Context.SystemModule.GetKnownType("System", "EETypePtr");
-            MethodDesc eeTypePtrOfMethod = eeTypePtrType.GetKnownMethod("EETypePtrOf", null);
-            ILToken eeTypePtrToken = emitter.NewToken(eeTypePtrType);
+            TypeDesc methodTableType = Context.SystemModule.GetKnownType("Internal.Runtime", "MethodTable");
+            MethodDesc methodTableOfMethod = methodTableType.GetKnownMethod("Of", null);
 
             var switchStream = emitter.NewCodeStream();
             var getFieldStream = emitter.NewCodeStream();
@@ -98,10 +97,10 @@ namespace Internal.IL.Stubs
                 // Don't unnecessarily create an MethodTable for the enum.
                 boxableFieldType = boxableFieldType.UnderlyingType;
 
-                MethodDesc ptrOfField = eeTypePtrOfMethod.MakeInstantiatedMethod(boxableFieldType);
-                getFieldStream.Emit(ILOpcode.call, emitter.NewToken(ptrOfField));
+                MethodDesc mtOfFieldMethod = methodTableOfMethod.MakeInstantiatedMethod(boxableFieldType);
+                getFieldStream.Emit(ILOpcode.call, emitter.NewToken(mtOfFieldMethod));
 
-                getFieldStream.Emit(ILOpcode.stobj, eeTypePtrToken);
+                getFieldStream.Emit(ILOpcode.stind_i);
 
                 getFieldStream.EmitLdArg(0);
                 getFieldStream.Emit(ILOpcode.ldflda, emitter.NewToken(field));