Skip to content

Commit

Permalink
Speed up interface checking and casting (#49257)
Browse files Browse the repository at this point in the history
* Reduce branches in IsInstanceOfInterface/ChkCastInterface

* Drop extra var, lea; additional check for small counts

* Feedback

* Undo IsInstanceOfClass change

* Tidy usings
  • Loading branch information
benaadams committed Mar 7, 2021
1 parent 49eed0e commit bea0abb
Showing 1 changed file with 84 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using Internal.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

using Internal.Runtime.CompilerServices;

namespace System.Runtime.CompilerServices
{
Expand Down Expand Up @@ -207,34 +206,57 @@ private static CastResult TryGet(nuint source, nuint target)
[DebuggerStepThrough]
private static object? IsInstanceOfInterface(void* toTypeHnd, object? obj)
{
const int unrollSize = 4;

if (obj != null)
{
MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
nuint interfaceCount = mt->InterfaceCount;
nint interfaceCount = mt->InterfaceCount;
if (interfaceCount != 0)
{
MethodTable** interfaceMap = mt->InterfaceMap;
for (nuint i = 0; ; i += 4)
if (interfaceCount < unrollSize)
{
if (interfaceMap[i + 0] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
break;
if (interfaceMap[i + 1] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
break;
if (interfaceMap[i + 2] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
break;
if (interfaceMap[i + 3] == toTypeHnd)
// If not enough for unrolled, jmp straight to small loop
// as we already know there is one or more interfaces so don't need to check again.
goto few;
}

do
{
if (interfaceMap[0] == toTypeHnd ||
interfaceMap[1] == toTypeHnd ||
interfaceMap[2] == toTypeHnd ||
interfaceMap[3] == toTypeHnd)
{
goto done;
if (--interfaceCount == 0)
break;
}

interfaceMap += unrollSize;
interfaceCount -= unrollSize;
} while (interfaceCount >= unrollSize);

if (interfaceCount == 0)
{
// If none remaining, skip the short loop
goto extra;
}

few:
do
{
if (interfaceMap[0] == toTypeHnd)
{
goto done;
}

// Assign next offset
interfaceMap++;
interfaceCount--;
} while (interfaceCount > 0);
}

extra:
if (mt->NonTrivialInterfaceCast)
{
goto slowPath;
Expand Down Expand Up @@ -374,35 +396,60 @@ private static CastResult TryGet(nuint source, nuint target)
[DebuggerStepThrough]
private static object? ChkCastInterface(void* toTypeHnd, object? obj)
{
const int unrollSize = 4;

if (obj != null)
{
MethodTable* mt = RuntimeHelpers.GetMethodTable(obj);
nuint interfaceCount = mt->InterfaceCount;
nint interfaceCount = mt->InterfaceCount;
if (interfaceCount == 0)
{
goto slowPath;
}

MethodTable** interfaceMap = mt->InterfaceMap;
for (nuint i = 0; ; i += 4)
if (interfaceCount < unrollSize)
{
if (interfaceMap[i + 0] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
goto slowPath;
if (interfaceMap[i + 1] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
goto slowPath;
if (interfaceMap[i + 2] == toTypeHnd)
goto done;
if (--interfaceCount == 0)
goto slowPath;
if (interfaceMap[i + 3] == toTypeHnd)
// If not enough for unrolled, jmp straight to small loop
// as we already know there is one or more interfaces so don't need to check again.
goto few;
}

do
{
if (interfaceMap[0] == toTypeHnd ||
interfaceMap[1] == toTypeHnd ||
interfaceMap[2] == toTypeHnd ||
interfaceMap[3] == toTypeHnd)
{
goto done;
if (--interfaceCount == 0)
goto slowPath;
}

// Assign next offset
interfaceMap += unrollSize;
interfaceCount -= unrollSize;
} while (interfaceCount >= unrollSize);

if (interfaceCount == 0)
{
// If none remaining, skip the short loop
goto slowPath;
}

few:
do
{
if (interfaceMap[0] == toTypeHnd)
{
goto done;
}

// Assign next offset
interfaceMap++;
interfaceCount--;
} while (interfaceCount > 0);

goto slowPath;
}

done:
Expand Down

0 comments on commit bea0abb

Please sign in to comment.