You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
16 KiB
474 lines
16 KiB
using System; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.InteropServices; |
|
using Unity.Burst; |
|
using Unity.Collections; |
|
using Unity.Collections.LowLevel.Unsafe; |
|
using UnityBenchShared; |
|
using static Unity.Burst.CompilerServices.Aliasing; |
|
|
|
namespace Burst.Compiler.IL.Tests |
|
{ |
|
internal partial class Aliasing |
|
{ |
|
public unsafe struct NoAliasField |
|
{ |
|
[NoAlias] |
|
public int* ptr1; |
|
|
|
[NoAlias] |
|
public int* ptr2; |
|
|
|
public void Compare(ref NoAliasField other) |
|
{ |
|
// Check that we can definitely alias with another struct of the same type as us. |
|
ExpectAliased(in this, in other); |
|
} |
|
|
|
public void Compare(ref ContainerOfManyNoAliasFields other) |
|
{ |
|
// Check that we can definitely alias with another struct which contains the same type as ourself. |
|
ExpectAliased(in this, in other); |
|
} |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value => new NoAliasField { ptr1 = null, ptr2 = null }; |
|
} |
|
} |
|
|
|
public unsafe struct ContainerOfManyNoAliasFields |
|
{ |
|
public NoAliasField s0; |
|
|
|
public NoAliasField s1; |
|
|
|
[NoAlias] |
|
public NoAliasField s2; |
|
|
|
[NoAlias] |
|
public NoAliasField s3; |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value => new ContainerOfManyNoAliasFields { s0 = new NoAliasField { ptr1 = null, ptr2 = null }, s1 = new NoAliasField { ptr1 = null, ptr2 = null }, s2 = new NoAliasField { ptr1 = null, ptr2 = null }, s3 = new NoAliasField { ptr1 = null, ptr2 = null } }; |
|
} |
|
} |
|
|
|
[StructLayout(LayoutKind.Explicit)] |
|
public struct Union |
|
{ |
|
[FieldOffset(0)] |
|
public ulong a; |
|
|
|
[FieldOffset(1)] |
|
public int b; |
|
|
|
[FieldOffset(5)] |
|
public float c; |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value => new Union { a = 4242424242424242, b = 13131313, c = 42.0f }; |
|
} |
|
} |
|
|
|
public unsafe struct LinkedList |
|
{ |
|
public LinkedList* next; |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value => new LinkedList { next = null }; |
|
} |
|
} |
|
|
|
[NoAlias] |
|
public unsafe struct NoAliasWithContentsStruct |
|
{ |
|
public void* ptr0; |
|
public void* ptr1; |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value => new NoAliasWithContentsStruct { ptr0 = null, ptr1 = null }; |
|
} |
|
} |
|
|
|
[NoAlias] |
|
public unsafe struct DoesAliasWithSubStructPointersStruct : IDisposable |
|
{ |
|
public NoAliasWithContentsStruct* s; |
|
public void* ptr; |
|
|
|
public class Provider : IArgumentProvider |
|
{ |
|
public object Value |
|
{ |
|
get |
|
{ |
|
var noAliasSubStruct = (NoAliasWithContentsStruct*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<NoAliasWithContentsStruct>(), UnsafeUtility.AlignOf<NoAliasWithContentsStruct>(), Allocator.Temp); |
|
noAliasSubStruct->ptr0 = null; |
|
noAliasSubStruct->ptr1 = null; |
|
|
|
var s = new DoesAliasWithSubStructPointersStruct { s = noAliasSubStruct, ptr = null }; |
|
|
|
return s; |
|
} |
|
} |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
UnsafeUtility.Free(s, Allocator.Temp); |
|
} |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldWithItself(ref NoAliasField s) |
|
{ |
|
// Check that they correctly alias with themselves. |
|
ExpectAliased(s.ptr1, s.ptr1); |
|
ExpectAliased(s.ptr2, s.ptr2); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldWithAnotherPointer(ref NoAliasField s) |
|
{ |
|
// Check that they do not alias each other because of the [NoAlias] on the ptr1 field above. |
|
ExpectNotAliased(s.ptr1, s.ptr2); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldWithNull(ref NoAliasField s) |
|
{ |
|
// Check that comparing a pointer with null is no alias. |
|
ExpectNotAliased(s.ptr1, null); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckAliasFieldWithNull(ref NoAliasField s) |
|
{ |
|
// Check that comparing a pointer with null is no alias. |
|
ExpectNotAliased(s.ptr2, null); |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static unsafe void NoAliasInfoSubFunctionAlias(int* a, int* b) |
|
{ |
|
ExpectAliased(a, b); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldSubFunctionAlias(ref NoAliasField s) |
|
{ |
|
NoAliasInfoSubFunctionAlias(s.ptr1, s.ptr1); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckCompareWithItself(ref NoAliasField s) |
|
{ |
|
s.Compare(ref s); |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static unsafe void AliasInfoSubFunctionNoAlias([NoAlias] int* a, int* b) |
|
{ |
|
ExpectNotAliased(a, b); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldSubFunctionWithNoAliasParameter(ref NoAliasField s) |
|
{ |
|
AliasInfoSubFunctionNoAlias(s.ptr1, s.ptr1); |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static unsafe void AliasInfoSubFunctionTwoSameTypedStructs(ref NoAliasField s0, ref NoAliasField s1) |
|
{ |
|
// Check that they do not alias within their own structs. |
|
ExpectNotAliased(s0.ptr1, s0.ptr2); |
|
ExpectNotAliased(s1.ptr1, s1.ptr2); |
|
|
|
// But that they do alias across structs. |
|
ExpectAliased(s0.ptr1, s1.ptr1); |
|
ExpectAliased(s0.ptr1, s1.ptr2); |
|
ExpectAliased(s0.ptr2, s1.ptr1); |
|
ExpectAliased(s0.ptr2, s1.ptr2); |
|
|
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider), typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasFieldAcrossTwoSameTypedStructs(ref NoAliasField s0, ref NoAliasField s1) |
|
{ |
|
AliasInfoSubFunctionTwoSameTypedStructs(ref s0, ref s1); |
|
} |
|
|
|
[TestCompiler(4, 13)] |
|
public static void CheckNoAliasRefs([NoAlias] ref int a, ref int b) |
|
{ |
|
ExpectAliased(in a, in a); |
|
ExpectAliased(in b, in b); |
|
ExpectNotAliased(in a, in b); |
|
} |
|
|
|
[TestCompiler(4, 13.53f)] |
|
public static void CheckNoAliasRefsAcrossTypes([NoAlias] ref int a, ref float b) |
|
{ |
|
ExpectNotAliased(in a, in b); |
|
} |
|
|
|
[TestCompiler(typeof(Union.Provider))] |
|
public static void CheckNoAliasRefsInUnion(ref Union u) |
|
{ |
|
ExpectAliased(in u.a, in u.b); |
|
ExpectAliased(in u.a, in u.c); |
|
ExpectNotAliased(in u.b, in u.c); |
|
} |
|
|
|
[TestCompiler(typeof(ContainerOfManyNoAliasFields.Provider))] |
|
public static unsafe void CheckNoAliasOfSubStructs(ref ContainerOfManyNoAliasFields s) |
|
{ |
|
// Since ptr1 and ptr2 have [NoAlias], they do not alias within the same struct instance. |
|
ExpectNotAliased(s.s0.ptr1, s.s0.ptr2); |
|
ExpectNotAliased(s.s1.ptr1, s.s1.ptr2); |
|
ExpectNotAliased(s.s2.ptr1, s.s2.ptr2); |
|
ExpectNotAliased(s.s3.ptr1, s.s3.ptr2); |
|
|
|
// Across s0 and s1 their pointers can alias each other though. |
|
ExpectAliased(s.s0.ptr1, s.s1.ptr1); |
|
ExpectAliased(s.s0.ptr1, s.s1.ptr2); |
|
ExpectAliased(s.s0.ptr2, s.s1.ptr1); |
|
ExpectAliased(s.s0.ptr2, s.s1.ptr2); |
|
|
|
// Also s2 can alias with s0 and s1 (because they do not have [NoAlias]). |
|
ExpectAliased(s.s2.ptr1, s.s0.ptr1); |
|
ExpectAliased(s.s2.ptr1, s.s0.ptr2); |
|
ExpectAliased(s.s2.ptr2, s.s1.ptr1); |
|
ExpectAliased(s.s2.ptr2, s.s1.ptr2); |
|
|
|
// Also s3 can alias with s0 and s1 (because they do not have [NoAlias]). |
|
ExpectAliased(s.s3.ptr1, s.s0.ptr1); |
|
ExpectAliased(s.s3.ptr1, s.s0.ptr2); |
|
ExpectAliased(s.s3.ptr2, s.s1.ptr1); |
|
ExpectAliased(s.s3.ptr2, s.s1.ptr2); |
|
|
|
// But s2 and s3 cannot alias each other (they both have [NoAlias]). |
|
ExpectNotAliased(s.s2.ptr1, s.s3.ptr1); |
|
ExpectNotAliased(s.s2.ptr1, s.s3.ptr2); |
|
ExpectNotAliased(s.s2.ptr2, s.s3.ptr1); |
|
ExpectNotAliased(s.s2.ptr2, s.s3.ptr2); |
|
} |
|
|
|
[TestCompiler(typeof(ContainerOfManyNoAliasFields.Provider))] |
|
public static unsafe void CheckNoAliasFieldCompareWithParentStruct(ref ContainerOfManyNoAliasFields s) |
|
{ |
|
s.s0.Compare(ref s); |
|
s.s1.Compare(ref s); |
|
s.s2.Compare(ref s); |
|
s.s3.Compare(ref s); |
|
} |
|
|
|
[TestCompiler(typeof(LinkedList.Provider))] |
|
public static unsafe void CheckStructPointerOfSameTypeInStruct(ref LinkedList l) |
|
{ |
|
ExpectAliased(in l, l.next); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasWithContentsStruct.Provider))] |
|
public static unsafe void CheckStructWithNoAlias(ref NoAliasWithContentsStruct s) |
|
{ |
|
// Since NoAliasWithContentsStruct has [NoAlias] on the struct definition, it cannot alias with any pointers within the struct. |
|
ExpectNotAliased(in s, s.ptr0); |
|
ExpectNotAliased(in s, s.ptr1); |
|
} |
|
|
|
[TestCompiler(typeof(DoesAliasWithSubStructPointersStruct.Provider))] |
|
public static unsafe void CheckStructWithNoAliasAndSubStructs(ref DoesAliasWithSubStructPointersStruct s) |
|
{ |
|
// Since DoesAliasWithSubStructPointersStruct has [NoAlias] on the struct definition, it cannot alias with any pointers within the struct. |
|
ExpectNotAliased(in s, s.s); |
|
ExpectNotAliased(in s, s.ptr); |
|
|
|
// s.s is a [NoAlias] struct, so it shouldn't alias with pointers within it. |
|
ExpectNotAliased(s.s, s.s->ptr0); |
|
ExpectNotAliased(s.s, s.s->ptr1); |
|
|
|
// But we don't know whether s.s and s.ptr alias. |
|
ExpectAliased(s.s, s.ptr); |
|
|
|
// And we cannot assume that s does not alias with the sub-pointers of s.s. |
|
ExpectAliased(in s, s.s->ptr0); |
|
ExpectAliased(in s, s.s->ptr1); |
|
} |
|
|
|
private unsafe struct AliasingWithSelf |
|
{ |
|
public AliasingWithSelf* ptr; |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
public void CheckAlias() |
|
{ |
|
ExpectAliased(in this, ptr); |
|
} |
|
} |
|
|
|
[TestCompiler] |
|
public static unsafe void CheckAliasingWithSelf() |
|
{ |
|
var s = new AliasingWithSelf { ptr = null }; |
|
s.ptr = (AliasingWithSelf*) &s; |
|
s.CheckAlias(); |
|
} |
|
|
|
private unsafe struct AliasingWithHiddenSelf |
|
{ |
|
public void* ptr; |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
public void CheckAlias() |
|
{ |
|
ExpectAliased(in this, ptr); |
|
} |
|
} |
|
|
|
[TestCompiler] |
|
public static unsafe void CheckAliasingWithHiddenSelf() |
|
{ |
|
var s = new AliasingWithHiddenSelf { ptr = null }; |
|
s.ptr = &s; |
|
s.CheckAlias(); |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
[return: NoAlias] |
|
private static unsafe int* NoAliasReturn(int size) |
|
{ |
|
return (int*)UnsafeUtility.Malloc(size, 16, Allocator.Temp); |
|
} |
|
|
|
[TestCompiler(typeof(NoAliasField.Provider))] |
|
public static unsafe void CheckNoAliasReturn(ref NoAliasField s) |
|
{ |
|
int* ptr1 = NoAliasReturn(40); |
|
int* ptr2 = NoAliasReturn(4); |
|
int* ptr3 = ptr2 + 4; |
|
byte* ptr4 = (byte*)ptr3 + 1; |
|
|
|
// Obviously it still aliases with itself even it we bitcast. |
|
ExpectAliased((char*)ptr1 + 4, ptr1 + 1); |
|
|
|
// We know that both allocations can't point to the same memory as |
|
// they are derived from Malloc!). |
|
ExpectNotAliased(ptr1, ptr2); |
|
|
|
// Since ptr3 derives from ptr2 it cannot alias with ptr1. |
|
ExpectNotAliased(ptr3, ptr1); |
|
|
|
// And the derefenced memory locations at ptr3 and ptr2 cannot alias |
|
// since ptr3 does not overlap the allocation in ptr2. |
|
ExpectNotAliased(in *ptr3, in *ptr2); |
|
|
|
// The pointers pt4 and ptr3 have overlapping ranges so they do alias. |
|
ExpectAliased(in *ptr4, in *ptr3); |
|
|
|
// The pointers cannot alias with anything else too! |
|
ExpectNotAliased(ptr1, in s); |
|
ExpectNotAliased(ptr1, s.ptr1); |
|
ExpectNotAliased(ptr1, s.ptr2); |
|
ExpectNotAliased(ptr2, in s); |
|
ExpectNotAliased(ptr2, s.ptr1); |
|
ExpectNotAliased(ptr2, s.ptr2); |
|
ExpectNotAliased(ptr3, in s); |
|
ExpectNotAliased(ptr3, s.ptr1); |
|
ExpectNotAliased(ptr3, s.ptr2); |
|
ExpectNotAliased(ptr4, in s); |
|
ExpectNotAliased(ptr4, s.ptr1); |
|
ExpectNotAliased(ptr4, s.ptr2); |
|
|
|
UnsafeUtility.Free(ptr1, Allocator.Temp); |
|
UnsafeUtility.Free(ptr2, Allocator.Temp); |
|
} |
|
|
|
[TestCompiler] |
|
public static unsafe void CheckMallocIsNoAlias() |
|
{ |
|
int* ptr1 = (int*)UnsafeUtility.Malloc(sizeof(int) * 4, 16, Allocator.Temp); |
|
int* ptr2 = (int*)UnsafeUtility.Malloc(sizeof(int), 16, Allocator.Temp); |
|
|
|
ExpectNotAliased(ptr1, ptr2); |
|
|
|
UnsafeUtility.Free(ptr1, Allocator.Temp); |
|
UnsafeUtility.Free(ptr2, Allocator.Temp); |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
[return: NoAlias] |
|
private static unsafe int* BumpAlloc(int* alloca) |
|
{ |
|
int location = alloca[0]++; |
|
return alloca + location; |
|
} |
|
|
|
[TestCompiler] |
|
public static unsafe void CheckBumpAllocIsNoAlias() |
|
{ |
|
int* alloca = stackalloc int[128]; |
|
|
|
// Store our size at the start of the alloca. |
|
alloca[0] = 1; |
|
|
|
int* ptr1 = BumpAlloc(alloca); |
|
int* ptr2 = BumpAlloc(alloca); |
|
|
|
// Our bump allocator will never return the same address twice. |
|
ExpectNotAliased(ptr1, ptr2); |
|
} |
|
|
|
[TestCompiler(42, 13, 56)] |
|
public static unsafe void CheckInRefOut(in int a, ref int b, out int c) |
|
{ |
|
c = 42; |
|
|
|
// They obviously alias with themselves. |
|
ExpectAliased(in a, in a); |
|
ExpectAliased(in b, in b); |
|
ExpectAliased(in c, in c); |
|
|
|
// And alias with each other too. |
|
ExpectAliased(in a, in b); |
|
ExpectAliased(in a, in c); |
|
ExpectAliased(in b, in c); |
|
} |
|
|
|
[TestCompiler(42, 13)] |
|
public static unsafe void CheckOutOut(out int a, out int b) |
|
{ |
|
a = 56; |
|
b = -4; |
|
|
|
ExpectAliased(in a, in b); |
|
} |
|
|
|
private struct SomeData |
|
{ |
|
public int A; |
|
} |
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)] |
|
private static unsafe void OutOfBoundsGEPNoAlias(SomeData* someData) |
|
{ |
|
ExpectNotAliased(in someData[0], in someData[1]); |
|
} |
|
|
|
[TestCompiler] |
|
public static unsafe void CheckOutOfBoundsGEPNoAlias() |
|
{ |
|
var someData = stackalloc SomeData[2]; |
|
someData[0].A = 42; |
|
someData[1].A = 13; |
|
ExpectNotAliased(in someData[0], in someData[1]); |
|
OutOfBoundsGEPNoAlias(someData); |
|
} |
|
} |
|
}
|
|
|