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

Refactor Span<T> to ease implementation of JIT intrinsics #8497

Merged
merged 2 commits into from
Dec 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions 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/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/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
16 changes: 14 additions & 2 deletions src/mscorlib/src/System/Runtime/CompilerServices/Unsafe.cs
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