Skip to content

Commit

Permalink
Refactor Span<T> to ease implementation of JIT intrinsics (dotnet/cor…
Browse files Browse the repository at this point in the history
…eclr#8497)

- Introduce internal ByReference<T> type for byref fields and change Span to use it
- Generalize handling of byref-like types in the type loader
- Make DangerousGetPinnableReference public while I was on it

Commit migrated from dotnet/coreclr@64c2ad1
  • Loading branch information
jkotas authored Dec 7, 2016
1 parent 09a349a commit 4025e6f
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 96 deletions.
3 changes: 1 addition & 2 deletions src/coreclr/src/inc/dacvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pArrayClass, ::g_pArrayClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pSZArrayHelperClass, ::g_pSZArrayHelperClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pNullableClass, ::g_pNullableClass)
#ifdef FEATURE_SPAN_OF_T
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pSpanClass, ::g_pSpanClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pReadOnlySpanClass, ::g_pReadOnlySpanClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pByReferenceClass, ::g_pByReferenceClass)
#endif
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pExceptionClass, ::g_pExceptionClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pThreadAbortExceptionClass, ::g_pThreadAbortExceptionClass)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/mscorlib/model.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12406,6 +12406,7 @@
<Member Name="#ctor(T[],System.Int32)" />
<Member Name="#ctor(T[],System.Int32,System.Int32)" />
<Member Name="#ctor(System.Void*,System.Int32)" />
<Member Name="DangerousGetPinnableReference" />
<Member Name="op_Implicit(T[])" ReturnType="System.Span&lt;T&gt;" />
<Member Name="op_Implicit(System.ArraySegment&lt;T&gt;)" ReturnType="System.Span&lt;T&gt;" />
<Member Name="get_Length" />
Expand All @@ -12425,6 +12426,7 @@
<Member Name="#ctor(T[],System.Int32)" />
<Member Name="#ctor(T[],System.Int32,System.Int32)" />
<Member Name="#ctor(System.Void*,System.Int32)" />
<Member Name="DangerousGetPinnableReference" />
<Member Name="op_Implicit(System.Span&lt;T&gt;)" ReturnType="System.ReadOnlySpan&lt;T&gt;" />
<Member Name="op_Implicit(T[])" ReturnType="System.ReadOnlySpan&lt;T&gt;" />
<Member Name="op_Implicit(System.ArraySegment&lt;T&gt;)" ReturnType="System.ReadOnlySpan&lt;T&gt;" />
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/mscorlib/mscorlib.shared.sources.props
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@
<SystemSources Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\__ComObject.cs" />
<SystemSources Condition="'$(FeatureCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\Variant.cs" />
<SystemSources Condition="'$(FeatureClassicCominterop)' == 'true'" Include="$(BclSourcesRoot)\System\OleAutBinder.cs" />
<SystemSources Condition="'$(FeatureSpanOfT)' == 'true'" Include="$(BclSourcesRoot)\System\ByReference.cs" />
<SystemSources Condition="'$(FeatureSpanOfT)' == 'true'" Include="$(BclSourcesRoot)\System\Span.cs" />
<SystemSources Condition="'$(FeatureSpanOfT)' == 'true'" Include="$(BclSourcesRoot)\System\ReadOnlySpan.cs" />
</ItemGroup>
Expand Down
31 changes: 31 additions & 0 deletions src/coreclr/src/mscorlib/src/System/ByReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.CompilerServices;

namespace System
{
// ByReference<T> is meant to be used to represent "ref T" fields. It is working
// around lack of first class support for byref fields in C# and IL. The JIT and
// type loader has special handling for it that turns it into a thin wrapper around ref T.
internal struct ByReference<T>
{
private IntPtr _value;

public ByReference(ref T value)
{
// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
unsafe { _value = (IntPtr)Unsafe.AsPointer(ref value); }
}

public ref T Value
{
get
{
// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
unsafe { return ref Unsafe.As<IntPtr, T>(ref *(IntPtr*)_value); }
}
}
}
}
42 changes: 19 additions & 23 deletions src/coreclr/src/mscorlib/src/System/ReadOnlySpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ namespace System
/// characteristics on par with T[]. Unlike arrays, it can point to either managed
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
public unsafe struct ReadOnlySpan<T>
public struct ReadOnlySpan<T>
{
/// <summary>A byref or a native ptr. Do not access directly</summary>
private readonly IntPtr _rawPointer;
/// <summary>A byref or a native ptr.</summary>
private readonly ByReference<T> _pointer;
/// <summary>The number of elements this ReadOnlySpan contains.</summary>
private readonly int _length;

Expand All @@ -31,8 +31,7 @@ public ReadOnlySpan(T[] array)
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);

// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
_rawPointer = (IntPtr)Unsafe.AsPointer(ref JitHelpers.GetArrayData(array));
_pointer = new ByReference<T>(ref JitHelpers.GetArrayData(array));
_length = array.Length;
}

Expand All @@ -54,8 +53,7 @@ public ReadOnlySpan(T[] array, int start)
if ((uint)start > (uint)array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();

// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
_rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_length = array.Length - start;
}

Expand All @@ -78,8 +76,7 @@ public ReadOnlySpan(T[] array, int start, int length)
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();

// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
_rawPointer = (IntPtr)Unsafe.AsPointer(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
_length = length;
}

Expand All @@ -105,7 +102,7 @@ public unsafe ReadOnlySpan(void* pointer, int length)
if (length < 0)
ThrowHelper.ThrowArgumentOutOfRangeException();

_rawPointer = (IntPtr)pointer;
_pointer = new ByReference<T>(ref Unsafe.AsRef<T>(pointer));
_length = length;
}

Expand All @@ -114,26 +111,25 @@ public unsafe ReadOnlySpan(void* pointer, int length)
/// </summary>
internal ReadOnlySpan(ref T ptr, int length)
{
// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
_rawPointer = (IntPtr)Unsafe.AsPointer(ref ptr);
_pointer = new ByReference<T>(ref ptr);
_length = length;
}

/// <summary>
/// An internal helper for accessing spans.
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
internal unsafe ref T GetRawPointer()
public ref T DangerousGetPinnableReference()
{
// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
return ref Unsafe.As<IntPtr, T>(ref *(IntPtr *)_rawPointer);
return ref _pointer.Value;
}

/// <summary>
/// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
/// </summary>
public static implicit operator ReadOnlySpan<T>(Span<T> slice)
{
return new ReadOnlySpan<T>(ref slice.GetRawPointer(), slice.Length);
return new ReadOnlySpan<T>(ref slice.DangerousGetPinnableReference(), slice.Length);
}

/// <summary>
Expand Down Expand Up @@ -189,7 +185,7 @@ public T this[int index]
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();

return Unsafe.Add(ref GetRawPointer(), index);
return Unsafe.Add(ref DangerousGetPinnableReference(), index);
}
}

Expand All @@ -204,7 +200,7 @@ public T[] ToArray()
return Array.Empty<T>();

var destination = new T[_length];
SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref GetRawPointer(), _length);
SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref DangerousGetPinnableReference(), _length);
return destination;
}

Expand All @@ -221,7 +217,7 @@ public ReadOnlySpan<T> Slice(int start)
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), _length - start);
return new ReadOnlySpan<T>(ref Unsafe.Add(ref DangerousGetPinnableReference(), start), _length - start);
}

/// <summary>
Expand All @@ -238,7 +234,7 @@ public ReadOnlySpan<T> Slice(int start, int length)
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();

return new ReadOnlySpan<T>(ref Unsafe.Add(ref GetRawPointer(), start), length);
return new ReadOnlySpan<T>(ref Unsafe.Add(ref DangerousGetPinnableReference(), start), length);
}

/// <summary>
Expand All @@ -248,7 +244,7 @@ public ReadOnlySpan<T> Slice(int start, int length)
public bool Equals(ReadOnlySpan<T> other)
{
return (_length == other.Length) &&
(_length == 0 || Unsafe.AreSame(ref GetRawPointer(), ref other.GetRawPointer()));
(_length == 0 || Unsafe.AreSame(ref DangerousGetPinnableReference(), ref other.DangerousGetPinnableReference()));
}

/// <summary>
Expand All @@ -261,7 +257,7 @@ public bool TryCopyTo(Span<T> destination)
if ((uint)_length > (uint)destination.Length)
return false;

SpanHelper.CopyTo<T>(ref destination.GetRawPointer(), ref GetRawPointer(), _length);
SpanHelper.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref DangerousGetPinnableReference(), _length);
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal static unsafe class Unsafe
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void* AsPointer<T>(ref T value)
{
// The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
// The body of this function will be replaced by the EE with unsafe code!!!
// See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
Expand All @@ -40,14 +40,26 @@ public static int SizeOf<T>()
throw new InvalidOperationException();
}

/// <summary>
/// Reinterprets the given location as a reference to a value of type<typeparamref name="T"/>.
/// </summary>
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(void * source)
{
// The body of this function will be replaced by the EE with unsafe code!!!
// See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}

/// <summary>
/// Reinterprets the given reference as a reference to a value of type <typeparamref name="TTo"/>.
/// </summary>
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo As<TFrom, TTo>(ref TFrom source)
{
// The body of this function will be replaced by the EE with unsafe code that just returns sizeof !!T
// The body of this function will be replaced by the EE with unsafe code!!!
// See getILIntrinsicImplementationForUnsafe for how this happens.
throw new InvalidOperationException();
}
Expand Down
Loading

0 comments on commit 4025e6f

Please sign in to comment.