Skip to content

Commit

Permalink
Remove some RuntimeExport/RuntimeImport indirections (dotnet#110437)
Browse files Browse the repository at this point in the history
We had indirections set up where we'd `RuntimeExport` a method under a symbolic name and then `RuntimeImport` it elsewhere (or call the export from JIT-generated code).

Most of this was motivated by the old Redhawk layering where things like casting and interface dispatch were part of Runtime.Base and not part of CoreLib. Since we decided if we ever reintroduce a Runtime.Base, we wouldn't have casting in it, this indirection will no longer be needed.

Motivated by dotnet#110267 (and also the reason why I'm only doing it for `RuntimeExport`s used from the JIT right now). Once that PR gets in, calling methods through `RuntimeExport`s would actually become a bigger deoptimization than it is now.
  • Loading branch information
MichalStrehovsky authored and hez2010 committed Dec 14, 2024
1 parent 8e63d8d commit f7fad34
Show file tree
Hide file tree
Showing 20 changed files with 130 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ namespace System.Runtime.CompilerServices
/// </summary>
public static unsafe class Unsafe
{
/// <summary>
/// Determines the byte offset from origin to target from the given references.
/// </summary>
[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref readonly T origin, ref readonly T target)
{
throw new PlatformNotSupportedException();

// ldarg .1
// ldarg .0
// sub
// ret
}

/// <summary>
/// Returns a pointer to the given by-ref parameter.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public static unsafe object RhNewArray(MethodTable* pEEType, int length)
}
}

[RuntimeExport("RhBox")]
public static unsafe object RhBox(MethodTable* pEEType, ref byte data)
{
// A null can be passed for boxing of a null ref.
Expand Down Expand Up @@ -207,7 +206,6 @@ public static unsafe void RhUnboxAny(object? o, ref byte data, MethodTable* pUnb
//
// Unbox helpers with RyuJIT conventions
//
[RuntimeExport("RhUnbox2")]
public static unsafe ref byte RhUnbox2(MethodTable* pUnboxToEEType, object obj)
{
if ((obj == null) || !UnboxAnyTypeCompare(obj.GetMethodTable(), pUnboxToEEType))
Expand All @@ -218,7 +216,6 @@ public static unsafe ref byte RhUnbox2(MethodTable* pUnboxToEEType, object obj)
return ref obj.GetRawData();
}

[RuntimeExport("RhUnboxNullable")]
public static unsafe void RhUnboxNullable(ref byte data, MethodTable* pUnboxToEEType, object obj)
{
if (obj != null && obj.GetMethodTable() != pUnboxToEEType->NullableType)
Expand All @@ -228,7 +225,6 @@ public static unsafe void RhUnboxNullable(ref byte data, MethodTable* pUnboxToEE
RhUnbox(obj, ref data, pUnboxToEEType);
}

[RuntimeExport("RhUnboxTypeTest")]
public static unsafe void RhUnboxTypeTest(MethodTable* pType, MethodTable* pBoxType)
{
Debug.Assert(pType->IsValueType);
Expand Down
11 changes: 0 additions & 11 deletions src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/TypeCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ internal enum AssignmentVariation
// IsInstanceOf test used for unusual cases (naked type parameters, variant generic types)
// Unlike the IsInstanceOfInterface and IsInstanceOfClass functions,
// this test must deal with all kinds of type tests
[RuntimeExport("RhTypeCast_IsInstanceOfAny")]
public static unsafe object? IsInstanceOfAny(MethodTable* pTargetType, object? obj)
{
if (obj != null)
Expand Down Expand Up @@ -94,7 +93,6 @@ internal enum AssignmentVariation
return IsInstanceOfAny_NoCacheLookup(pTargetType, obj);
}

[RuntimeExport("RhTypeCast_IsInstanceOfInterface")]
public static unsafe object? IsInstanceOfInterface(MethodTable* pTargetType, object? obj)
{
Debug.Assert(pTargetType->IsInterface);
Expand Down Expand Up @@ -184,7 +182,6 @@ internal enum AssignmentVariation
return obj;
}

[RuntimeExport("RhTypeCast_IsInstanceOfClass")]
public static unsafe object? IsInstanceOfClass(MethodTable* pTargetType, object? obj)
{
Debug.Assert(!pTargetType->IsParameterizedType, "IsInstanceOfClass called with parameterized MethodTable");
Expand Down Expand Up @@ -248,7 +245,6 @@ internal enum AssignmentVariation
return obj;
}

[RuntimeExport("RhTypeCast_IsInstanceOfException")]
public static unsafe bool IsInstanceOfException(MethodTable* pTargetType, object? obj)
{
// Based on IsInstanceOfClass
Expand Down Expand Up @@ -279,7 +275,6 @@ public static unsafe bool IsInstanceOfException(MethodTable* pTargetType, object
// ChkCast test used for unusual cases (naked type parameters, variant generic types)
// Unlike the ChkCastInterface and ChkCastClass functions,
// this test must deal with all kinds of type tests
[RuntimeExport("RhTypeCast_CheckCastAny")]
public static unsafe object CheckCastAny(MethodTable* pTargetType, object obj)
{
CastResult result;
Expand Down Expand Up @@ -307,7 +302,6 @@ public static unsafe object CheckCastAny(MethodTable* pTargetType, object obj)
return objRet;
}

[RuntimeExport("RhTypeCast_CheckCastInterface")]
public static unsafe object CheckCastInterface(MethodTable* pTargetType, object obj)
{
Debug.Assert(pTargetType->IsInterface);
Expand Down Expand Up @@ -393,7 +387,6 @@ private static unsafe object CheckCastInterface_Helper(MethodTable* pTargetType,
return ThrowInvalidCastException(pTargetType);
}

[RuntimeExport("RhTypeCast_CheckCastClass")]
public static unsafe object CheckCastClass(MethodTable* pTargetType, object obj)
{
Debug.Assert(!pTargetType->IsParameterizedType, "CheckCastClass called with parameterized MethodTable");
Expand All @@ -411,7 +404,6 @@ public static unsafe object CheckCastClass(MethodTable* pTargetType, object obj)

// Optimized helper for classes. Assumes that the trivial cases
// has been taken care of by the inlined check
[RuntimeExport("RhTypeCast_CheckCastClassSpecial")]
private static unsafe object CheckCastClassSpecial(MethodTable* pTargetType, object obj)
{
Debug.Assert(!pTargetType->IsParameterizedType, "CheckCastClassSpecial called with parameterized MethodTable");
Expand Down Expand Up @@ -761,8 +753,6 @@ private static unsafe void ThrowArrayMismatchException(object?[] array)
//
// Array stelem/ldelema helpers with RyuJIT conventions
//

[RuntimeExport("RhpLdelemaRef")]
public static unsafe ref object? LdelemaRef(object?[] array, nint index, MethodTable* elementType)
{
Debug.Assert(array is null || array.GetMethodTable()->IsArray, "first argument must be an array");
Expand Down Expand Up @@ -794,7 +784,6 @@ private static unsafe void ThrowArrayMismatchException(object?[] array)
return ref element;
}

[RuntimeExport("RhpStelemRef")]
public static unsafe void StelemRef(object?[] array, nint index, object? obj)
{
// This is supported only on arrays
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public static unsafe void StoreValueTypeField(IntPtr address, object fieldValue,

public static unsafe object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType)
{
return RuntimeImports.RhBox(fieldType.ToMethodTable(), ref *(byte*)address);
return RuntimeExports.RhBox(fieldType.ToMethodTable(), ref *(byte*)address);
}

public static unsafe object LoadPointerTypeField(IntPtr address, RuntimeTypeHandle fieldType)
Expand All @@ -236,15 +236,15 @@ public static unsafe void StoreValueTypeField(object obj, int fieldOffset, objec
public static unsafe object LoadValueTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType)
{
ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize));
return RuntimeImports.RhBox(fieldType.ToMethodTable(), ref address);
return RuntimeExports.RhBox(fieldType.ToMethodTable(), ref address);
}

public static unsafe object LoadPointerTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType)
{
ref byte address = ref Unsafe.AddByteOffset(ref obj.GetRawData(), new IntPtr(fieldOffset - ObjectHeaderSize));

if (fieldType.ToMethodTable()->IsFunctionPointer)
return RuntimeImports.RhBox(MethodTable.Of<IntPtr>(), ref address);
return RuntimeExports.RhBox(MethodTable.Of<IntPtr>(), ref address);

return ReflectionPointer.Box((void*)Unsafe.As<byte, IntPtr>(ref address), Type.GetTypeFromHandle(fieldType));
}
Expand Down Expand Up @@ -285,7 +285,7 @@ public static object LoadValueTypeFieldValueFromValueType(TypedReference typedRe
Debug.Assert(TypedReference.TargetTypeToken(typedReference).ToMethodTable()->IsValueType);
Debug.Assert(fieldTypeHandle.ToMethodTable()->IsValueType);

return RuntimeImports.RhBox(fieldTypeHandle.ToMethodTable(), ref Unsafe.Add<byte>(ref typedReference.Value, fieldOffset));
return RuntimeExports.RhBox(fieldTypeHandle.ToMethodTable(), ref Unsafe.Add<byte>(ref typedReference.Value, fieldOffset));
}

[CLSCompliant(false)]
Expand Down Expand Up @@ -391,7 +391,7 @@ public static bool IsInterface(RuntimeTypeHandle type)

public static unsafe object Box(RuntimeTypeHandle type, IntPtr address)
{
return RuntimeImports.RhBox(type.ToMethodTable(), ref *(byte*)address);
return RuntimeExports.RhBox(type.ToMethodTable(), ref *(byte*)address);
}

//==============================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex
for (int i = 0; i < length; i++)
{
object? value = Unsafe.Add(ref refSourceArray, sourceIndex - i);
if (mustCastCheckEachElement && value != null && RuntimeImports.IsInstanceOf(destinationElementEEType, value) == null)
if (mustCastCheckEachElement && value != null && TypeCast.IsInstanceOfAny(destinationElementEEType, value) == null)
throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement);
Unsafe.Add(ref refDestinationArray, destinationIndex - i) = value;
}
Expand All @@ -340,7 +340,7 @@ private static unsafe void CopyImplGcRefArray(Array sourceArray, int sourceIndex
for (int i = 0; i < length; i++)
{
object? value = Unsafe.Add(ref refSourceArray, sourceIndex + i);
if (mustCastCheckEachElement && value != null && RuntimeImports.IsInstanceOf(destinationElementEEType, value) == null)
if (mustCastCheckEachElement && value != null && TypeCast.IsInstanceOfAny(destinationElementEEType, value) == null)
throw new InvalidCastException(SR.InvalidCast_DownCastArrayElement);
Unsafe.Add(ref refDestinationArray, destinationIndex + i) = value;
}
Expand Down Expand Up @@ -370,7 +370,7 @@ private static unsafe void CopyImplValueTypeArrayToReferenceArray(Array sourceAr
ref object refDestinationArray = ref Unsafe.As<byte, object>(ref MemoryMarshal.GetArrayDataReference(destinationArray));
for (int i = 0; i < length; i++)
{
object boxedValue = RuntimeImports.RhBox(sourceElementEEType, ref *pElement);
object boxedValue = RuntimeExports.RhBox(sourceElementEEType, ref *pElement);
Unsafe.Add(ref refDestinationArray, destinationIndex + i) = boxedValue;
pElement += sourceElementSize;
}
Expand Down Expand Up @@ -457,7 +457,7 @@ private static unsafe void CopyImplValueTypeArrayWithInnerGcRefs(Array sourceArr
pDestinationElement -= cbElementSize;
}

object boxedValue = RuntimeImports.RhBox(sourceElementEEType, ref *pSourceElement);
object boxedValue = RuntimeExports.RhBox(sourceElementEEType, ref *pSourceElement);
if (boxedElements != null)
boxedElements[i] = boxedValue;
else
Expand Down Expand Up @@ -768,7 +768,7 @@ internal unsafe nint GetFlattenedIndex(ReadOnlySpan<int> indices)
MethodTable* pElementEEType = ElementMethodTable;
if (pElementEEType->IsValueType)
{
return RuntimeImports.RhBox(pElementEEType, ref element);
return RuntimeExports.RhBox(pElementEEType, ref element);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private static Exception ConvertOrWidenPrimitivesEnumsAndPointersIfPossible(obje
if (dstElementType == srcElementType)
{
// Rebox the value if the EETypeElementTypes match
dstObject = RuntimeImports.RhBox(dstEEType, ref srcObject.GetRawData());
dstObject = RuntimeExports.RhBox(dstEEType, ref srcObject.GetRawData());
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ private unsafe void CopyBackToArray(ref object? src, object?[] dest)
}
else
{
obj = RuntimeImports.RhBox(
obj = RuntimeExports.RhBox(
(transform & Transform.FunctionPointer) != 0 ? MethodTable.Of<IntPtr>() : argumentInfo.Type,
ref obj.GetRawData());
}
Expand Down Expand Up @@ -818,7 +818,7 @@ private unsafe void CopyBackToSpan(Span<object?> src, Span<object?> dest)
}
else
{
obj = RuntimeImports.RhBox(
obj = RuntimeExports.RhBox(
(transform & Transform.FunctionPointer) != 0 ? MethodTable.Of<IntPtr>() : argumentInfo.Type,
ref obj.GetRawData());
}
Expand Down Expand Up @@ -849,7 +849,7 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio
else if ((_returnTransform & Transform.FunctionPointer) != 0)
{
Debug.Assert(Type.GetTypeFromMethodTable(_returnType).IsFunctionPointer);
obj = RuntimeImports.RhBox(MethodTable.Of<IntPtr>(), ref byref);
obj = RuntimeExports.RhBox(MethodTable.Of<IntPtr>(), ref byref);
}
else if ((_returnTransform & Transform.Reference) != 0)
{
Expand All @@ -859,7 +859,7 @@ private unsafe object ReturnTransform(ref byte byref, bool wrapInTargetInvocatio
else
{
Debug.Assert((_returnTransform & (Transform.ByRef | Transform.Nullable)) != 0);
obj = RuntimeImports.RhBox(_returnType, ref byref);
obj = RuntimeExports.RhBox(_returnType, ref byref);
}
return obj;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ public static unsafe object GetUninitializedObject(
if (mt->IsByRefLike)
throw new NotSupportedException(SR.NotSupported_ByRefLike);

return RuntimeImports.RhBox(mt, ref target);
return RuntimeExports.RhBox(mt, ref target);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,6 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary)
[RuntimeImport(RuntimeLibrary, "RhTypeCast_CheckArrayStore")]
internal static extern void RhCheckArrayStore(object array, object? obj);

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhTypeCast_IsInstanceOfAny")]
internal static extern unsafe object IsInstanceOf(MethodTable* pTargetType, object obj);

//
// calls to runtime for allocation
// These calls are needed in types which cannot use "new" to allocate and need to do it manually
Expand All @@ -405,10 +401,6 @@ internal static IntPtr RhHandleAllocDependent(object primary, object secondary)
[RuntimeImport(RuntimeLibrary, "RhNewString")]
internal static extern unsafe string RhNewString(MethodTable* pEEType, int length);

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhBox")]
internal static extern unsafe object RhBox(MethodTable* pEEType, ref byte data);

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhUnbox")]
internal static extern unsafe void RhUnbox(object? obj, ref byte data, MethodTable* pUnboxToEEType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ public override bool IsInstanceOfType([NotNullWhen(true)] object? o)
return false;
if (pEEType->IsNullable)
pEEType = pEEType->NullableType;
return RuntimeImports.IsInstanceOf(pEEType, o) != null;
return TypeCast.IsInstanceOfAny(pEEType, o) != null;
}

//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private unsafe void RegularGetValueTypeHashCode(ref HashCode hashCode, ref byte
// of __GetFieldHelper, decodes the unboxing stub pointed to by the slot to the real target
// (we already have that part), and calls the entrypoint that expects a byref `this`, and use the
// data to decide between calling fast or regular hashcode helper.
var fieldValue = (ValueType)RuntimeImports.RhBox(fieldType, ref fieldData);
var fieldValue = (ValueType)RuntimeExports.RhBox(fieldType, ref fieldData);
if (fieldValue != null)
{
hashCode.Add(fieldValue);
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/nativeaot/Test.CoreLib/src/System/Buffer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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;
using System.Runtime.CompilerServices;

namespace System
{
public static partial class Buffer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount) =>
RuntimeImports.RhBulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
}
}
34 changes: 34 additions & 0 deletions src/coreclr/nativeaot/Test.CoreLib/src/System/SpanHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.CompilerServices;

namespace System
{
internal static partial class SpanHelpers
{
[Intrinsic]
public static unsafe void ClearWithoutReferences(ref byte dest, nuint len)
{
Fill(ref dest, 0, len);
}

[Intrinsic]
internal static unsafe void Memmove(ref byte dest, ref byte src, nuint len)
{
if ((nuint)(nint)Unsafe.ByteOffset(ref src, ref dest) >= len)
for (nuint i = 0; i < len; i++)
Unsafe.Add(ref dest, (nint)i) = Unsafe.Add(ref src, (nint)i);
else
for (nuint i = len; i > 0; i--)
Unsafe.Add(ref dest, (nint)(i - 1)) = Unsafe.Add(ref src, (nint)(i - 1));
}


internal static void Fill(ref byte dest, byte value, nuint len)
{
for (nuint i = 0; i < len; i++)
Unsafe.Add(ref dest, (nint)i) = value;
}
}
}
2 changes: 2 additions & 0 deletions src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
<Compile Include="System\Runtime\InitializeFinalizerThread.cs" />
<Compile Include="System\Threading\Interlocked.cs" />
<Compile Include="System\Array.cs" />
<Compile Include="System\Buffer.cs" />
<Compile Include="System\SpanHelpers.cs" />
<Compile Include="System\RuntimeExceptionHelpers.cs" />
<Compile Include="System\Object.cs" />
<Compile Include="System\Type.cs" />
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public static MethodDesc GetHelperEntryPoint(this TypeSystemContext context, str
return helperMethod;
}

public static MethodDesc GetCoreLibEntryPoint(this TypeSystemContext context, string namespaceName, string typeName, string methodName, MethodSignature signature)
{
MetadataType owningType = context.SystemModule.GetKnownType(namespaceName, typeName);
return owningType.GetKnownMethod(methodName, signature);
}

public static MethodDesc GetOptionalHelperEntryPoint(this TypeSystemContext context, string typeName, string methodName)
{
MetadataType helperType = context.GetOptionalHelperType(typeName);
Expand Down
Loading

0 comments on commit f7fad34

Please sign in to comment.