1766 lines
54 KiB
C#
1766 lines
54 KiB
C#
|
using System;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using Burst.Compiler.IL.Tests.Helpers;
|
||
|
using NUnit.Framework;
|
||
|
using Unity.Burst;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityBenchShared;
|
||
|
|
||
|
namespace Burst.Compiler.IL.Tests
|
||
|
{
|
||
|
internal partial class TestStructs
|
||
|
{
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_func_call_by_value()
|
||
|
{
|
||
|
var localVar = new CustomStruct();
|
||
|
localVar.firstfield = 94;
|
||
|
localVar.value = 123;
|
||
|
return byvalue_function_helper(localVar);
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_func_call_by_ref()
|
||
|
{
|
||
|
var localVar = new CustomStruct
|
||
|
{
|
||
|
firstfield = 94,
|
||
|
value = 123
|
||
|
};
|
||
|
byref_function_helper(ref localVar);
|
||
|
return localVar.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_func_call_instance()
|
||
|
{
|
||
|
var localVar = new CustomStruct2 { value = 123 };
|
||
|
return localVar.returnDoubleValue();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_constructor_nondefault()
|
||
|
{
|
||
|
var localVar = new CustomStruct2(123.0f);
|
||
|
return localVar.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_constructor_default()
|
||
|
{
|
||
|
var localVar = new CustomStruct2();
|
||
|
localVar.value = 1;
|
||
|
return localVar.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_copysemantic()
|
||
|
{
|
||
|
var a = new CustomStruct2 { value = 123.0f };
|
||
|
var b = a;
|
||
|
b.value = 345;
|
||
|
return b.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_nested()
|
||
|
{
|
||
|
var a = new TestNestedStruct { v1 = { x = 5 } };
|
||
|
return a.v1.x;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(1.0f)]
|
||
|
public static float test_struct_multiple_fields(float x)
|
||
|
{
|
||
|
var v = new TestVector4
|
||
|
{
|
||
|
x = 1.0f,
|
||
|
y = 2.0f,
|
||
|
z = 3.0f,
|
||
|
w = 4.0f
|
||
|
};
|
||
|
return x + v.x + v.y + v.z + v.w;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_multi_assign()
|
||
|
{
|
||
|
var a = new MultiAssignStruct(2.0F);
|
||
|
return a.x + a.y + a.z;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int test_custom_struct_return_simple()
|
||
|
{
|
||
|
var a = return_value_helper_simple(1, 2);
|
||
|
return a.firstfield + a.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int test_custom_struct_return_constructor()
|
||
|
{
|
||
|
var a = return_value_helper_constructor(1, 2);
|
||
|
return a.firstfield + a.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int test_struct_self_reference()
|
||
|
{
|
||
|
var a = new SelfReferenceStruct
|
||
|
{
|
||
|
Value = 1
|
||
|
};
|
||
|
return a.Value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int test_struct_deep()
|
||
|
{
|
||
|
var deep = new DeepStruct2();
|
||
|
deep.value.value.SetValue(10);
|
||
|
return deep.value.value.GetValue() + deep.value.value.value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(2)]
|
||
|
public static int test_struct_empty(int x)
|
||
|
{
|
||
|
var emptyStruct = new EmptyStruct();
|
||
|
var result = emptyStruct.Increment(x);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float test_struct_with_static_fields()
|
||
|
{
|
||
|
StructWithStaticVariables myStruct = new StructWithStaticVariables();
|
||
|
myStruct.myFloat = 5;
|
||
|
myStruct = copy_struct_with_static_by_value(myStruct);
|
||
|
mutate_struct_with_static_by_ref_value(ref myStruct);
|
||
|
return myStruct.myFloat;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(true)]
|
||
|
[TestCompiler(false)]
|
||
|
public static bool TestStructWithBoolAsInt(bool value)
|
||
|
{
|
||
|
var structWithBoolAsInt = new StructWithBoolAsInt(value);
|
||
|
return structWithBoolAsInt;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
[Ignore("IL Instruction LEAVE not yet supported")]
|
||
|
public static int TestStructDisposable()
|
||
|
{
|
||
|
using (var structDisposable = new StructDisposable())
|
||
|
{
|
||
|
return structDisposable.x + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_InstructionStsfldNotSupported)]
|
||
|
public static void TestStructWithStaticFieldWrite()
|
||
|
{
|
||
|
var test = new StructWithStaticField();
|
||
|
test.CheckWrite();
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromNonReadonlyStaticFieldNotSupported)]
|
||
|
public static void TestStructWithStaticFieldRead()
|
||
|
{
|
||
|
var test = new StructWithStaticField();
|
||
|
test.CheckRead();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestExplicitLayoutSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<Color>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestExplicitLayoutStruct()
|
||
|
{
|
||
|
var color = new Color() { Value = 0xAABBCCDD };
|
||
|
var a = color.Value + GetColorR(ref color) + GetColorG(color) + color.GetColorB() + color.A;
|
||
|
var pair = new NumberPair()
|
||
|
{
|
||
|
SignedA = -13,
|
||
|
UnsignedB = 37
|
||
|
};
|
||
|
var b = pair.SignedA - ((int)pair.UnsignedA) + pair.SignedB - ((int)pair.UnsignedB);
|
||
|
return ((int)a) + b;
|
||
|
}
|
||
|
|
||
|
static uint GetColorR(ref Color color)
|
||
|
{
|
||
|
return color.R;
|
||
|
}
|
||
|
|
||
|
static uint GetColorG(Color color)
|
||
|
{
|
||
|
return color.G;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static uint TestExplicitLayoutWrite()
|
||
|
{
|
||
|
var color = new Color() { Value = 0xAABBCCDD };
|
||
|
color.G = 3;
|
||
|
ColorWriteBByRef(ref color, 7);
|
||
|
return color.Value;
|
||
|
}
|
||
|
|
||
|
static void ColorWriteBByRef(ref Color color, byte v)
|
||
|
{
|
||
|
color.B = v;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private unsafe struct ExplicitLayoutStructUnaligned
|
||
|
{
|
||
|
[FieldOffset(0)] public int a;
|
||
|
[FieldOffset(4)] public sbyte b;
|
||
|
[FieldOffset(5)] public int c;
|
||
|
[FieldOffset(9)] public fixed int d[4];
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitLayoutStructUnaligned()
|
||
|
{
|
||
|
var value = new ExplicitLayoutStructUnaligned
|
||
|
{
|
||
|
a = -2,
|
||
|
b = -5,
|
||
|
c = 9
|
||
|
};
|
||
|
|
||
|
value.d[0] = 1;
|
||
|
value.d[1] = 2;
|
||
|
value.d[2] = 3;
|
||
|
value.d[3] = 4;
|
||
|
|
||
|
return value.a + value.b + value.c + value.d[0] + value.d[1] + value.d[2] + value.d[3];
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public unsafe struct ExplicitLayoutStructFixedBuffer
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public int First;
|
||
|
[FieldOffset(4)]
|
||
|
public fixed int Data[128];
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new ExplicitLayoutStructFixedBuffer(3);
|
||
|
}
|
||
|
|
||
|
public ExplicitLayoutStructFixedBuffer(int x)
|
||
|
{
|
||
|
First = x;
|
||
|
fixed (int* dataPtr = Data)
|
||
|
{
|
||
|
dataPtr[8] = x + 2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if UNITY_ANDROID || UNITY_IOS
|
||
|
[Ignore("This test fails on mobile platforms")]
|
||
|
#endif
|
||
|
[TestCompiler(typeof(ExplicitLayoutStructFixedBuffer.Provider))]
|
||
|
public static unsafe int TestExplicitLayoutStructFixedBuffer(ref ExplicitLayoutStructFixedBuffer x)
|
||
|
{
|
||
|
return x.First + x.Data[8];
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 9)]
|
||
|
public struct ExplicitStructWithSize
|
||
|
{
|
||
|
[FieldOffset(0)] public int a;
|
||
|
[FieldOffset(4)] public sbyte b;
|
||
|
[FieldOffset(5)] public int c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingExplicitStructWithSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructWithSize>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 9)]
|
||
|
public struct SequentialStructWithSize
|
||
|
{
|
||
|
public int a;
|
||
|
public int b;
|
||
|
public sbyte c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingSequentialStructWithSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructWithSize>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 13)]
|
||
|
public struct SequentialStructWithSize2
|
||
|
{
|
||
|
public int a;
|
||
|
public int b;
|
||
|
public sbyte c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingSequentialStructWithSize2()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructWithSize2>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 12, Pack = 8)]
|
||
|
private struct StructSequentialWithSizeAndPack8
|
||
|
{
|
||
|
public double FieldA;
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingSequentialStructWithSizeAndPack8()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructSequentialWithSizeAndPack8>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 8)]
|
||
|
private struct StructExplicitWithSizeAndPack8
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public double FieldA;
|
||
|
[FieldOffset(8)]
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingExplicitStructWithSizeAndPack8()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack8>();
|
||
|
}
|
||
|
|
||
|
private struct StructExplicitWithSizeAndPack8Wrapper
|
||
|
{
|
||
|
public byte FieldA;
|
||
|
public StructExplicitWithSizeAndPack8 FieldB;
|
||
|
|
||
|
public StructExplicitWithSizeAndPack8Wrapper(byte a, StructExplicitWithSizeAndPack8 b)
|
||
|
{
|
||
|
FieldA = a;
|
||
|
FieldB = b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructExplicitWithSizeAndPack8Wrapper()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack8Wrapper>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 10)]
|
||
|
private struct StructSequentialWithSizeSmallerThanActual
|
||
|
{
|
||
|
public double FieldA;
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSequentialWithSizeSmallerThanActual()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructSequentialWithSizeSmallerThanActual>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 12)]
|
||
|
private struct StructSequentialWithSizeSmallerThanNatural
|
||
|
{
|
||
|
public double FieldA;
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructSequentialWithSizeSmallerThanNatural()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructSequentialWithSizeSmallerThanNatural>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 10)]
|
||
|
private struct StructExplicitWithSizeSmallerThanActual
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public double FieldA;
|
||
|
[FieldOffset(8)]
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructExplicitWithSizeSmallerThanActual()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSizeSmallerThanActual>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 12)]
|
||
|
private struct StructExplicitWithSizeAndOverlappingFields
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public double FieldA;
|
||
|
[FieldOffset(4)]
|
||
|
public int FieldB;
|
||
|
[FieldOffset(8)]
|
||
|
public int FieldC;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructExplicitWithSizeAndOverlappingFields()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndOverlappingFields>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 12)]
|
||
|
private struct StructExplicitWithSize
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public double FieldA;
|
||
|
[FieldOffset(8)]
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructExplicitWithSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSize>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 17)]
|
||
|
private struct StructExplicitWithSize17
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public double FieldA;
|
||
|
[FieldOffset(8)]
|
||
|
public int FieldB;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
|
||
|
public static unsafe int TestStructExplicitWithSize17()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructExplicitWithSize17>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public struct ExplicitStructEmpty { }
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingExplicitStructEmpty()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructEmpty>();
|
||
|
}
|
||
|
|
||
|
public struct ExplicitStructEmptyContainer
|
||
|
{
|
||
|
public ExplicitStructEmpty A;
|
||
|
public int B;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
|
||
|
public static unsafe int TestEmptyStructEmbeddedInStruct()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructEmptyContainer>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 0)]
|
||
|
public struct ExplicitStructEmptyWithSize { }
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingExplicitStructEmptyWithSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructEmptyWithSize>();
|
||
|
}
|
||
|
|
||
|
public struct SequentialStructEmptyNoAttributes { }
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestStructSizingSequentialStructEmptyNoAttributes()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructEmptyNoAttributes>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
public struct SequentialStructEmpty { }
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingSequentialStructEmpty()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructEmpty>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 0)]
|
||
|
public struct SequentialStructEmptyWithSize { }
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
|
||
|
public static unsafe int TestStructSizingSequentialStructEmptyWithSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructEmptyWithSize>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 1)]
|
||
|
public struct SequentialStructEmptyWithNonZeroSize { }
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestStructSizingSequentialStructEmptyWithNonZeroSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<SequentialStructEmptyWithNonZeroSize>();
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Auto)]
|
||
|
public struct AutoStruct
|
||
|
{
|
||
|
public int a;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructWithAutoLayoutNotSupported)]
|
||
|
public static unsafe int TestAutoStruct()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<AutoStruct>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestNestedExplicitLayouts()
|
||
|
{
|
||
|
var nested = new NestedExplicit0()
|
||
|
{
|
||
|
Next = new NestedExplicit1()
|
||
|
{
|
||
|
Next = new NestedExplicit2()
|
||
|
{
|
||
|
FValue = 13.37f
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
var a = nested.NextAsInt + nested.Next.NextAsInt + nested.Next.Next.IValue;
|
||
|
nested.Next.Next.FValue = 0.0042f;
|
||
|
var b = nested.NextAsInt + nested.Next.NextAsInt + nested.Next.Next.IValue;
|
||
|
return a + b;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestNestedExplicitLayoutsSize()
|
||
|
{
|
||
|
|
||
|
return UnsafeUtility.SizeOf<NestedExplicit0>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static uint TestBitcast()
|
||
|
{
|
||
|
return new FloatRepr()
|
||
|
{
|
||
|
Value = 13.37f
|
||
|
}.AsUint;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static uint TestExplicitStructFromCall()
|
||
|
{
|
||
|
return ReturnStruct().Value + ReturnStruct().R;
|
||
|
}
|
||
|
|
||
|
static Color ReturnStruct()
|
||
|
{
|
||
|
return new Color()
|
||
|
{
|
||
|
R = 10,
|
||
|
G = 20,
|
||
|
B = 30,
|
||
|
A = 255
|
||
|
};
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe uint TestExplicitLayoutStructWithFixedArray()
|
||
|
{
|
||
|
var x = new FixedArrayExplitLayoutStruct()
|
||
|
{
|
||
|
UpperUInt = 0xAABBCCDD,
|
||
|
LowerUInt = 0xEEFF3344
|
||
|
};
|
||
|
|
||
|
uint sum = 0;
|
||
|
for (int i = 0; i < 8; i++)
|
||
|
{
|
||
|
sum += x.Bytes[i];
|
||
|
if (i < 4) sum += x.Shorts[i];
|
||
|
}
|
||
|
|
||
|
return x.UpperUInt + x.LowerUInt + sum;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitLayoutStructWithFixedArraySize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<FixedArrayExplitLayoutStruct>();
|
||
|
}
|
||
|
|
||
|
public struct StructInvalid
|
||
|
{
|
||
|
public string WowThatStringIsNotSupported;
|
||
|
}
|
||
|
|
||
|
//private struct StructInvalidProvider : IArgumentProvider
|
||
|
//{
|
||
|
// public object[] Arguments => new object[] { new StructInvalid() };
|
||
|
//}
|
||
|
|
||
|
private static CustomStruct return_value_helper_simple(int a, int b)
|
||
|
{
|
||
|
CustomStruct val;
|
||
|
val.firstfield = a;
|
||
|
val.value = b;
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
private static CustomStruct return_value_helper_constructor(int a, int b)
|
||
|
{
|
||
|
return new CustomStruct(a, b);
|
||
|
}
|
||
|
|
||
|
private static float byvalue_function_helper(CustomStruct customStruct)
|
||
|
{
|
||
|
return customStruct.value * 2;
|
||
|
}
|
||
|
|
||
|
private static void byref_function_helper(ref CustomStruct customStruct)
|
||
|
{
|
||
|
customStruct.value = customStruct.value * 2;
|
||
|
}
|
||
|
|
||
|
static StructWithStaticVariables copy_struct_with_static_by_value(StructWithStaticVariables byValue)
|
||
|
{
|
||
|
byValue.myFloat += 2;
|
||
|
return byValue;
|
||
|
}
|
||
|
|
||
|
static void mutate_struct_with_static_by_ref_value(ref StructWithStaticVariables byValue)
|
||
|
{
|
||
|
byValue.myFloat += 2;
|
||
|
}
|
||
|
|
||
|
|
||
|
private struct EmptyStruct
|
||
|
{
|
||
|
public int Increment(int x)
|
||
|
{
|
||
|
return x + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct CustomStruct
|
||
|
{
|
||
|
public int firstfield;
|
||
|
public int value;
|
||
|
|
||
|
public CustomStruct(int a, int b)
|
||
|
{
|
||
|
firstfield = a;
|
||
|
value = b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct DeepStruct2
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public DeepStruct1 value;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
struct DeepStruct1
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public DeepStruct0 value;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
struct DeepStruct0
|
||
|
{
|
||
|
public int value;
|
||
|
|
||
|
public void SetValue(int value)
|
||
|
{
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
public int GetValue()
|
||
|
{
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct CustomStruct2
|
||
|
{
|
||
|
public float value;
|
||
|
|
||
|
public float returnDoubleValue()
|
||
|
{
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
public CustomStruct2(float initialValue)
|
||
|
{
|
||
|
value = initialValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct TestVector4
|
||
|
{
|
||
|
public float x;
|
||
|
public float y;
|
||
|
public float z;
|
||
|
public float w;
|
||
|
}
|
||
|
|
||
|
private struct StructWithBoolAsInt
|
||
|
{
|
||
|
private int _value;
|
||
|
|
||
|
public StructWithBoolAsInt(bool value)
|
||
|
{
|
||
|
_value = value ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
public static implicit operator bool(StructWithBoolAsInt val)
|
||
|
{
|
||
|
return val._value != 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct TestNestedStruct
|
||
|
{
|
||
|
public TestVector4 v1;
|
||
|
}
|
||
|
|
||
|
private struct MultiAssignStruct
|
||
|
{
|
||
|
public float x;
|
||
|
public float y;
|
||
|
public float z;
|
||
|
|
||
|
public MultiAssignStruct(float val)
|
||
|
{
|
||
|
x = y = z = val;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct SelfReferenceStruct
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public int Value;
|
||
|
public unsafe SelfReferenceStruct* Left;
|
||
|
public unsafe SelfReferenceStruct* Right;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
private struct StructForSizeOf
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
|
||
|
public IntPtr Value1;
|
||
|
|
||
|
public Float4 Vec1;
|
||
|
|
||
|
public IntPtr Value2;
|
||
|
|
||
|
public Float4 Vec2;
|
||
|
#pragma warning disable 0649
|
||
|
}
|
||
|
|
||
|
private struct StructWithStaticField
|
||
|
{
|
||
|
public static int MyField;
|
||
|
|
||
|
public void CheckWrite()
|
||
|
{
|
||
|
MyField = 0;
|
||
|
}
|
||
|
|
||
|
public int CheckRead()
|
||
|
{
|
||
|
return MyField;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct Float4
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public float x;
|
||
|
public float y;
|
||
|
public float z;
|
||
|
public float w;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
private struct StructWithStaticVariables
|
||
|
{
|
||
|
#pragma warning disable 0414
|
||
|
#pragma warning disable 0649
|
||
|
const float static_const_float = 9;
|
||
|
static string static_string = "hello";
|
||
|
|
||
|
public float myFloat;
|
||
|
public Float4 myFloat4;
|
||
|
|
||
|
static float static_float_2 = 5;
|
||
|
#pragma warning restore 0649
|
||
|
#pragma warning restore 0414
|
||
|
}
|
||
|
|
||
|
struct StructDisposable : IDisposable
|
||
|
{
|
||
|
public int x;
|
||
|
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
x++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct Color
|
||
|
{
|
||
|
[FieldOffset(0)] public uint Value;
|
||
|
[FieldOffset(0)] public byte R;
|
||
|
[FieldOffset(1)] public byte G;
|
||
|
[FieldOffset(2)] public byte B;
|
||
|
[FieldOffset(3)] public byte A;
|
||
|
|
||
|
public byte GetColorB()
|
||
|
{
|
||
|
return B;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct NumberPair
|
||
|
{
|
||
|
[FieldOffset(0)] public uint UnsignedA;
|
||
|
[FieldOffset(0)] public int SignedA;
|
||
|
[FieldOffset(4)] public uint UnsignedB;
|
||
|
[FieldOffset(4)] public int SignedB;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct NestedExplicit0
|
||
|
{
|
||
|
[FieldOffset(0)] public NestedExplicit1 Next;
|
||
|
[FieldOffset(0)] public int NextAsInt;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct NestedExplicit1
|
||
|
{
|
||
|
[FieldOffset(0)] public NestedExplicit2 Next;
|
||
|
[FieldOffset(0)] public int NextAsInt;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct NestedExplicit2
|
||
|
{
|
||
|
[FieldOffset(0)] public float FValue;
|
||
|
[FieldOffset(0)] public int IValue;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct FloatRepr
|
||
|
{
|
||
|
[FieldOffset(0)] public float Value;
|
||
|
[FieldOffset(0)] public uint AsUint;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit, Size = 24)]
|
||
|
private struct PaddedStruct
|
||
|
{
|
||
|
[FieldOffset(8)] public int Value;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private unsafe struct FixedArrayExplitLayoutStruct
|
||
|
{
|
||
|
[FieldOffset(0)] public fixed byte Bytes[8];
|
||
|
[FieldOffset(0)] public fixed ushort Shorts[4];
|
||
|
[FieldOffset(0)] public uint UpperUInt;
|
||
|
[FieldOffset(4)] public uint LowerUInt;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public unsafe struct Chunk
|
||
|
{
|
||
|
[FieldOffset(0)] public Chunk* Archetype;
|
||
|
[FieldOffset(8)] public Chunk* metaChunkEntity;
|
||
|
|
||
|
[FieldOffset(16)] public int Count;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestRegressionInvalidGetElementPtrStructLayout()
|
||
|
{
|
||
|
Chunk* c = stackalloc Chunk[1];
|
||
|
c[0].Archetype = null;
|
||
|
c[0].metaChunkEntity = null;
|
||
|
c[0].Count = 0;
|
||
|
|
||
|
return TestRegressionInvalidGetElementPtrStructLayoutInternal(0, 1, &c);
|
||
|
}
|
||
|
|
||
|
public static unsafe int TestRegressionInvalidGetElementPtrStructLayoutInternal(int index, int limit, Chunk** currentChunk)
|
||
|
{
|
||
|
int rValue = 0;
|
||
|
while (index >= limit + 1)
|
||
|
{
|
||
|
rValue += (*currentChunk)->Count;
|
||
|
index += 1;
|
||
|
}
|
||
|
|
||
|
return rValue;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public unsafe struct Packet
|
||
|
{
|
||
|
[FieldOffset(0)] public int data;
|
||
|
[FieldOffset(0)] public fixed byte moreData[1500];
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitSizeReporting()
|
||
|
{
|
||
|
return sizeof(Packet);
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct ExplicitStructPackedButWithHoles
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public byte A;
|
||
|
|
||
|
[FieldOffset(1)]
|
||
|
public long B;
|
||
|
|
||
|
[FieldOffset(21)]
|
||
|
public byte C;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestExplicitStructPackedButWithHolesSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructPackedButWithHoles>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitStructPackedButWithHolesOffsetC()
|
||
|
{
|
||
|
var value = new ExplicitStructPackedButWithHoles();
|
||
|
var addressStart = &value;
|
||
|
var addressField = &value.C;
|
||
|
return (int)((byte*)addressField - (byte*)addressStart);
|
||
|
}
|
||
|
|
||
|
private struct ExplicitStructPackedButWithHolesContainer
|
||
|
{
|
||
|
public ExplicitStructPackedButWithHoles A;
|
||
|
public int B;
|
||
|
public ExplicitStructPackedButWithHoles C;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestExplicitStructPackedButWithHolesContainerSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructPackedButWithHolesContainer>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitStructPackedButWithHolesContainerOffsetC()
|
||
|
{
|
||
|
var value = new ExplicitStructPackedButWithHolesContainer();
|
||
|
var addressStart = &value;
|
||
|
var addressField = &value.C;
|
||
|
return (int)((byte*)addressField - (byte*)addressStart);
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct ExplicitStructNotPackedWithHoles
|
||
|
{
|
||
|
[FieldOffset(4)]
|
||
|
public int A;
|
||
|
|
||
|
[FieldOffset(12)]
|
||
|
public int B;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestExplicitStructNotPackedWithHolesSize()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<ExplicitStructNotPackedWithHoles>();
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float TestExplicitStructNested()
|
||
|
{
|
||
|
StructWithNestUnion b;
|
||
|
b.Value.Min = 5.0f;
|
||
|
|
||
|
return b.Value.Min;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static float TestExplicitStructNestedAsArgument()
|
||
|
{
|
||
|
float Helper(StructWithNestUnion outer)
|
||
|
{
|
||
|
return outer.Value.Min;
|
||
|
}
|
||
|
|
||
|
return Helper(new StructWithNestUnion
|
||
|
{
|
||
|
Value = new UnionValue { Min = 5.0f }
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public struct StructWithNestUnion
|
||
|
{
|
||
|
public UnionValue Value;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
|
||
|
public struct UnionValue
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public float Min;
|
||
|
[FieldOffset(4)]
|
||
|
public float Max;
|
||
|
|
||
|
[FieldOffset(0)]
|
||
|
public uint Property;
|
||
|
}
|
||
|
|
||
|
#if UNITY_ANDROID || UNITY_IOS
|
||
|
[Ignore("This test fails on mobile platforms")]
|
||
|
#endif
|
||
|
[TestCompiler(typeof(NetworkEndPoint.Provider), typeof(NetworkEndPoint.Provider), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructByValueNotSupported)]
|
||
|
public static bool TestABITransformIntoExplicitLayoutTransform(NetworkEndPoint a, NetworkEndPoint b)
|
||
|
{
|
||
|
return a.Compare(b);
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public unsafe struct NetworkEndPoint
|
||
|
{
|
||
|
internal const int ArrayLength = 2;
|
||
|
[FieldOffset(0)]
|
||
|
internal fixed byte data[ArrayLength];
|
||
|
[FieldOffset(0)]
|
||
|
internal ushort family;
|
||
|
[FieldOffset(28)]
|
||
|
internal int length;
|
||
|
|
||
|
public bool Compare(NetworkEndPoint other)
|
||
|
{
|
||
|
if (length != other.length)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
public class Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => default(NetworkEndPoint);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public struct SequentialStructWithPaddingAndVectorField
|
||
|
{
|
||
|
public byte a;
|
||
|
public float2 b;
|
||
|
|
||
|
public class Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new SequentialStructWithPaddingAndVectorField { a = 1, b = new float2(4, 5) };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if UNITY_ANDROID || UNITY_IOS
|
||
|
[Ignore("This test fails on mobile platforms")]
|
||
|
#endif
|
||
|
[TestCompiler(typeof(SequentialStructWithPaddingAndVectorField.Provider))]
|
||
|
public static int TestSequentialStructWithPaddingAndVectorField(ref SequentialStructWithPaddingAndVectorField value)
|
||
|
{
|
||
|
return (int)value.b.x;
|
||
|
}
|
||
|
|
||
|
private static void TestSequentialStructWithPaddingAndVectorFieldRefHelper(ref SequentialStructWithPaddingAndVectorField value)
|
||
|
{
|
||
|
value.b.yx = value.b;
|
||
|
value.b = value.b.yx;
|
||
|
}
|
||
|
|
||
|
#if UNITY_ANDROID || UNITY_IOS
|
||
|
[Ignore("This test fails on mobile platforms")]
|
||
|
#endif
|
||
|
[TestCompiler(typeof(SequentialStructWithPaddingAndVectorField.Provider))]
|
||
|
public static int TestSequentialStructWithPaddingAndVectorFieldRef(ref SequentialStructWithPaddingAndVectorField value)
|
||
|
{
|
||
|
TestSequentialStructWithPaddingAndVectorFieldRefHelper(ref value);
|
||
|
return (int)value.b.x;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestSequentialStructWithPaddingAndVectorFieldPtr()
|
||
|
{
|
||
|
var vec = new float2(1, 2);
|
||
|
var vecPtr = &vec;
|
||
|
var value = new SequentialStructWithPaddingAndVectorField();
|
||
|
value.b = *vecPtr;
|
||
|
return (int)value.b.x;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestCreatingVectorTypeFromNonVectorScalarType()
|
||
|
{
|
||
|
var x = (short)4;
|
||
|
var value = new int4(x, x, x, x);
|
||
|
return value.w;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public struct ExplicitVectors
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public int A;
|
||
|
[FieldOffset(4)]
|
||
|
public int2 B; //NB: Any Vector type is sufficient
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestVectorLoadFromExplicitStruct()
|
||
|
{
|
||
|
var header = new ExplicitVectors { };
|
||
|
|
||
|
return header.B.x;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(DataRange.Standard)]
|
||
|
public static unsafe int TestVectorStoreToExplicitStruct(ref int2 a)
|
||
|
{
|
||
|
var header = new ExplicitVectors { B = a };
|
||
|
|
||
|
return header.B.x;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(StructWithNonBlittableTypes), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotBlittableForFunctionPointer)]
|
||
|
public static unsafe int TestStructWithNonBlittableTypes(ref StructWithNonBlittableTypes a)
|
||
|
{
|
||
|
var checksum = 0;
|
||
|
checksum = (checksum * 397) ^ a.a0;
|
||
|
checksum = (checksum * 397) ^ a.b0;
|
||
|
checksum = (checksum * 397) ^ a.b1;
|
||
|
checksum = (checksum * 397) ^ (a.d0 ? 10 : 0);
|
||
|
checksum = (checksum * 397) ^ a.a1;
|
||
|
checksum = (checksum * 397) ^ a.b1;
|
||
|
checksum = (checksum * 397) ^ a.c1;
|
||
|
checksum = (checksum * 397) ^ (a.d1 ? 0 : 7);
|
||
|
checksum = (checksum * 397) ^ a.Check;
|
||
|
return checksum;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(StructWithNonBlittableTypesWithMarshalAs))]
|
||
|
public static unsafe int TestStructWithBlittableTypesWithMarshalAs(ref StructWithNonBlittableTypesWithMarshalAs a)
|
||
|
{
|
||
|
var checksum = 0;
|
||
|
checksum = (checksum * 397) ^ a.a0;
|
||
|
checksum = (checksum * 397) ^ a.b0;
|
||
|
checksum = (checksum * 397) ^ a.b1;
|
||
|
checksum = (checksum * 397) ^ (a.d0 ? 10 : 0);
|
||
|
checksum = (checksum * 397) ^ a.a1;
|
||
|
checksum = (checksum * 397) ^ a.b1;
|
||
|
checksum = (checksum * 397) ^ a.c1;
|
||
|
checksum = (checksum * 397) ^ (a.d1 ? 0 : 7);
|
||
|
checksum = (checksum * 397) ^ a.Check;
|
||
|
return checksum;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestSizeOfStructWithBlittableTypesWithMarshalAs()
|
||
|
{
|
||
|
return UnsafeUtility.SizeOf<StructWithNonBlittableTypesWithMarshalAs>();
|
||
|
}
|
||
|
|
||
|
#if BURST_TESTS_ONLY
|
||
|
[TestCompiler(typeof(StructWithNonBlittableTypes), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotBlittableForFunctionPointer)]
|
||
|
public static int TestStructWithNonBlittableTypesOffset(ref StructWithNonBlittableTypes a)
|
||
|
{
|
||
|
return Unsafe.ByteOffset(ref a.a0, ref a.a1).ToInt32();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
[TestCompiler(typeof(StructWithBlittableTypes))]
|
||
|
public static unsafe int TestStructWithBlittableTypes(ref StructWithBlittableTypes a)
|
||
|
{
|
||
|
var checksum = 0;
|
||
|
checksum = (checksum * 397) ^ a.a;
|
||
|
checksum = (checksum * 397) ^ a.b;
|
||
|
checksum = (checksum * 397) ^ a.c;
|
||
|
checksum = (checksum * 397) ^ a.d.x;
|
||
|
checksum = (checksum * 397) ^ a.d.y;
|
||
|
return checksum;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static int TestStructWithPointerDependency()
|
||
|
{
|
||
|
var test = new StructWithPointerDependency();
|
||
|
return test.DirectNoDependency.Value;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static uint TestExplicitStructNestedFieldAccess()
|
||
|
{
|
||
|
var buffer = new StructWithNestUnionContainer();
|
||
|
return buffer.Something.Value.Property;
|
||
|
}
|
||
|
|
||
|
public struct StructWithNestUnionContainer
|
||
|
{
|
||
|
public StructWithNestUnion Something => new StructWithNestUnion { Value = new UnionValue { Property = 42 } };
|
||
|
}
|
||
|
|
||
|
public struct StructWithBlittableTypes : IArgumentProvider
|
||
|
{
|
||
|
public StructWithBlittableTypes(int a, int b, int c, int2 d)
|
||
|
{
|
||
|
this.a = a;
|
||
|
this.b = b;
|
||
|
this.c = c;
|
||
|
this.d = d;
|
||
|
}
|
||
|
|
||
|
public int a;
|
||
|
public int b;
|
||
|
public int c;
|
||
|
public int2 d;
|
||
|
|
||
|
public object Value => new StructWithBlittableTypes(1, 2, 3, new int2(4, 5));
|
||
|
}
|
||
|
|
||
|
public struct StructWithNonBlittableTypes : IArgumentProvider
|
||
|
{
|
||
|
public StructWithNonBlittableTypes(byte a0, byte b0, byte c0, bool d0, byte a1, byte b1, byte c1, bool d1, int check)
|
||
|
{
|
||
|
this.a0 = a0;
|
||
|
this.b0 = b0;
|
||
|
this.c0 = c0;
|
||
|
this.d0 = d0;
|
||
|
this.a1 = a1;
|
||
|
this.b1 = b1;
|
||
|
this.c1 = c1;
|
||
|
this.d1 = d1;
|
||
|
this.Check = check;
|
||
|
}
|
||
|
|
||
|
public byte a0;
|
||
|
public byte b0;
|
||
|
public byte c0;
|
||
|
public bool d0;
|
||
|
|
||
|
public byte a1;
|
||
|
public byte b1;
|
||
|
public byte c1;
|
||
|
public bool d1;
|
||
|
|
||
|
public int Check;
|
||
|
|
||
|
|
||
|
public object Value => new StructWithNonBlittableTypes(1, 2, 3, true, 5, 6, 7, false, 0x12345678);
|
||
|
}
|
||
|
|
||
|
public struct StructWithNonBlittableTypesWithMarshalAs : IArgumentProvider
|
||
|
{
|
||
|
public StructWithNonBlittableTypesWithMarshalAs(byte a0, byte b0, byte c0, bool d0, byte a1, byte b1, byte c1, bool d1, int check)
|
||
|
{
|
||
|
this.a0 = a0;
|
||
|
this.b0 = b0;
|
||
|
this.c0 = c0;
|
||
|
this.d0 = d0;
|
||
|
this.a1 = a1;
|
||
|
this.b1 = b1;
|
||
|
this.c1 = c1;
|
||
|
this.d1 = d1;
|
||
|
this.Check = check;
|
||
|
}
|
||
|
|
||
|
public byte a0;
|
||
|
public byte b0;
|
||
|
public byte c0;
|
||
|
[MarshalAs(UnmanagedType.U1)]
|
||
|
public bool d0;
|
||
|
|
||
|
public byte a1;
|
||
|
public byte b1;
|
||
|
public byte c1;
|
||
|
[MarshalAs(UnmanagedType.U1)]
|
||
|
public bool d1;
|
||
|
|
||
|
public int Check;
|
||
|
|
||
|
|
||
|
public object Value => new StructWithNonBlittableTypesWithMarshalAs(1, 2, 3, true, 5, 6, 7, false, 0x12345678);
|
||
|
}
|
||
|
|
||
|
public unsafe struct StructWithPointerDependency
|
||
|
{
|
||
|
public StructWithNoDependency* PointerToNoDependency;
|
||
|
|
||
|
public StructWithNoDependency DirectNoDependency;
|
||
|
}
|
||
|
|
||
|
public struct StructWithNoDependency
|
||
|
{
|
||
|
public int Value;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
||
|
public unsafe struct Sized4096
|
||
|
{
|
||
|
public byte First;
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new Sized4096();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static unsafe void TestSized4096(ref Sized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
a = b;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static unsafe void TestSized4096ManualCopy(ref Sized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
for (int i = 0; i < UnsafeUtility.SizeOf<Sized4096>(); i++)
|
||
|
{
|
||
|
fixed (byte* aBytes = &a.First, bBytes = &b.First)
|
||
|
{
|
||
|
aBytes[i] = bBytes[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static unsafe void TestSized4096CopyToAlloca(ref Sized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
Sized4096 c = b;
|
||
|
(&c.First)[4] = 42;
|
||
|
a = c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static unsafe void TestSized4096CopyToStackAlloc0(ref Sized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
Sized4096* c = stackalloc Sized4096[1];
|
||
|
(&c->First)[4] = 42;
|
||
|
a = *c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static unsafe void TestSized4096CopyToStackAlloc1(ref Sized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
byte* bytes = stackalloc byte[4096];
|
||
|
Sized4096* c = (Sized4096*)bytes;
|
||
|
(&c->First)[4] = 42;
|
||
|
a = *c;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 4096)]
|
||
|
public struct MultipleSized4096
|
||
|
{
|
||
|
public Sized4096 a;
|
||
|
public Sized4096 b;
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new MultipleSized4096 { a = new Sized4096(), b = new Sized4096() };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(MultipleSized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static void TestMultipleSized4096(ref MultipleSized4096 a, ref Sized4096 b)
|
||
|
{
|
||
|
a.a = b;
|
||
|
a.a.First = 42;
|
||
|
b = a.b;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(MultipleSized4096.Provider), typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
|
||
|
public static void TestMultipleSized4096CopyToAlloca(ref MultipleSized4096 a, ref Sized4096 b, ref Sized4096 c)
|
||
|
{
|
||
|
MultipleSized4096 d = default;
|
||
|
d.a = b;
|
||
|
b = d.b;
|
||
|
c = a.a;
|
||
|
a = d;
|
||
|
}
|
||
|
|
||
|
public unsafe struct Fixed4096
|
||
|
{
|
||
|
public fixed byte First[4096];
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new Fixed4096();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestFixed4096(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
a = b;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestFixed4096ManualCopy(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
for (int i = 0; i < UnsafeUtility.SizeOf<Fixed4096>(); i++)
|
||
|
{
|
||
|
a.First[i] = b.First[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestFixed4096CopyToAlloca(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
Fixed4096 c = b;
|
||
|
c.First[4] = 42;
|
||
|
a = c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestFixed4096CopyToStackAlloc0(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
Fixed4096* c = stackalloc Fixed4096[1];
|
||
|
c->First[4] = 42;
|
||
|
a = *c;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestFixed4096CopyToStackAlloc1(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
byte* bytes = stackalloc byte[4096];
|
||
|
Fixed4096* c = (Fixed4096*)bytes;
|
||
|
c->First[4] = 42;
|
||
|
a = *c;
|
||
|
}
|
||
|
|
||
|
public unsafe struct PointersInStruct
|
||
|
{
|
||
|
public byte* a;
|
||
|
public byte* b;
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new PointersInStruct { a = null, b = null };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(PointersInStruct.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestPointersInStruct(ref PointersInStruct a, ref Fixed4096 b)
|
||
|
{
|
||
|
fixed (byte* ptr = b.First)
|
||
|
{
|
||
|
a.a = ptr;
|
||
|
}
|
||
|
|
||
|
a.b = a.a;
|
||
|
}
|
||
|
|
||
|
private static unsafe T GenericGetT<T>(T* t) where T : unmanaged
|
||
|
{
|
||
|
return *t;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestGetStructThroughGeneric(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
fixed (void* ptr = &a)
|
||
|
{
|
||
|
var elem = GenericGetT<Fixed4096>((Fixed4096*) ptr);
|
||
|
b = elem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestGetStructThroughReadArrayElement(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
fixed (void* ptr = &a)
|
||
|
{
|
||
|
var elem = UnsafeUtility.ReadArrayElement<Fixed4096>(ptr, 0);
|
||
|
b = elem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestSetStructThroughWriteArrayElement(ref Fixed4096 a, ref Fixed4096 b)
|
||
|
{
|
||
|
fixed (void* ptr = &a)
|
||
|
{
|
||
|
var elem = a;
|
||
|
UnsafeUtility.WriteArrayElement(ptr, 0, elem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private struct Fixed1021
|
||
|
{
|
||
|
public unsafe fixed byte Data[1021];
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestGetSetStructThroughReadWriteArrayElement(ref Fixed4096 a)
|
||
|
{
|
||
|
fixed (void* ptr1 = &a)
|
||
|
{
|
||
|
var ptr2 = (byte*) ptr1 + 1;
|
||
|
UnsafeUtility.WriteArrayElement(ptr1, 0, UnsafeUtility.ReadArrayElement<Fixed1021>(ptr2, 0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(Fixed4096.Provider))]
|
||
|
public static unsafe void TestGetSetStructThroughReadWriteArrayElementNoAlias(ref Fixed4096 a)
|
||
|
{
|
||
|
fixed (void* ptr1 = &a)
|
||
|
{
|
||
|
var ptr2 = (byte*) ptr1 + UnsafeUtility.SizeOf<Fixed1021>();
|
||
|
UnsafeUtility.WriteArrayElement(ptr1, 0, UnsafeUtility.ReadArrayElement<Fixed1021>(ptr2, 0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, Size = 2)]
|
||
|
public struct WithPadding
|
||
|
{
|
||
|
public byte A;
|
||
|
|
||
|
public struct Provider : IArgumentProvider
|
||
|
{
|
||
|
public object Value => new WithPadding { A = 42 };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static readonly WithPadding withPadding = new WithPadding { A = 42 };
|
||
|
|
||
|
[TestCompiler(typeof(ReturnBox))]
|
||
|
public static unsafe byte TestWithPadding(WithPadding* o)
|
||
|
{
|
||
|
*o = withPadding;
|
||
|
return withPadding.A;
|
||
|
}
|
||
|
|
||
|
[CompilerGenerated]
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
public unsafe struct MyCompilerGeneratedButNotReally
|
||
|
{
|
||
|
public fixed int A[1];
|
||
|
}
|
||
|
|
||
|
private static readonly MyCompilerGeneratedButNotReally myCompilerGeneratedButNotReally = new MyCompilerGeneratedButNotReally { };
|
||
|
|
||
|
[TestCompiler(typeof(ReturnBox))]
|
||
|
public static unsafe int TestMyCompilerGeneratedButNotReallyStruct(MyCompilerGeneratedButNotReally* o)
|
||
|
{
|
||
|
*o = myCompilerGeneratedButNotReally;
|
||
|
|
||
|
fixed (int* a = myCompilerGeneratedButNotReally.A)
|
||
|
{
|
||
|
return *a;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public unsafe struct UninitFieldsAreZero
|
||
|
{
|
||
|
public fixed ushort a[3];
|
||
|
public fixed byte b[3];
|
||
|
|
||
|
public UninitFieldsAreZero(ushort x, ushort y, ushort z)
|
||
|
{
|
||
|
a[0] = x;
|
||
|
a[1] = y;
|
||
|
a[2] = z;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[TestCompiler(typeof(ReturnBox))]
|
||
|
public static unsafe void TestUninitFieldsAreZero(UninitFieldsAreZero* o)
|
||
|
{
|
||
|
o->a[0] = 42;
|
||
|
o->a[1] = 42;
|
||
|
o->a[2] = 42;
|
||
|
o->b[0] = 42;
|
||
|
o->b[1] = 42;
|
||
|
o->b[2] = 42;
|
||
|
|
||
|
var n = new UninitFieldsAreZero(13, 53, 4);
|
||
|
|
||
|
*o = n;
|
||
|
}
|
||
|
|
||
|
#pragma warning disable 0649
|
||
|
private struct ExplicitSizesMatchB
|
||
|
{
|
||
|
public uint U;
|
||
|
}
|
||
|
private struct ExplicitSizesMatchC
|
||
|
{
|
||
|
public ulong L;
|
||
|
public uint U;
|
||
|
public ushort S;
|
||
|
public byte B;
|
||
|
}
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
private struct ExplicitSizesMatch
|
||
|
{
|
||
|
[FieldOffset(0)] public int A;
|
||
|
[FieldOffset(4)] public ExplicitSizesMatchB B;
|
||
|
[FieldOffset(4)] public ExplicitSizesMatchC C;
|
||
|
}
|
||
|
#pragma warning restore 0649
|
||
|
[TestCompiler]
|
||
|
public static unsafe int TestExplicitSizesMatch()
|
||
|
{
|
||
|
return sizeof(ExplicitSizesMatch);
|
||
|
}
|
||
|
|
||
|
#if BURST_TESTS_ONLY
|
||
|
private struct ExplicitLayoutAndBoolStruct
|
||
|
{
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public struct ExplicitLayoutStruct
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public byte Byte;
|
||
|
}
|
||
|
|
||
|
// Having `bool` AND a field whose type is an explicit-layout struct
|
||
|
// causes .NET to fallback to auto-layout for the containing struct.
|
||
|
public bool Bool;
|
||
|
public ExplicitLayoutStruct ExplicitLayout;
|
||
|
public long Int64;
|
||
|
}
|
||
|
|
||
|
// This test just exists to verify that .NET does indeed do the fallback
|
||
|
// to auto-layout that we expect it does, since this is the underlying
|
||
|
// reason for us to prevent the combination of
|
||
|
// "bool + explicit-layout struct".
|
||
|
[Test]
|
||
|
[RestrictPlatform(".NET falls back to auto-layout for this struct, but Mono doesn't", Platform.Windows)]
|
||
|
public static unsafe void TestExplicitLayoutAndBoolCausesDotNetToFallbackToAutoLayout()
|
||
|
{
|
||
|
var s = new ExplicitLayoutAndBoolStruct();
|
||
|
|
||
|
var offsetStart = (IntPtr)(&s);
|
||
|
var offsetField = (IntPtr)(&s.Int64);
|
||
|
|
||
|
var offset = offsetField.ToInt64() - offsetStart.ToInt64();
|
||
|
|
||
|
// We would expect the offset to the `Int64` field to be 8.
|
||
|
// But because .NET falls back to auto-layout,
|
||
|
// and places the `Int64` field first,
|
||
|
// the offset is actually 0.
|
||
|
Assert.AreEqual((long)0, offset);
|
||
|
}
|
||
|
|
||
|
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
|
||
|
public static long TestExplicitLayoutAndBoolIsNotSupported()
|
||
|
{
|
||
|
return new ExplicitLayoutAndBoolStruct { Int64 = 8 }.Int64;
|
||
|
}
|
||
|
|
||
|
private struct ExplicitLayoutNestedAndBoolStruct
|
||
|
{
|
||
|
public struct SequentialLayoutStruct
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public ExplicitLayoutStruct ExplicitLayout;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public struct ExplicitLayoutStruct
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public byte Byte;
|
||
|
}
|
||
|
|
||
|
#pragma warning disable 0649
|
||
|
public bool Bool;
|
||
|
public SequentialLayoutStruct SequentialLayout;
|
||
|
#pragma warning restore 0649
|
||
|
public long Int64;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
|
||
|
public static long TestExplicitLayoutNestedAndBoolIsNotSupported()
|
||
|
{
|
||
|
return new ExplicitLayoutNestedAndBoolStruct { Int64 = 8 }.Int64;
|
||
|
}
|
||
|
|
||
|
private struct ExplicitLayoutStructAndBoolWithMarshalAs
|
||
|
{
|
||
|
[StructLayout(LayoutKind.Explicit)]
|
||
|
public struct ExplicitLayoutStruct
|
||
|
{
|
||
|
[FieldOffset(0)]
|
||
|
public byte Byte;
|
||
|
}
|
||
|
|
||
|
#pragma warning disable 0649
|
||
|
[MarshalAs(UnmanagedType.U1)]
|
||
|
public bool Bool;
|
||
|
public ExplicitLayoutStruct ExplicitLayout;
|
||
|
#pragma warning restore 0649
|
||
|
public long Int64;
|
||
|
}
|
||
|
|
||
|
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
|
||
|
public static unsafe long TestExplicitLayoutAndBoolWithMarshalAsIsNotSupported()
|
||
|
{
|
||
|
return new ExplicitLayoutStructAndBoolWithMarshalAs { Int64 = 8 }.Int64;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
private struct SequentialLayoutAndBoolStruct
|
||
|
{
|
||
|
public struct SequentialLayoutStruct
|
||
|
{
|
||
|
#pragma warning disable 0649
|
||
|
public byte Byte;
|
||
|
#pragma warning restore 0649
|
||
|
}
|
||
|
|
||
|
#pragma warning disable 0649
|
||
|
public bool Bool;
|
||
|
public SequentialLayoutStruct SequentialLayout;
|
||
|
#pragma warning restore 0649
|
||
|
public long Int64;
|
||
|
}
|
||
|
|
||
|
[TestCompiler]
|
||
|
public static unsafe long TestSequentialLayoutAndBoolIsSupported()
|
||
|
{
|
||
|
return new SequentialLayoutAndBoolStruct { Int64 = 8 }.Int64;
|
||
|
}
|
||
|
}
|
||
|
}
|