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.
855 lines
21 KiB
855 lines
21 KiB
using System; |
|
using NUnit.Framework; |
|
using Unity.Burst; |
|
using Unity.Collections; |
|
using Unity.Jobs; |
|
using Unity.Mathematics; |
|
|
|
namespace Burst.Compiler.IL.Tests |
|
{ |
|
internal class ControlFlows |
|
{ |
|
[TestCompiler] |
|
public static int For() |
|
{ |
|
var counter = 0; |
|
for (var i = 0; i < 10; i++) |
|
counter++; |
|
return counter; |
|
} |
|
|
|
[TestCompiler(10)] |
|
public static int ForBreak(int a) |
|
{ |
|
int result = 0; |
|
for (int i = 0; i < a; i++) |
|
{ |
|
if (i == 5) |
|
{ |
|
break; |
|
} |
|
|
|
result += 2; |
|
} |
|
return result; |
|
} |
|
|
|
[TestCompiler(10, 5)] |
|
public static int ForContinue(int a, int b) |
|
{ |
|
int result = 0; |
|
for (int i = 0; i < a; i++) |
|
{ |
|
if (i == b) |
|
{ |
|
continue; |
|
} |
|
|
|
result += i; |
|
} |
|
return result; |
|
} |
|
|
|
[TestCompiler] |
|
public static int ForBreak2() |
|
{ |
|
int i = 0; |
|
while (true) |
|
{ |
|
if (i == 5) |
|
{ |
|
break; |
|
} |
|
|
|
i++; |
|
} |
|
return i; |
|
} |
|
|
|
[TestCompiler(10)] |
|
public static float ForDynamicCondition(ref int b) |
|
{ |
|
var counter = 0.0f; |
|
for (var i = 0; i < b; i++) |
|
counter++; |
|
return counter; |
|
} |
|
|
|
[TestCompiler(5, 5)] |
|
public static int ForNestedIf(int a, int b) |
|
{ |
|
var counter = 0; |
|
for (var i = 0; i < a; i++) |
|
for (var i2 = 0; i != b; i++) |
|
{ |
|
counter += i; |
|
counter += i2; |
|
} |
|
return counter; |
|
} |
|
|
|
[TestCompiler(5, 5)] |
|
public static int DoWhileNested(int a, int b) |
|
{ |
|
var total = 0; |
|
var counter2 = 0; |
|
do |
|
{ |
|
var counter1 = 0; |
|
do |
|
{ |
|
total++; |
|
counter1++; |
|
} while (counter1 < a); |
|
counter2++; |
|
} while (counter2 < b); |
|
|
|
return total; |
|
} |
|
|
|
[TestCompiler(5)] |
|
public static int While(int a) |
|
{ |
|
var i = 0; |
|
var counter = 0; |
|
while (i < a) |
|
{ |
|
i++; |
|
counter += i; |
|
} |
|
return counter; |
|
} |
|
|
|
[TestCompiler(5)] |
|
public static int ForForIf(int a) |
|
{ |
|
var counter = 0; |
|
for (var i = 0; i != a; i++) |
|
for (var j = 0; j < 4; j++) |
|
if (j > 2) |
|
counter = counter + i; |
|
return counter; |
|
} |
|
|
|
[TestCompiler(5)] |
|
public static int ForNestedComplex1(int a) |
|
{ |
|
var x = 0; |
|
var y = 0; |
|
for (var i = 0; i < a; i++) |
|
{ |
|
y = y + 1; |
|
for (var j = 0; j < 4; j++) |
|
{ |
|
if (y > 1) |
|
{ |
|
x = x + i; |
|
if (x > 2) |
|
{ |
|
for (int k = 0; k < 3; k++) |
|
{ |
|
y = y + 1; |
|
if (y > 3) |
|
{ |
|
x = x + 1; |
|
} |
|
else if (x > 6) |
|
{ |
|
y = 1; |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
continue; |
|
} |
|
} |
|
else |
|
{ |
|
x--; |
|
} |
|
x++; |
|
} |
|
if (y > 2) |
|
{ |
|
x = x + 1; |
|
} |
|
} |
|
return x; |
|
} |
|
|
|
[TestCompiler(5)] |
|
public static int ForNestedComplex2(int a) |
|
{ |
|
var x = 0; |
|
for (var i = 0; i < a; i++) |
|
{ |
|
var insideLoop1 = 0; |
|
for (var j = 0; j < 4; j++) |
|
{ |
|
x = x + i; |
|
if (x > 2) |
|
{ |
|
insideLoop1++; |
|
for (int k = 0; k < 3; k++) |
|
{ |
|
if (insideLoop1 > 3) |
|
{ |
|
x = x + 1; |
|
} |
|
else if (x > 6) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
if (insideLoop1 > 2) |
|
{ |
|
x = x + 1 + insideLoop1; |
|
} |
|
} |
|
return x; |
|
} |
|
|
|
|
|
[TestCompiler(5)] |
|
[TestCompiler(-5)] |
|
public static int IfReturn(int a) |
|
{ |
|
if (a < 0) |
|
return 55; |
|
return 111; |
|
} |
|
|
|
[TestCompiler(5)] |
|
[TestCompiler(-5)] |
|
public static int IfElseReturn(int a) |
|
{ |
|
int b = 0; |
|
if (a < 0) |
|
{ |
|
b = 1; |
|
} |
|
else |
|
{ |
|
b = 2; |
|
} |
|
return b; |
|
} |
|
|
|
[TestCompiler(5)] |
|
[TestCompiler(-5)] |
|
public static int IfElseReturnDynamic(int a) |
|
{ |
|
int b; |
|
if (a < 0) |
|
{ |
|
b = a; |
|
} |
|
else |
|
{ |
|
b = a + 1; |
|
} |
|
return b; |
|
} |
|
|
|
|
|
[TestCompiler(10)] |
|
public static int WhileFunction(int a) |
|
{ |
|
while (condition_helper(a)) |
|
{ |
|
a--; |
|
} |
|
return a; |
|
} |
|
|
|
[TestCompiler(10)] |
|
public static int WhileDynamic(ref int a) |
|
{ |
|
while (a > 2) |
|
{ |
|
a--; |
|
} |
|
return a; |
|
} |
|
|
|
|
|
[TestCompiler(5, 6, 7)] |
|
[TestCompiler(-5, -6, -7)] |
|
public static int IfDeep(int a, int b, int c) |
|
{ |
|
int result = 0; |
|
if (a < 0) |
|
{ |
|
if (b > 1) |
|
{ |
|
if (c < 2) |
|
{ |
|
result = 55; |
|
} |
|
else |
|
{ |
|
result = 66; |
|
} |
|
} |
|
else |
|
{ |
|
result = 77; |
|
} |
|
} |
|
else |
|
{ |
|
if (b < 0) |
|
{ |
|
if (c < -2) |
|
{ |
|
result = 88; |
|
} |
|
else |
|
{ |
|
result = 99; |
|
} |
|
} |
|
else |
|
{ |
|
result = 100; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
[TestCompiler(5)] |
|
public static int CallRecursive(int n) |
|
{ |
|
return InternalCallRecursive(n); |
|
} |
|
|
|
private static int InternalCallRecursive(int n) |
|
{ |
|
if (n <= 1) |
|
return 1; |
|
return n * InternalCallRecursive(n - 1); |
|
} |
|
|
|
[TestCompiler(3f, 8f)] |
|
[TestCompiler(6f, 8f)] |
|
public static float IfCompareFloat(float a, float b) |
|
{ |
|
if (a > 5f) |
|
return 10f; |
|
return b; |
|
} |
|
|
|
[TestCompiler(10)] |
|
[TestCompiler(0)] |
|
public static float TernaryCompareFloat(int input) |
|
{ |
|
return input > 5 ? 2.5f : 1.2F; |
|
} |
|
|
|
[TestCompiler(0)] |
|
[TestCompiler(1)] |
|
public static int TernaryMask(int a) |
|
{ |
|
return (a & 1) != 0 ? 5 : 4; |
|
} |
|
|
|
[TestCompiler(0)] |
|
[TestCompiler(1)] |
|
public static int IfElseMash(int a) |
|
{ |
|
if ((a & 1) != 0) |
|
return 5; |
|
else |
|
return 4; |
|
} |
|
|
|
[TestCompiler(0)] |
|
public static int IfCallCondition(int a) |
|
{ |
|
if (a > 0 && condition_helper(++a)) |
|
{ |
|
return a; |
|
} |
|
return -10 + a; |
|
} |
|
|
|
[TestCompiler(1)] |
|
[TestCompiler(0)] |
|
[TestCompiler(-1)] |
|
public static int IfIncrementCondition(int a) |
|
{ |
|
if (a < 0 || condition_helper(++a)) |
|
{ |
|
return a; |
|
} |
|
return -10 + a; |
|
} |
|
|
|
|
|
private static bool condition_helper(int value) |
|
{ |
|
return value > 2; |
|
} |
|
|
|
[TestCompiler(1, 8)] |
|
public static int IfWhileGotoForward(int a, int b) |
|
{ |
|
if (a > 0) |
|
{ |
|
while (a < 10) |
|
{ |
|
a++; |
|
if (a == b) |
|
{ |
|
a--; |
|
goto TestLabel; |
|
} |
|
} |
|
a++; |
|
} |
|
TestLabel: |
|
a--; |
|
return a; |
|
} |
|
|
|
[TestCompiler(1, 5)] |
|
public static int IfWhileGotoBackward(int a, int b) |
|
{ |
|
RewindLabel: |
|
if (a > 0) |
|
{ |
|
while (a < 10) |
|
{ |
|
a++; |
|
if (a == b) |
|
{ |
|
a++; |
|
goto RewindLabel; |
|
} |
|
} |
|
a++; |
|
} |
|
a--; |
|
return a; |
|
} |
|
|
|
[TestCompiler(-1, 0)] |
|
[TestCompiler(0, 0)] |
|
[TestCompiler(0, -1)] |
|
public static int IfAssignCondition(int a, int b) |
|
{ |
|
int result = 0; |
|
if (++a > 0 && ++b > 0) |
|
{ |
|
result = a + b; |
|
} |
|
else |
|
{ |
|
result = a * 10 + b; |
|
} |
|
return result; |
|
} |
|
|
|
|
|
private static bool ProcessFirstInt(int a, out float b) |
|
{ |
|
b = a + 1; |
|
return b < 10; |
|
} |
|
|
|
private static bool ProcessNextInt(int a, ref float b) |
|
{ |
|
b = a + 2; |
|
return b < 20; |
|
} |
|
|
|
[TestCompiler(1, 10)] |
|
public static float ForWhileNestedCall(int a, int b) |
|
{ |
|
float value = 0; |
|
for (int i = 0; i < b * 3; i++) |
|
{ |
|
var flag = ProcessFirstInt(a, out value); |
|
int num2 = 0; |
|
while (flag && num2 < 2) |
|
{ |
|
bool flag2 = i == a; |
|
if (flag2) |
|
{ |
|
flag = ProcessNextInt(a + i, ref value); |
|
} |
|
else |
|
{ |
|
value++; |
|
flag = ProcessNextInt(a + b + i, ref value); |
|
} |
|
num2++; |
|
} |
|
} |
|
return value; |
|
} |
|
|
|
#if BURST_TESTS_ONLY |
|
[TestCompiler(true)] |
|
[TestCompiler(false)] |
|
public static bool CheckDup(bool value) |
|
{ |
|
return ILTestsHelper.CheckDupBeforeJump(value); |
|
} |
|
#endif |
|
|
|
[TestCompiler(1)] |
|
public static int WhileIfContinue(int a) |
|
{ |
|
while (a > 10) |
|
{ |
|
if (a < 5) |
|
{ |
|
a++; |
|
if (a == 8) |
|
{ |
|
continue; |
|
} |
|
} |
|
a++; |
|
} |
|
return a; |
|
} |
|
|
|
[TestCompiler(0)] |
|
[TestCompiler(1)] |
|
[TestCompiler(2)] |
|
[TestCompiler(3)] |
|
[TestCompiler(4)] |
|
public static int SwitchReturn(int a) |
|
{ |
|
switch (a) |
|
{ |
|
case 1: |
|
return 100; |
|
case 2: |
|
return 200; |
|
case 3: |
|
return 300; |
|
case 10: |
|
return 300; |
|
default: |
|
return 1000; |
|
} |
|
} |
|
|
|
[TestCompiler(0)] |
|
[TestCompiler(1)] |
|
[TestCompiler(2)] |
|
[TestCompiler(3)] |
|
[TestCompiler(4)] |
|
public static int SwitchBreak(int a) |
|
{ |
|
switch (a) |
|
{ |
|
case 1: |
|
return 100; |
|
case 2: |
|
break; |
|
default: |
|
return 1000; |
|
} |
|
|
|
return 200; |
|
} |
|
|
|
[TestCompiler((byte)0)] |
|
[TestCompiler((byte)1)] |
|
[TestCompiler((byte)2)] |
|
[TestCompiler((byte)3)] |
|
[TestCompiler((byte)4)] |
|
public static int SwitchBreakByte(byte a) |
|
{ |
|
switch (a) |
|
{ |
|
case 1: |
|
return 100; |
|
case 2: |
|
break; |
|
default: |
|
return 1000; |
|
} |
|
|
|
return 200; |
|
} |
|
|
|
public static byte GetValueAsByte(int a) |
|
{ |
|
return (byte)a; |
|
} |
|
|
|
[TestCompiler(0)] |
|
[TestCompiler(1)] |
|
[TestCompiler(2)] |
|
[TestCompiler(3)] |
|
public static byte SwitchByteReturnFromFunction(int a) |
|
{ |
|
switch (GetValueAsByte(a)) |
|
{ |
|
case 0: |
|
return 1; |
|
case 1: |
|
return 2; |
|
case 2: |
|
return 3; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
[TestCompiler(long.MaxValue)] |
|
[TestCompiler(long.MinValue)] |
|
[TestCompiler(0)] |
|
public static byte SwitchOnLong(long a) |
|
{ |
|
switch (a) |
|
{ |
|
case long.MaxValue: |
|
return 1; |
|
case long.MinValue: |
|
return 2; |
|
default: |
|
return 0; |
|
} |
|
} |
|
|
|
public static byte TestSwitchByteReturn(NativeArray<byte> _results, int a) |
|
{ |
|
if (_results.Length > a) |
|
{ |
|
switch (_results[a]) |
|
{ |
|
case 0: |
|
return 1; |
|
case 1: |
|
return 2; |
|
case 2: |
|
return 3; |
|
default: |
|
return 0; |
|
} |
|
} |
|
return 99; |
|
} |
|
|
|
[TestCompiler(EnumSwitch.Case1)] |
|
[TestCompiler(EnumSwitch.Case2)] |
|
[TestCompiler(EnumSwitch.Case3)] |
|
public static int SwitchEnum(EnumSwitch a) |
|
{ |
|
switch (a) |
|
{ |
|
case EnumSwitch.Case1: |
|
return 100; |
|
case EnumSwitch.Case3: |
|
break; |
|
default: |
|
return 1000; |
|
} |
|
|
|
return 200; |
|
} |
|
|
|
public enum EnumSwitch |
|
{ |
|
Case1, |
|
|
|
Case2, |
|
|
|
Case3, |
|
} |
|
|
|
// This test requires 2020.2 because that enabled C# 8.0. |
|
#if BURST_TESTS_ONLY || UNITY_2020_2_OR_NEWER |
|
[TestCompiler(long.MaxValue)] |
|
[TestCompiler(long.MinValue)] |
|
[TestCompiler(0)] |
|
public static byte SwitchExpression(long a) |
|
{ |
|
return a switch |
|
{ |
|
long.MaxValue => 1, |
|
long.MinValue => 2, |
|
_ => 0, |
|
}; |
|
} |
|
#endif |
|
|
|
[TestCompiler(ExpectedException = typeof(InvalidOperationException), ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
[MonoOnly(".NET CLR does not support burst.abort correctly")] |
|
public static int ExceptionReachedReturn() |
|
{ |
|
throw new InvalidOperationException("This is bad 1"); |
|
} |
|
|
|
[TestCompiler(ExpectedException = typeof(InvalidOperationException), ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
[MonoOnly(".NET CLR does not support burst.abort correctly")] |
|
public static void ExceptionReached() |
|
{ |
|
throw new InvalidOperationException("This is bad 2"); |
|
} |
|
|
|
[TestCompiler(1, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
[TestCompiler(2, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
public static void ExceptionNotReached(int a) |
|
{ |
|
if (a > 10) |
|
{ |
|
throw new InvalidOperationException("This is bad 2"); |
|
} |
|
} |
|
|
|
[TestCompiler(1, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
[TestCompiler(2, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
public static void ExceptionMultipleNotReached(int a) |
|
{ |
|
if (a > 10) |
|
{ |
|
if (a > 15) |
|
{ |
|
throw new InvalidOperationException("This is bad 2"); |
|
} |
|
else |
|
{ |
|
if (a < 8) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
else |
|
{ |
|
a = a + 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
private struct SmallStruct |
|
{ |
|
public int I; |
|
public float F; |
|
} |
|
|
|
private static SmallStruct UnreachedException(bool b) |
|
{ |
|
if (b) |
|
{ |
|
throw new Exception("Never here!"); |
|
} |
|
|
|
return new SmallStruct { I = 42, F = 42.0f }; |
|
} |
|
|
|
[TestCompiler(0, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
public static double UnreachedExceptionInCalledFunction(int a) |
|
{ |
|
var result = UnreachedException(a != 0); |
|
|
|
return result.I + result.F; |
|
} |
|
|
|
[TestCompiler(1, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
public static int ExceptionNotReachedReturn(int a) |
|
{ |
|
int b = a; |
|
if (a > 10) |
|
{ |
|
b = 5; |
|
throw new InvalidOperationException("This is bad 2"); |
|
} |
|
return b; |
|
} |
|
|
|
|
|
[TestCompiler(13, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
[TestCompiler(1, ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)] |
|
public static int ExceptionMultipleNotReachedReturn(int a) |
|
{ |
|
if (a > 10) |
|
{ |
|
if (a > 15) |
|
{ |
|
throw new InvalidOperationException("This is bad 2"); |
|
} |
|
else |
|
{ |
|
if (a < 12) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
else |
|
{ |
|
a = a + 1; |
|
} |
|
} |
|
} |
|
return a; |
|
} |
|
|
|
[TestCompiler] |
|
public static void TestInternalError() |
|
{ |
|
var job = new InternalErrorVariableNotFound(); |
|
job.Execute(); |
|
} |
|
|
|
public struct InternalErrorVariableNotFound : IJob |
|
{ |
|
public void Execute() |
|
{ |
|
CausesError(3); |
|
} |
|
|
|
static int CausesError(int x) |
|
{ |
|
int y = 0; |
|
while (y != 0 && y != 1) |
|
{ |
|
if (x > 0) |
|
x = y++; |
|
} |
|
return y; |
|
} |
|
} |
|
|
|
[TestCompiler(true)] |
|
public static int TestPopNonInitialTrailingPush(bool x) |
|
{ |
|
return (x ? 1 : -1) * math.min(16, 1); |
|
} |
|
|
|
[TestCompiler] |
|
// Check unsigned ternary comparison (Bxx_Un) opcodes |
|
public static ulong TestUnsignedTernary() |
|
{ |
|
ulong a = 0; |
|
ulong b = ~0UL; |
|
ulong c = (a < b) ? 1UL : 0; |
|
ulong d = (a <= b) ? 1UL : 0; |
|
ulong e = (a > b) ? 0: 1UL; |
|
ulong f = (a >= b) ? 0: 1UL; |
|
|
|
return c + d + e + f; |
|
} |
|
|
|
[TestCompiler((byte)0)] |
|
[TestCompiler((byte) 1)] |
|
public static int TestByteAndIntFlow(byte value) |
|
{ |
|
var position = value == 0 ? -1 : value; |
|
if (position < 0) |
|
{ |
|
position = 17; |
|
} |
|
return position; |
|
} |
|
} |
|
}
|
|
|