Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Refactor Span<T> to ease implementation of JIT intrinsics
Browse files Browse the repository at this point in the history
- Introduce internal ByReference<T> type for byref fields and change Span to use it
- Generalize handling of byref-like types in the type loader
  • Loading branch information
jkotas committed Dec 7, 2016
1 parent 12c3a06 commit 8ecd21b
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 90 deletions.
3 changes: 1 addition & 2 deletions 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/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
40 changes: 18 additions & 22 deletions src/mscorlib/src/System/ReadOnlySpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace System
/// </summary>
public unsafe 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 unsafe 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
12 changes: 12 additions & 0 deletions src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ 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 that just returns sizeof !!T
// 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>
Expand Down
70 changes: 43 additions & 27 deletions src/mscorlib/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace System
/// </summary>
public unsafe struct Span<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 Span contains.</summary>
private readonly int _length;

Expand All @@ -36,8 +36,7 @@ public Span(T[] array)
ThrowHelper.ThrowArrayTypeMismatchException();
}

// 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 @@ -64,8 +63,7 @@ public Span(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 Down Expand Up @@ -93,8 +91,7 @@ public Span(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 @@ -120,7 +117,7 @@ public unsafe Span(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 @@ -129,18 +126,17 @@ public unsafe Span(void* pointer, int length)
/// </summary>
internal Span(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 unsafe 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>
Expand Down Expand Up @@ -196,14 +192,14 @@ public T this[int index]
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();

return Unsafe.Add(ref GetRawPointer(), index);
return Unsafe.Add(ref DangerousGetPinnableReference(), index);
}
set
{
if ((uint)index >= (uint)_length)
ThrowHelper.ThrowIndexOutOfRangeException();

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

Expand All @@ -218,7 +214,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 @@ -235,7 +231,7 @@ public Span<T> Slice(int start)
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

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

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

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

/// <summary>
Expand All @@ -262,7 +258,7 @@ public Span<T> Slice(int start, int length)
public bool Equals(Span<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 @@ -275,7 +271,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 All @@ -287,7 +283,7 @@ public void Set(ReadOnlySpan<T> values)
if ((uint)values.Length > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

SpanHelper.CopyTo<T>(ref GetRawPointer(), ref values.GetRawPointer(), values.Length);
SpanHelper.CopyTo<T>(ref DangerousGetPinnableReference(), ref values.DangerousGetPinnableReference(), values.Length);
}
}

Expand All @@ -308,7 +304,7 @@ public static Span<byte> AsBytes<T>(this Span<T> source)
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));

return new Span<byte>(
ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()),
checked(source.Length * Unsafe.SizeOf<T>()));
}

Expand All @@ -327,7 +323,7 @@ public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));

return new ReadOnlySpan<byte>(
ref Unsafe.As<T, byte>(ref source.GetRawPointer()),
ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()),
checked(source.Length * Unsafe.SizeOf<T>()));
}

Expand All @@ -352,7 +348,7 @@ public static unsafe Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> sour
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));

return new Span<TTo>(
ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
}

Expand All @@ -377,7 +373,7 @@ public static unsafe ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnly
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));

return new ReadOnlySpan<TTo>(
ref Unsafe.As<TFrom, TTo>(ref source.GetRawPointer()),
ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
}
}
Expand Down Expand Up @@ -421,4 +417,24 @@ internal static unsafe void CopyTo<T>(ref T destination, ref T source, int eleme
}
}
}

internal unsafe struct ByReference<T>
{
private IntPtr _value;

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

public ref T Value
{
get
{
// TODO-SPAN: This has GC hole. It needs to be JIT intrinsic instead
return ref Unsafe.As<IntPtr, T>(ref *(IntPtr*)_value);
}
}
}
}
5 changes: 2 additions & 3 deletions src/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2871,9 +2871,8 @@ void SystemDomain::LoadBaseSystemClasses()
g_pSZArrayHelperClass = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER);

#ifdef FEATURE_SPAN_OF_T
// Load Span class
g_pSpanClass = MscorlibBinder::GetClass(CLASS__SPAN);
g_pReadOnlySpanClass = MscorlibBinder::GetClass(CLASS__READONLY_SPAN);
// Load ByReference class
g_pByReferenceClass = MscorlibBinder::GetClass(CLASS__BYREFERENCE);
#endif

// Load Nullable class
Expand Down
Loading

0 comments on commit 8ecd21b

Please sign in to comment.