Skip to content

Commit

Permalink
Improve performance of Memory<T>.Span property getter (dotnet/coreclr…
Browse files Browse the repository at this point in the history
…#20386)

- We can use our knowledge of object representation in the runtime to speed up type checks.
- We leave the ref T and the length deconstructed until the very end, optimizing register usage.
- The Length property getter is once again just a simple field accessor with no bitwise logic.

Commit migrated from dotnet/coreclr@ef93a72
  • Loading branch information
GrabYourPitchforks committed Nov 6, 2018
1 parent 33b8804 commit 47aa264
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 178 deletions.
Expand Up @@ -13,6 +13,7 @@
namespace System.Runtime.CompilerServices
{
using System;
using System.Diagnostics;
using System.Security;
using System.Runtime;
using System.Runtime.CompilerServices;
Expand All @@ -21,6 +22,7 @@ namespace System.Runtime.CompilerServices
using System.Runtime.Serialization;
using System.Threading;
using System.Runtime.Versioning;
using Internal.Runtime.CompilerServices;

public static class RuntimeHelpers
{
Expand Down Expand Up @@ -194,6 +196,45 @@ public static bool IsReferenceOrContainsReferences<T>()
// See getILIntrinsicImplementation for how this happens.
throw new InvalidOperationException();
}

// Returns true iff the object has a component size;
// i.e., is variable length like System.String or Array.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe bool ObjectHasComponentSize(object obj)
{
// CLR objects are laid out in memory as follows.
// [ pMethodTable || .. object data .. ]
// ^-- the object reference points here
//
// The first DWORD of the method table class will have its high bit set if the
// method table has component size info stored somewhere. See member
// MethodTable:IsStringOrArray in src\vm\methodtable.h for full details.
//
// So in effect this method is the equivalent of
// return ((MethodTable*)(*obj))->IsStringOrArray();

Debug.Assert(obj != null);
return *(int*)GetObjectMethodTablePointer(obj) < 0;
}

// Given an object reference, returns its MethodTable* as an IntPtr.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IntPtr GetObjectMethodTablePointer(object obj)
{
Debug.Assert(obj != null);

// We know that the first data field in any managed object is immediately after the
// method table pointer, so just back up one pointer and immediately deref.
// This is not ideal in terms of minimizing instruction count but is the best we can do at the moment.

return Unsafe.Add(ref Unsafe.As<byte, IntPtr>(ref JitHelpers.GetPinningHelper(obj).m_data), -1);

// The JIT currently implements this as:
// lea tmp, [rax + 8h] ; assume rax contains the object reference, tmp is type IntPtr&
// mov tmp, qword ptr [tmp - 8h] ; tmp now contains the MethodTable* pointer
//
// Ideally this would just be a single dereference:
// mov tmp, qword ptr [rax] ; rax = obj ref, tmp = MethodTable* pointer
}
}
}

8 changes: 8 additions & 0 deletions src/coreclr/tests/CoreFX/CoreFX.issues.json
Expand Up @@ -486,6 +486,14 @@
{
"name": "System.Buffers.Text.Tests.FormatterTests.TestFormatterDecimal",
"reason": "https://github.com/dotnet/coreclr/pull/19775"
},
{
"name": "System.SpanTests.MemoryMarshalTests.CreateFromPinnedArrayIntSliceRemainsPinned",
"reason": "https://github.com/dotnet/corefx/pull/32994"
},
{
"name": "System.SpanTests.MemoryMarshalTests.CreateFromPinnedArrayIntReadOnlyMemorySliceRemainsPinned",
"reason": "https://github.com/dotnet/corefx/pull/32994"
}
]
}
Expand Down

0 comments on commit 47aa264

Please sign in to comment.