Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
da7b00b
initial port from the prototype
VSadov Feb 25, 2023
170a306
parse the attribute in MT builder
VSadov Feb 26, 2023
bb1c3db
validate replicated size
VSadov Feb 26, 2023
0a4cd78
aot changes
VSadov Feb 26, 2023
6eeb887
refmap
VSadov Feb 27, 2023
f0d156b
validate total size in ilc
VSadov Feb 27, 2023
470aee2
add the actual attribute
VSadov Feb 28, 2023
78ab523
Apply suggestions from code review
VSadov Feb 28, 2023
a452be7
add a small use of InlineArray in CoreLib - to get some crossgen cove…
VSadov Feb 28, 2023
2801f50
a few more uses in CorLib
VSadov Mar 1, 2023
e3284d1
fix for sequential layout
VSadov Mar 1, 2023
4ff0a25
Standardize on use of "InlineArray" in the implementation
VSadov Mar 2, 2023
b406dfe
simpler layout replication
VSadov Mar 2, 2023
7f197d6
some initial tests (will add more)
VSadov Mar 3, 2023
1852993
more tests
VSadov Mar 4, 2023
295caf1
limit the max size of array instance to 1MiB
VSadov Mar 4, 2023
e7738ea
fix an assert in importercalls.cpp
VSadov Mar 4, 2023
065467b
"result" in GC layout should track the pointer count, not the size
VSadov Mar 4, 2023
62b20c8
error messages
VSadov Mar 8, 2023
01e2e17
fixed uses of "value array" in comments.
VSadov Mar 8, 2023
f844772
PR feedback on StackAllocedArguments
VSadov Mar 8, 2023
1b5f608
use the same size limit for inline arrays in the typeloader
VSadov Mar 8, 2023
bf676e4
more PR feedback
VSadov Mar 8, 2023
7d42fbc
remove SetCannotBeBlittedByObjectCloner
VSadov Mar 8, 2023
7fff2be
moving GetInlineArrayLength to MetadataType and related changes
VSadov Mar 10, 2023
2d403ee
use type.Context.Target.PointerSize
VSadov Mar 10, 2023
f6f6acc
lost resources change
VSadov Mar 10, 2023
d68cfa2
fix for x86
VSadov Mar 12, 2023
ebc698b
Do not make InlineArrayType an inline array just yet.
VSadov Mar 13, 2023
9373c77
CORINFO_FLG_INDEXABLE_FIELDS
VSadov Mar 13, 2023
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
Prev Previous commit
Next Next commit
more tests
  • Loading branch information
VSadov committed Mar 10, 2023
commit 1852993d0d8836917500ecba16c7e1e44ffeea7c
350 changes: 346 additions & 4 deletions src/tests/Loader/classloader/InlineArray/InlineArrayValid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,365 @@

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

using Xunit;

[InlineArray(42)]
struct FourtyTwoBytes
// we will be doing "sizeof" with arrays containing managed references.
#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaredpar - An example of a simple inline array.

[InlineArray(LengthConst)]
struct MyArray<T> : IEnumerable<T>
{
byte b;
private const int LengthConst = 42;
private T _element;

public int Length => LengthConst;

[UnscopedRef]
public ref T this[int i]
{
get
{
if ((uint)i >= (uint)Length)
throw new IndexOutOfRangeException(nameof(i));

return ref Unsafe.Add(ref _element, i);
}
}

[UnscopedRef]
public Span<T> AsSpan() => MemoryMarshal.CreateSpan<T>(ref _element, Length);

IEnumerator IEnumerable.GetEnumerator() => (IEnumerator<T>)this.GetEnumerator();

public IEnumerator<T> GetEnumerator()
{
for (int i =0; i < Length; i++)
{
yield return this[i];
}
}
}

unsafe class Validate
{
// ====================== SizeOf ==============================================================
[InlineArray(42)]
struct FourtyTwoBytes
{
byte b;
}

[Fact]
public static void Sizeof()
{
Console.WriteLine($"{nameof(Sizeof)}...");
Assert.Equal(42, sizeof(FourtyTwoBytes));
}
Assert.Equal(84, sizeof(MyArray<char>));
}

// ====================== OneElement ==========================================================
[InlineArray(1)]
struct OneObj
{
public object obj;
}

[Fact]
public static void OneElement()
{
Console.WriteLine($"{nameof(OneElement)}...");
Assert.Equal(sizeof(nint), sizeof(OneObj));
}

// ====================== UseOnStack ==========================================================
class One { }
class Two { }
class Three { }
class Four { }

[MethodImpl(MethodImplOptions.NoInlining)]
private static unsafe Arr1 Initialize(Arr1 s)
{
s[0].o = new One();
s[1].o = new Two();
s[2].o = new Three();
s[3].o = new Four();
return s;
}

struct E
{
public int x;
public int y;
public object o;
}

[InlineArray(Length)]
struct Arr1
{
public const int Length = 42;
public E e;

[UnscopedRef]
public ref E this[int i] => ref Unsafe.Add(ref e, i);
}

static object s;
private static unsafe void MakeGarbage()
{
// make garbage
for (int i = 0; i < 10000; i++)
{
s = new int[i];
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void PassByValueDoGcAndValidate(Arr1 s1)
{
MakeGarbage();

GC.Collect(2, GCCollectionMode.Forced, true, true);
GC.Collect(2, GCCollectionMode.Forced, true, true);

Assert.Equal("One", s1[0].o.GetType().Name);
Assert.Equal("Two", s1[1].o.GetType().Name);
Assert.Equal("Three", s1[2].o.GetType().Name);
Assert.Equal("Four", s1[3].o.GetType().Name);
}

[Fact]
public static void UseOnStack()
{
Console.WriteLine($"{nameof(UseOnStack)}...");

Arr1 s = default;
MakeGarbage();

s = Initialize(s);

// use as a byval argument
PassByValueDoGcAndValidate(s);

// refs must be separate and alive
Assert.Equal("One", s[0].o.GetType().Name);
Assert.Equal("Two", s[1].o.GetType().Name);
Assert.Equal("Three", s[2].o.GetType().Name);
Assert.Equal("Four", s[3].o.GetType().Name);

// should copy by value
Arr1 s1 = s;
Assert.Equal("One", s1[0].o.GetType().Name);
Assert.Equal("Two", s1[1].o.GetType().Name);
Assert.Equal("Three", s1[2].o.GetType().Name);
Assert.Equal("Four", s1[3].o.GetType().Name);
}

// ====================== MixObjectsAndValuetypes =============================================
[InlineArray(Length)]
struct ObjShortArr
{
public const int Length = 100;
public (object, short) element;

[UnscopedRef]
public ref (object o, short s) this[int i] => ref Unsafe.Add(ref element, i);
}

[Fact]
public static void MixObjectsAndValuetypes()
{
Console.WriteLine($"{nameof(MixObjectsAndValuetypes)}...");
Assert.Equal(ObjShortArr.Length * sizeof(nint) * 2, sizeof(ObjShortArr));

var arr = new ObjShortArr();
for (short i = 0; i < ObjShortArr.Length; i++)
{
arr[i].o = i;
arr[i].s = (short)(i + 1);
}

GC.Collect(2, GCCollectionMode.Forced, true, true);

for (short i = 0; i < ObjShortArr.Length; i++)
{
Assert.Equal(i, arr[i].o);
Assert.Equal(i + 1, arr[i].s);
}
}

// ====================== RefLikeOuter ========================================================
[InlineArray(Length)]
ref struct ObjShortArrRef
{
public const int Length = 100;
public (object, short) element;

[UnscopedRef]
public ref (object o, short s) this[int i] => ref Unsafe.Add(ref element, i);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void TestRefLikeOuterMethodArg(ObjShortArrRef arr)
{
GC.Collect(2, GCCollectionMode.Forced, true, true);

for (short i = 0; i < ObjShortArrRef.Length; i++)
{
Assert.Equal(i * 2, arr[i].o);
Assert.Equal(i * 2 + 1, arr[i].s);
}
}

[Fact]
public static void RefLikeOuter()
{
Console.WriteLine($"{nameof(RefLikeOuter)}...");

var arr = new ObjShortArrRef();
for (short i = 0; i < ObjShortArrRef.Length; i++)
{
arr[i].o = i;
arr[i].s = (short)(i + 1);
}

GC.Collect(2, GCCollectionMode.Forced, true, true);

for (short i = 0; i < ObjShortArrRef.Length; i++)
{
Assert.Equal(i, arr[i].o);
Assert.Equal(i + 1, arr[i].s);
}

for (short i = 0; i < ObjShortArrRef.Length; i++)
{
arr[i].o = i * 2;
arr[i].s = (short)(i * 2 + 1);
}

TestRefLikeOuterMethodArg(arr);
}

// ====================== RefLikeInner ========================================================
[InlineArray(LengthConst)]
ref struct SpanArr
{
private const int LengthConst = 100;
public Span<object> element;

public Span<object>* this[int i]
{
get
{
fixed (Span<object>* p = &element)
{
return p + i;
}
}
}

public int Length => LengthConst;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void TestRefLikeInnerMethodArg(SpanArr arr)
{
GC.Collect(2, GCCollectionMode.Forced, true, true);

for (int i = 1; i < arr.Length; i++)
{
Assert.Equal(i, arr[i]->Length);
Assert.Equal(i, (*arr[i])[0]);
}
}

[Fact]
public static void RefLikeInner()
{
Console.WriteLine($"{nameof(RefLikeInner)}...");

SpanArr arr = default;
for (int i = 1; i < arr.Length; i++)
{
var objArr = new object[i];
objArr[0] = i;
*arr[i] = objArr;
}

TestRefLikeInnerMethodArg(arr);
}

// ====================== Nested ==============================================================

struct IntObj
{
public int i;
public object o;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void NestedMethodArg(ref MyArray<MyArray<IntObj>> nestedArray)
{
GC.Collect(2, GCCollectionMode.Forced, true, true);

for (int i = 0; i < nestedArray.Length; i++)
{
for (int j = 0; j < nestedArray[i].Length; j++)
{
Assert.Equal(i + j, nestedArray[i][j].o);
Assert.Equal(i * j, nestedArray[i][j].i);
}
}
}

[Fact]
public static void Nested()
{
Console.WriteLine($"{nameof(Nested)}...");

MyArray<MyArray<IntObj>> nestedArray = default;

for(int i = 0; i < nestedArray.Length; i++)
{
for (int j = 0; j < nestedArray[i].Length; j++)
{
nestedArray[i][j].o = i + j;
nestedArray[i][j].i = i * j;
}
}
}

// ====================== Boxed ===============================================================

[MethodImpl(MethodImplOptions.NoInlining)]
static void BoxedMethodArg(IEnumerable<object> arr)
{
GC.Collect(2, GCCollectionMode.Forced, true, true);

int i = 0;
foreach(object obj in arr)
{
Assert.Equal(i++, obj);
}
}

[Fact]
public static void Boxed()
{
Console.WriteLine($"{nameof(Boxed)}...");

MyArray<object> arr = default;
for (int i = 0; i < arr.Length; i++)
{
arr[i] = i;
}

BoxedMethodArg(arr);
}
}