initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a2b1c4300b7e37aea22a3646af54828f
folderAsset: yes

View file

@ -0,0 +1,189 @@
using System;
using System.Text;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Burst.Editor;
using UnityEngine;
using Unity.Burst;
using Random = System.Random;
[TestFixture]
public class BurstDisassemblerCoreInstructionTests
{
// Use chooser enum instead of BurstDisassembler.AsmKind because of accessibility level.
public enum Chooser
{
ARM,
INTEL,
LLVMIR,
Wasm
}
[Test]
[TestCase(Chooser.ARM)]
[TestCase(Chooser.INTEL)]
// [TestCase(Chooser.LLVMIR)]
[TestCase(Chooser.Wasm)]
public void TestInfo(Chooser provider)
{
BurstDisassembler.AsmTokenKindProvider tokenProvider;
switch (provider)
{
case Chooser.ARM:
tokenProvider = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
break;
case Chooser.INTEL:
tokenProvider = BurstDisassembler.X86AsmTokenKindProvider.Instance;
break;
case Chooser.Wasm:
tokenProvider = BurstDisassembler.WasmAsmTokenKindProvider.Instance;
break;
default:
throw new Exception("Oops you forgot to add a switch case in the test *quirky smiley*.");
}
var tokenProviderT = typeof(BurstDisassembler.AsmTokenKindProvider);
var field = tokenProviderT.GetField("_tokenKinds",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(field, "Could not find _tokenKinds field in AsmTokenKindProvider");
var allTokens = (Dictionary<StringSlice, BurstDisassembler.AsmTokenKind>)field.GetValue(tokenProvider);
var tokensToTest =
from tok in allTokens.Keys
where allTokens.TryGetValue(tok, out var kind)
&& kind != BurstDisassembler.AsmTokenKind.Qualifier
&& kind != BurstDisassembler.AsmTokenKind.Register
select tok.ToString();
var count = 0;
foreach (var token in tokensToTest)
{
var res = false;
switch (provider)
{
case Chooser.ARM:
res = BurstDisassembler.ARM64InstructionInfo.GetARM64Info(token, out var _);
break;
case Chooser.INTEL:
res = BurstDisassembler.X86AsmInstructionInfo.GetX86InstructionInfo(token, out var _);
break;
case Chooser.LLVMIR:
res = BurstDisassembler.LLVMIRInstructionInfo.GetLLVMIRInfo(token, out var _);
break;
case Chooser.Wasm:
res = BurstDisassembler.WasmInstructionInfo.GetWasmInfo(token, out var _);
break;
}
if (!res)
{
Debug.Log($"Token \"{token}\" from {provider} does not have information associated.");
count++;
}
}
Assert.Zero(count, $"{provider.ToString()} is missing information for {count} token(s).");
}
/// <summary>
/// Tests whether all instructions in available burst jobs are displayed correctly.
/// </summary>
[Test]
[TestCase(Chooser.ARM)]
[TestCase(Chooser.INTEL)]
[TestCase(Chooser.Wasm)]
public void TestInstructionsPresent(Chooser asmKind)
{
BurstTargetCpu targetCpu;
BurstDisassembler.AsmKind targetKind;
switch (asmKind)
{
case Chooser.INTEL:
targetCpu = BurstTargetCpu.X64_SSE4;
targetKind = BurstDisassembler.AsmKind.Intel;
break;
case Chooser.ARM:
targetCpu = BurstTargetCpu.ARMV7A_NEON32;
targetKind = BurstDisassembler.AsmKind.ARM;
break;
default: // WASM as LLVMIR is not tested.
targetCpu = BurstTargetCpu.WASM32;
targetKind = BurstDisassembler.AsmKind.Wasm;
break;
}
// Find all possible burst compile targets.
var jobList = BurstReflection.FindExecuteMethods(
BurstReflection.EditorAssembliesThatCanPossiblyContainJobs,
BurstReflectionAssemblyOptions.None).CompileTargets;
var missingInstructions = new Dictionary<string, string>();
var disassembler = new BurstDisassembler();
foreach (var target in jobList)
{
// Get disassembly of target.
var options = new StringBuilder();
target.Options.TryGetOptions(target.JobType, true, out var defaultOptions);
options.AppendLine(defaultOptions);
// Disables the 2 current warnings generated from code (since they clutter up the inspector display)
// BC1370 - throw inside code not guarded with ConditionalSafetyCheck attribute
// BC1322 - loop intrinsic on loop that has been optimised away
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, Enum.GetNames(typeof(BurstTargetCpu))[(int)targetCpu])}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, "0")}");
var baseOptions = options.ToString();
var append = BurstInspectorGUI.GetDisasmOptions()[(int)DisassemblyKind.Asm];
// Setup disAssembler with the job:
var text = BurstInspectorGUI.GetDisassembly(target.Method, baseOptions + append);
// Bail out if there was a Burst compiler error, because we'll have all sorts of unexpected tokens.
if (BurstInspectorGUI.IsBurstError(text))
{
continue;
}
text = text.TrimStart('\n');
Assert.IsTrue(disassembler.Initialize(text, targetKind, true, false), "Could not initialize disassembler.");
// Get all tokens labeled as AsmTokenKind.Identifier that do not start with '.' nor ends on ':'.
// If this number exceeds 0 we are missing instructions (I believe).
const int INSTRUCTION_PRE_PADDING = 8;
var tokens =
(from tok in disassembler.Tokens
where tok.Kind == BurstDisassembler.AsmTokenKind.Identifier
&& !disassembler.GetTokenAsText(tok).StartsWith(".")
&& !disassembler.GetTokenAsText(tok).EndsWith(":")
&& text[tok.Position - INSTRUCTION_PRE_PADDING - 1] == '\n'
select tok).ToList();
foreach (var token in tokens)
{
if (missingInstructions.ContainsKey(token.ToString(text)))
{
continue;
}
missingInstructions.Add(token.ToString(text), target.GetDisplayName());
}
}
// Convey result.
if (missingInstructions.Count > 0)
{
foreach (var itm in missingInstructions)
{
var token = itm.Key;
var name = itm.Value;
Debug.Log($"Token \"{token}\" was not recognised as instruction for {targetKind} (Found in job {name}).");
}
Assert.Fail($"{missingInstructions.Count} missing instructions, see log. Add missing instructions and call both this test and {nameof(TestInfo)}.");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c68d1e883c8136a89133cc095391d26b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,383 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Burst;
using Unity.Burst.Editor;
using UnityEditorInternal;
using System.Runtime.CompilerServices;
public class BurstDisassemblerTests
{
private BurstDisassembler _disassembler;
[OneTimeSetUp]
public void SetUp()
{
_disassembler = new BurstDisassembler();
}
private static string GetThisFilePath([CallerFilePath] string path = null) => path;
// A Test behaves as an ordinary method
[Test]
public void GetBlockIdxFromTextIdxTest()
{
var thisPath = Path.GetDirectoryName(GetThisFilePath());
Assert.IsTrue(_disassembler.Initialize(
File.ReadAllText(Path.Combine(thisPath, "burstTestTarget.txt")),
BurstDisassembler.AsmKind.Intel,
false,
false));
for (int blockIdx = 0; blockIdx < _disassembler.Blocks.Count; blockIdx++)
{
int blockStart = 0;
for (int i = 0; i < blockIdx; i++)
{
blockStart += _disassembler.GetOrRenderBlockToText(i).Length;
}
var blockStr = _disassembler.GetOrRenderBlockToText(blockIdx);
int blockEnd = blockStart + blockStr.Length - 1;
Assert.AreEqual((blockIdx, blockStart, blockEnd),
_disassembler.GetBlockIdxFromTextIdx(blockStart + 1),
$"Block index was wrong for block with label {blockStr.Substring(0, blockStr.IndexOf('\n'))}");
}
}
[Test]
public void InstantiateRegistersUsedTest()
{
Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
var regsUsed = _disassembler._registersUsedAtLine;
// Match against expected:
var expectedLines = from l in expected select l.lineNr;
var failed = expectedLines.Except(regsUsed._linesRegisters.Keys);
failed = failed.Concat(regsUsed._linesRegisters.Keys.Except(expectedLines)).Distinct();
if (failed.Any())
{
// Not exact match
foreach (var f in failed)
{
Debug.Log($"lineNumber {f} failed");
}
Assert.Fail();
}
}
[Test]
public void CleanRegisterListTest()
{
Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
var regs = new List<string> { "rcx", "ecx", "rax" };
var output = _disassembler._registersUsedAtLine.CleanRegs(regs);
var expected = new List<string> { "rcx", "rax" };
Assert.AreEqual(output, expected);
}
[Test]
public void IndexOfRegisterTest()
{
var assembly =
"\n" +
" nop\n" +
" movsxd rcx, cx\n" +
" mov rax, qword ptr [rbp - 16]";
Assert.IsTrue(_disassembler.Initialize(assembly, BurstDisassembler.AsmKind.Intel));
string[,] regs =
{
{ "rcx", "cx" },
{ "rax", "rbp" }
};
string[] lines =
{
" movsxd rcx, cx\n",
" mov rax, qword ptr [rbp - 16]"
};
for (var i = 0; i < 2; i++)
{
var line = lines[i];
var reg = regs[i, 0];
var asmLine = _disassembler.Lines[i+1];
var output = _disassembler.GetRegisterTokenIndex(asmLine, reg);
var regIdx = _disassembler.Tokens[output].AlignedPosition - _disassembler.Tokens[asmLine.TokenIndex].AlignedPosition;
var expected = line.IndexOf(reg) + 1;
Assert.AreEqual(expected, regIdx, $"Failed for line \"{line}\"");
reg = regs[i, 1];
output = _disassembler.GetRegisterTokenIndex(asmLine, reg, output + 1);
regIdx = _disassembler.Tokens[output].AlignedPosition - _disassembler.Tokens[asmLine.TokenIndex].AlignedPosition;
expected = line.IndexOf(reg, expected + 1) + 1;
Assert.AreEqual(expected, regIdx, $"Failed for line \"{line}\"");
}
}
[Test]
[TestCase("x86", new [] {"rdx","edx","dx","dl"}, "dl")]
[TestCase("arm", new [] {"wsp", "sp"},"sp")]
[TestCase("arm", new [] {"v0.2d", "s0", "q0", "h0", "d0", "b0"}, "b0")]
[TestCase("arm", new [] {"w0","x0"}, "x0")]
public void RegisterEqualityTest(string assemblyName, string[] assemblyLine, string register)
{
BurstDisassembler.AsmTokenKindProvider tokenProvider = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
if (assemblyName == "x86")
{
tokenProvider = BurstDisassembler.X86AsmTokenKindProvider.Instance;
}
foreach (var reg in assemblyLine)
{
Assert.IsTrue(tokenProvider.RegisterEqual(reg, register), $"{reg} == {register}");
}
// Some special cases:
tokenProvider = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
Assert.IsFalse(tokenProvider.RegisterEqual("w8", "x0"), $"w8 != x0");
Assert.IsFalse(tokenProvider.RegisterEqual("w0", "q0"), "w0 != q0");
Assert.IsFalse(tokenProvider.RegisterEqual("x0", "q0"), "x0 != q0");
}
[Test]
public void RegisterEqualTest()
{
// Only tests for x86, as the others are trivial.
Assert.IsTrue(_disassembler.Initialize(simpleAssembly, BurstDisassembler.AsmKind.Intel));
// Get all register strings:
var tokenProvider = BurstDisassembler.X86AsmTokenKindProvider.Instance;
var tokenProviderT = typeof(BurstDisassembler.AsmTokenKindProvider);
var field = tokenProviderT.GetField("_tokenKinds",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(field, "Could not find _tokenKinds field in AsmTokenKindProvider");
var allTokens = (Dictionary<StringSlice, BurstDisassembler.AsmTokenKind>)field.GetValue(tokenProvider);
var tokensToTest =
from tok in allTokens.Keys
where allTokens.TryGetValue(tok, out var kind)
&& kind == BurstDisassembler.AsmTokenKind.Register
select tok.ToString();
// Test that equality works for all registers:
try
{
foreach (var reg in tokensToTest)
{
// Simply check whether all registers are processable:
tokenProvider.RegisterEqual(reg, "rax");
}
}
catch (Exception e)
{
Assert.Fail($"Not all registers works for register equality (x86). {e}");
}
}
[Test]
public void InstructionAlignmentTest()
{
var assembly =
"\n" + // newline as BurstDisassembler ignores first line
" push rbp\n" +
" .seh_pushreg rbp\n" +
" sub rsp, 32\n";
(int, char)[] expectedPositions =
{
(1,' '), (10, 'p'), (14, ' '), (24, 'r'), (27, '\n'),
(28, ' '), (37, '.'), (49, ' '), (50, 'r'), (53, '\n'),
(54, ' '), (63, 's'), (66, ' '), (77, 'r'), (80, ','), (82, '3'), (84, '\n')
};
Assert.IsTrue(_disassembler.Initialize(assembly, BurstDisassembler.AsmKind.Intel));
var builder = new StringBuilder();
for (int i = 0; i < _disassembler.Blocks.Count; i++)
{
var text = _disassembler.GetOrRenderBlockToTextUncached(i, false);
builder.Append(text);
}
var output = builder.ToString();
for (var i = 0; i < expectedPositions.Length; i++)
{
Assert.AreEqual(expectedPositions[i].Item1, _disassembler.Tokens[i].AlignedPosition);
}
foreach (var (idx, c) in expectedPositions)
{
// -1 as token index for some reason aren't zero indexed.
Assert.AreEqual(c, output[idx-1], $"Token position for index {idx} was wrong.");
}
}
[Test]
public void X86AsmTokenProviderSimdKindTest()
{
var tp = BurstDisassembler.X86AsmTokenKindProvider.Instance;
BurstDisassembler.SIMDkind actual = tp.SimdKind(new StringSlice("vsqrtsd"));
var expected = BurstDisassembler.SIMDkind.Scalar;
Assert.AreEqual(expected, actual);
actual = tp.SimdKind(new StringSlice("vroundpd"));
expected = BurstDisassembler.SIMDkind.Packed;
Assert.AreEqual(expected, actual);
actual = tp.SimdKind(new StringSlice("xsaves"));
expected = BurstDisassembler.SIMDkind.Infrastructure;
Assert.AreEqual(expected,actual);
}
[Test]
public void ARMAsmTokenProviderSimdKindTest()
{
var tp = BurstDisassembler.ARM64AsmTokenKindProvider.Instance;
BurstDisassembler.SIMDkind actual = tp.SimdKind(new StringSlice("vaddw"));
var expected = BurstDisassembler.SIMDkind.Scalar;
Assert.AreEqual(expected, actual);
actual = tp.SimdKind(new StringSlice("vadd.i8"));
expected = BurstDisassembler.SIMDkind.Packed;
Assert.AreEqual(expected, actual);
}
private string GetFirstColorTag(string line)
{
const string colorTag = "#XXXXXX";
const string tag = "<color=";
int idx = line.IndexOf('<');
return line.Substring(idx + tag.Length, colorTag.Length);
}
private const string ARMsimdAssembly =
"\n" +
" ldr r0, [sp, #12]\n" +
" vldr s0, [sp, #20]\n" +
" vstr s0, [sp, #4]\n" +
" ldr r1, [sp, #24]\n" +
" vldr s0, [sp, #4]\n" +
" vmov s2, r0\n" +
" vadd.f32 s0, s0, s2\n" +
" vstr s0, [sp, #20]";
private const string X86SimdAssembly =
"\n" +
" mov rcx, qword ptr [rbp - 32]\n" +
" vmovss xmm0, dword ptr [rbp - 12]\n" +
" vmovss dword ptr [rbp - 40], xmm0\n" +
" mov edx, dword ptr [rbp - 8]\n" +
" call \"Unity.Collections.NativeArray`1<float>.get_Item(Unity.Collections.NativeArray`1<float>* this, int index) -> float_c303f72c9cc472e2ef84a442ead69ef2 from Unity.Burst.Editor.Tests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\"\n" +
" vmovaps xmm1, xmm0\n" +
" vmovss xmm0, dword ptr [rbp - 40]\n" +
" vaddss xmm0, xmm0, xmm1\n" +
" vmovss dword ptr [rbp - 12], xmm0\n" +
" vzeroall";
[Test]
[TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMDScalar, 1)]
[TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMDPacked, 5)]
[TestCase(X86SimdAssembly, 0, BurstDisassembler.DarkColorInstructionSIMD, 9)]
[TestCase(ARMsimdAssembly, 1, BurstDisassembler.DarkColorInstructionSIMDScalar, 1)]
[TestCase(ARMsimdAssembly, 1, BurstDisassembler.DarkColorInstructionSIMDPacked, 6)]
public void AssemblyColouringSmellTest(string asm, int asmkind, string colorTag, int lineIdx)
{
_disassembler.Initialize(asm, (BurstDisassembler.AsmKind)asmkind, true, true, true);
var line = _disassembler.Lines[lineIdx];
_disassembler._output.Clear();
_disassembler.RenderLine(ref line, true);
var lineString = _disassembler._output.ToString();
_disassembler._output.Clear();
Assert.AreEqual(colorTag, GetFirstColorTag(lineString));
}
private List<(int lineNr, List<string>)> expected = new List<(int lineNr, List<string>)>
{
(2, new List<string> { "rbp" }),
(3, new List<string> { "rbp" }),
(4, new List<string> { "rsp" }),
(6, new List<string> { "rbp", "rsp" }),
(7, new List<string> { "rbp" }),
(11, new List<string> { "rsp" }),
(12, new List<string> { "rbp" }),
(26, new List<string> { "rbp" }),
(27, new List<string> { "rbp" }),
(28, new List<string> { "rsp" }),
(30, new List<string> { "rbp", "rsp" }),
(31, new List<string> { "rbp" }),
(36, new List<string> { "rsp" }),
(37, new List<string> { "rbp" }),
};
private string simpleAssembly =
"\n" + // newline as BurstDisassembler ignores first line
".Lfunc_begin0:\n" +
".seh_proc 589a9d678dbb1201e550a054238fad11\n" +
" push rbp\n" +
" .seh_pushreg rbp\n" +
" sub rsp, 32\n" +
" .seh_stackalloc 32\n" +
" lea rbp, [rsp + 32]\n" +
" .seh_setframe rbp, 32\n" +
" .seh_endprologue\n" +
" call A.B.DoIt\n" +
" nop\n" +
" add rsp, 32\n" +
" pop rbp\n" +
" ret\n" +
" .Lfunc_end0:\n" +
" .seh_endproc\n" +
" \n" +
" .def burst.initialize;\n" +
" .scl 2;\n" +
" .type 32;\n" +
" .endef\n" +
" .globl burst.initialize\n" +
" .p2align 4, 0x90\n" +
" burst.initialize:\n" +
" .Lfunc_begin1:\n" +
" .seh_proc burst.initialize\n" +
" push rbp\n" +
" .seh_pushreg rbp\n" +
" sub rsp, 32\n" +
" .seh_stackalloc 32\n" +
" lea rbp, [rsp + 32]\n" +
" .seh_setframe rbp, 32\n" +
" .seh_endprologue\n" +
" call burst.initialize.externals\n" +
" call burst.initialize.statics\n" +
" nop\n" +
" add rsp, 32\n" +
" pop rbp\n" +
" ret\n" +
" .Lfunc_end1:\n" +
" .seh_endproc\n" +
" \n" +
" .def burst.initialize.externals;\n" +
" .scl 2;\n" +
" .type 32;\n" +
" .endef\n" +
" .globl burst.initialize.externals\n" +
" .p2align 4, 0x90";
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35ff2f449e6f335cb4f69ebb36ecf3e5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,588 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Burst.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Collections;
using Unity.Burst;
using Unity.Jobs;
[TestFixture]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor)]
public class BurstInspectorGUITests
{
private readonly WaitUntil _waitForInitialized =
new WaitUntil(() => EditorWindow.GetWindow<BurstInspectorGUI>()._initialized);
private IEnumerator SelectJobAwaitLoad(string assemblyName)
{
EditorWindow.GetWindow<BurstInspectorGUI>()._treeView.TrySelectByDisplayName(assemblyName);
return new WaitUntil(() =>
EditorWindow.GetWindow<BurstInspectorGUI>()._textArea.IsTextSet(assemblyName)
);
}
[UnitySetUp]
public IEnumerator SetUp()
{
// Close down window if it's open, to start with a fresh inspector.
EditorWindow.GetWindow<BurstInspectorGUI>().Close();
EditorWindow.GetWindow<BurstInspectorGUI>().Show();
// Make sure window is actually initialized before continuing.
yield return _waitForInitialized;
}
[UnityTest]
public IEnumerator TestInspectorOpenDuringDomainReloadDoesNotLogErrors()
{
// Show Inspector window
EditorWindow.GetWindow<BurstInspectorGUI>().Show();
Assert.IsTrue(EditorWindow.HasOpenInstances<BurstInspectorGUI>());
// Ask for domain reload
EditorUtility.RequestScriptReload();
// Wait for the domain reload to be completed
yield return new WaitForDomainReload();
Assert.IsTrue(EditorWindow.HasOpenInstances<BurstInspectorGUI>());
// Hide Inspector window
EditorWindow.GetWindow<BurstInspectorGUI>().Close();
Assert.IsFalse(EditorWindow.HasOpenInstances<BurstInspectorGUI>());
}
[UnityTest]
public IEnumerator DisassemblerNotChangingUnexpectedlyTest()
{
const string jobName2 = "BurstReflectionTests.MyJob - (IJob)";
const string jobName = "BurstInspectorGUITests.MyJob - (IJob)";
// Selecting a specific assembly.
yield return SelectJobAwaitLoad(jobName);
var window = EditorWindow.GetWindow<BurstInspectorGUI>();
try
{
// Sending event to set the displayname, to avoid it resetting _scrollPos because of target change.
window.SendEvent(new Event()
{
type = EventType.Repaint,
mousePosition = new Vector2(window.position.width / 2f, window.position.height / 2f)
});
yield return null;
// Doing actual test work:
var prev = new BurstDisassemblerWithCopy(window._burstDisassembler);
window.SendEvent(new Event()
{
type = EventType.Repaint,
mousePosition = new Vector2(window.position.width / 2f, window.position.height / 2f)
});
yield return null;
Assert.IsTrue(prev.Equals(window._burstDisassembler),
"Public fields changed in burstDisassembler even though they shouldn't");
prev = new BurstDisassemblerWithCopy(window._burstDisassembler);
window.SendEvent(new Event() { type = EventType.MouseUp, mousePosition = Vector2.zero });
yield return null;
Assert.IsTrue(prev.Equals(window._burstDisassembler),
"Public fields changed in burstDisassembler even though they shouldn't");
prev = new BurstDisassemblerWithCopy(window._burstDisassembler);
yield return SelectJobAwaitLoad(jobName2);
window = EditorWindow.GetWindow<BurstInspectorGUI>();
window.SendEvent(new Event()
{
type = EventType.Repaint,
mousePosition = new Vector2(window.position.width / 2f, window.position.height / 2f)
});
yield return null;
Assert.IsFalse(prev.Equals(window._burstDisassembler), "Public fields of burstDisassembler did not change");
}
finally
{
window.Close();
}
}
[UnityTest]
public IEnumerator InspectorStallingLoadTest()
{
// Error was triggered by selecting a display name, filtering it out, and then doing a script recompilation.
yield return SelectJobAwaitLoad("BurstInspectorGUITests.MyJob - (IJob)");
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
win._searchFieldJobs.SetFocus();
yield return null;
// Simulate event for sending "a" as it will filter out the chosen job.
win.SendEvent(Event.KeyboardEvent("a"));
yield return null;
// Send RequestScriptReload to try and trigger the bug
// and wait for it to return
EditorUtility.RequestScriptReload();
yield return new WaitForDomainReload();
win = EditorWindow.GetWindow<BurstInspectorGUI>();
// Wait for it to actually initialize.
yield return _waitForInitialized;
Assert.IsTrue(win._initialized, "BurstInspector did not initialize properly after script reload");
win.Close();
}
[UnityTest]
public IEnumerator FontStyleDuringDomainReloadTest()
{
// Enter play mod
yield return new EnterPlayMode();
// Exit play mode
yield return new ExitPlayMode();
// Wait for the inspector to actually reload
yield return _waitForInitialized;
var inspectorWindow = EditorWindow.GetWindow<BurstInspectorGUI>();
#if UNITY_2023_1_OR_NEWER
Assert.AreEqual("RobotoMono-Regular", inspectorWindow._font.name);
#else
if (Application.platform == RuntimePlatform.WindowsEditor)
{
Assert.AreEqual("Consolas", inspectorWindow._font.name);
}
else
{
Assert.AreEqual("Courier", inspectorWindow._font.name);
}
#endif
inspectorWindow.Close();
}
[UnityTest]
public IEnumerator BranchHoverTest()
{
const string jobName = "BurstInspectorGUITests.MyJob - (IJob)";
yield return SelectJobAwaitLoad(jobName);
var info = SetupBranchTest();
var window = EditorWindow.GetWindow<BurstInspectorGUI>();
window.SendEvent(new Event() { type = EventType.MouseUp, mousePosition = info.mousePos });
var branch = window._textArea.hoveredBranch;
yield return null;
// Close window to avoid it sending more events
window.Close();
Assert.AreNotEqual(branch, default(LongTextArea.Branch), "Mouse is not hovering any branch.");
Assert.AreEqual(info.blockIdx.src, branch.Edge.OriginRef.BlockIndex);
Assert.AreEqual(info.blockIdx.dst, branch.Edge.LineRef.BlockIndex);
}
[UnityTest]
public IEnumerator ClickBranchTest()
{
const string jobName = "BurstInspectorGUITests.MyJob - (IJob)";
yield return SelectJobAwaitLoad(jobName);
var info = SetupBranchTest();
var window = EditorWindow.GetWindow<BurstInspectorGUI>();
// Seeing if clicking the branch takes us to a spot where branch is still hovered.
window.SendEvent(new Event() { type = EventType.MouseDown, mousePosition = info.mousePos });
var branch = window._textArea.hoveredBranch;
yield return null;
Assert.AreNotEqual(branch, default(LongTextArea.Branch), "Mouse is not hovering any branch.");
Assert.AreEqual(info.blockIdx.src, branch.Edge.OriginRef.BlockIndex);
Assert.AreEqual(info.blockIdx.dst, branch.Edge.LineRef.BlockIndex);
// Going back again.
window.SendEvent(new Event() { type = EventType.MouseDown, mousePosition = info.mousePos });
var branch2 = window._textArea.hoveredBranch;
yield return null;
Assert.AreNotEqual(branch2, default(LongTextArea.Branch), "Mouse is not hovering any branch.");
Assert.AreEqual(info.blockIdx.src, branch2.Edge.OriginRef.BlockIndex);
Assert.AreEqual(info.blockIdx.dst, branch2.Edge.LineRef.BlockIndex);
// Close window to avoid it sending more events.
window.Close();
}
private struct InfoThingy
{
public (int src, int dst) blockIdx;
public Vector2 mousePos;
}
private InfoThingy SetupBranchTest()
{
var window = EditorWindow.GetWindow<BurstInspectorGUI>();
// Make sure we use fontSize 12:
window.fontSizeIndex = 4;
window._textArea.Invalidate();
window.fixedFontStyle = null;
// Force window size to actually show branch arrows.
window.position = new Rect(window.position.x, window.position.y, 390, 405);
// Sending event to set the displayname, to avoid it resetting _scrollPos because of target change.
// Sending two events as initial guess for buttonbar width might be off, and it will be a precise calculation after second event.
window.SendEvent(new Event() { type = EventType.Repaint, mousePosition = new Vector2(window.position.width / 2f, window.position.height / 2f) });
window.SendEvent(new Event() { type = EventType.Repaint, mousePosition = new Vector2(window.position.width / 2f, window.position.height / 2f) });
// Setting up for the test.
// Finding an edge:
int dstBlockIdx = -1;
int srcBlockIdx = -1;
int line = -1;
for (int idx = 0; idx < window._burstDisassembler.Blocks.Count; idx++)
{
var block = window._burstDisassembler.Blocks[idx];
if (block.Edges != null)
{
foreach (var edge in block.Edges)
{
if (edge.Kind == BurstDisassembler.AsmEdgeKind.OutBound)
{
dstBlockIdx = edge.LineRef.BlockIndex;
line = window._textArea.blockLine[dstBlockIdx];
if ((dstBlockIdx == idx + 1 && edge.LineRef.LineIndex == 0)) // pointing to next line
{
continue;
}
srcBlockIdx = idx;
break;
}
}
if (srcBlockIdx != -1)
{
break;
}
}
}
if (srcBlockIdx == -1)
{
window.Close();
throw new System.Exception("No edges present in assembly for \"BurstInspectorGUITests.MyJob - (IJob)\"");
}
float dist = window._textArea.fontHeight * line;
float x = (window.position.width - (window._inspectorView.width + BurstInspectorGUI._scrollbarThickness)) + window._textArea.horizontalPad - (2*window._textArea.fontWidth);
// setting _ScrollPos so end of arrow is at bottom of screen, to make sure there is actually room for the scrolling.
window._scrollPos = new Vector2(0, dist - window._inspectorView.height * 0.93f);
// Setting mousePos to bottom of inspector view.
float topOfInspectorToBranchArrow = window._buttonOverlapInspectorView + 66.5f;//66.5f is the size of space over the treeview of different jobs.
var mousePos = new Vector2(x, topOfInspectorToBranchArrow + window._inspectorView.height - 0.5f*window._textArea.fontHeight);
return new InfoThingy() { blockIdx = (srcBlockIdx, dstBlockIdx), mousePos = mousePos};
}
public static IEnumerable ValueSource
{
get
{
yield return "BurstInspectorGUITests.MyJob - (IJob)";
yield return "BurstReflectionTests.GenericType`1.NestedGeneric`1[System.Int32,System.Single].TestMethod3()";
yield return "BurstReflectionTests.GenericType`1.NestedNonGeneric[System.Int32].TestMethod2()";
yield return "BurstReflectionTests.GenericParallelForJob`1[System.Int32] - (IJobParallelFor)";
}
}
[UnityTest]
public IEnumerator FocusCodeTest([ValueSource(nameof(ValueSource))] string job)
{
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
yield return SelectJobAwaitLoad(job);
// Doesn't check that it's at the right spot, simply that it actually moves
Assert.IsFalse(Mathf.Approximately(win._inspectorView.y, 0f), "Inspector view did not change");
win.Close();
}
public static IEnumerable FocusCodeNotBranchesSource
{
get
{
yield return (1000, false);
yield return (563, true);
}
}
[UnityTest]
public IEnumerator FocusCodeNotBranchesTest([ValueSource(nameof(FocusCodeNotBranchesSource))] (int, bool) input)
{
var (width, doFocus) = input;
const string case1 = "BurstInspectorGUITests.BranchArrows - (IJob)";
const string case2 = "BurstInspectorGUITests.BranchArrows2 - (IJob)";
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
// Force window size to be small enough for it to position it more to the right.
win.position = new Rect(win.position.x, win.position.y, width, 405);
// Test one where it should focus.
yield return SelectJobAwaitLoad(case1);
var val1 = win._inspectorView.x;
var result1 = Mathf.Approximately(val1, 0f);
// Test two with no focus.
win._assemblyKind = BurstInspectorGUI.AssemblyOptions.PlainWithDebugInformation;
yield return SelectJobAwaitLoad(case2);
var val2 = win._inspectorView.x;
var result2 = Mathf.Approximately(val2, 0f);
// Cleanup and test assertions.
win.Close();
Assert.AreEqual(doFocus, result1 == doFocus, $"Inspector view wrong.");
//Assert.IsFalse(result1, $"Inspector view did not change (Is {val1}).");
Assert.IsTrue(result2, $"Inspector view changed unexpectedly (Is {val2}).");
}
[UnityTest]
public IEnumerator SelectionNotOutsideBoundsTest()
{
void MoveSelection(BurstInspectorGUI gui, LongTextArea.Direction dir)
{
switch (dir)
{
case LongTextArea.Direction.Down:
gui._textArea.SelectAll();
gui._textArea.MoveSelectionDown(gui._inspectorView, true);
break;
case LongTextArea.Direction.Right:
gui._textArea.SelectAll();
gui._textArea.MoveSelectionRight(gui._inspectorView, true);
break;
case LongTextArea.Direction.Left:
gui._textArea.selectDragPos = Vector2.zero;
gui._textArea.MoveSelectionLeft(gui._inspectorView, true);
break;
case LongTextArea.Direction.Up:
gui._textArea.selectDragPos = Vector2.zero;
gui._textArea.MoveSelectionUp(gui._inspectorView, true);
break;
}
}
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
yield return SelectJobAwaitLoad("BurstInspectorGUITests.MyJob - (IJob)");
try
{
foreach (var dir in Enum.GetValues(typeof(LongTextArea.Direction)))
{
MoveSelection(win, (LongTextArea.Direction)dir);
yield return null;
// Check that no errors have happened.
LogAssert.NoUnexpectedReceived();
}
}
finally
{
win.Close();
}
}
[UnityTest]
public IEnumerator SelectionInAssemblySearchBarTest()
{
yield return SelectJobAwaitLoad("BurstInspectorGUITests.MyJob - (IJob)");
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
win._searchFieldAssembly.SetFocus();
yield return null;
// Send events to input some text.
win.SendEvent(Event.KeyboardEvent("a"));
win.SendEvent(Event.KeyboardEvent("b"));
yield return null;
// Move select some using keyboard input
win.SendEvent(Event.KeyboardEvent("left"));
win.SendEvent(Event.KeyboardEvent("#right"));
yield return null;
// Do a copy
var savedClipBoard = EditorGUIUtility.systemCopyBuffer;
win.SendEvent(SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX
? Event.KeyboardEvent("%c")
: Event.KeyboardEvent("^c"));
yield return null;
var copiedText = EditorGUIUtility.systemCopyBuffer;
EditorGUIUtility.systemCopyBuffer = savedClipBoard;
// Check that all is good
win.Close();
Assert.AreEqual("b", copiedText, "Copied text did not match expectation.");
}
[UnityTest]
public IEnumerator GoToNextSearchTargetTest()
{
var active = -1;
var nextActive = -1;
yield return SelectJobAwaitLoad("BurstInspectorGUITests.MyJob - (IJob)");
var win = EditorWindow.GetWindow<BurstInspectorGUI>();
try
{
win._searchFieldAssembly.SetFocus();
yield return null;
// Do a search in the text.
win.SendEvent(Event.KeyboardEvent("p"));
win.SendEvent(Event.KeyboardEvent("u"));
win.SendEvent(Event.KeyboardEvent("return"));
yield return null;
active = win._textArea._activeSearchHitIdx;
// Select next search target.
win.SendEvent(Event.KeyboardEvent("return"));
yield return null;
nextActive = win._textArea._activeSearchHitIdx;
}
finally
{
win.Close();
}
Assert.AreNotEqual(active, nextActive, "Active search target was not changed.");
}
[BurstCompile]
private struct MyJob : IJob
{
[ReadOnly]
public NativeArray<float> Inpút;
[WriteOnly]
public NativeArray<float> Output;
public void Execute()
{
float result = 0.0f;
for (int i = 0; i < Inpút.Length; i++)
{
result += Inpút[i];
}
Output[0] = result;
}
}
[BurstCompile]
private struct BranchArrows : IJob
{
[ReadOnly]
public NativeArray<float> Inpút;
[WriteOnly]
public NativeArray<float> Output;
public void Execute()
{
float result = 0.0f;
for (int i = 0; i < Inpút.Length; i++)
{
if (Inpút[i] < 10) { result += 1; }
else if (Inpút[i] < 20) { result += 2; }
else if (Inpút[i] < 30) { result += 3; }
else if (Inpút[i] < 40) { result += 4; }
else if (Inpút[i] < 50) { result += 5; }
else if (Inpút[i] < 60) { result += 6; }
else if (Inpút[i] < 70) { result += 7; }
else if (Inpút[i] < 80) { result += 8; }
else if (Inpút[i] < 90) { result += 9; }
result += Inpút[i];
}
Output[0] = result;
}
}
[BurstCompile]
private struct BranchArrows2 : IJob
{
[ReadOnly]
public NativeArray<float> Inpút;
[WriteOnly]
public NativeArray<float> Output;
public void Execute()
{
float result = 0.0f;
for (int i = 0; i < Inpút.Length; i++)
{
if (Inpút[i] < 10) { result += 1; }
else if (Inpút[i] < 20) { result += 2; }
else if (Inpút[i] < 30) { result += 3; }
else if (Inpút[i] < 40) { result += 4; }
else if (Inpút[i] < 50) { result += 5; }
else if (Inpút[i] < 60) { result += 6; }
else if (Inpút[i] < 70) { result += 7; }
else if (Inpút[i] < 80) { result += 8; }
else if (Inpút[i] < 90) { result += 9; }
result += Inpút[i];
}
Output[0] = result;
}
}
private class BurstDisassemblerWithCopy : BurstDisassembler
{
public List<AsmBlock> BlocksCopy;
public bool IsColoredCopy;
public List<AsmLine> LinesCopy;
public List<AsmToken> TokensCopy;
public BurstDisassemblerWithCopy(BurstDisassembler disassembler) : base()
{
IsColoredCopy = disassembler.IsColored;
BlocksCopy = new List<AsmBlock>(disassembler.Blocks);
LinesCopy = new List<AsmLine>(disassembler.Lines);
TokensCopy = new List<AsmToken>(disassembler.Tokens);
}
public bool Equals(BurstDisassembler other)
{
return IsColoredCopy == other.IsColored
&& BlocksCopy.SequenceEqual(other.Blocks)
&& LinesCopy.SequenceEqual(other.Lines)
&& TokensCopy.SequenceEqual(other.Tokens);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c2988be6da139988b149bb3b60dc9ca
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
using NUnit.Framework;
using Unity.Burst.Editor;
public class BurstMathTests
{
[Test]
[TestCase(1f, 3f, 3f, true)]
[TestCase(1f, 3f, 2f, true)]
[TestCase(1f, 3f, 3.00001f, false)]
public void WithinRangeTest(float start, float end, float num, bool res)
{
Assert.That(BurstMath.WithinRange(start, end, num), Is.EqualTo(res));
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a647d1cecc83c4eaf9750af7ae6a1d0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,280 @@
using System;
using NUnit.Framework;
using Unity.Burst.Editor;
using Unity.Burst;
using Unity.Jobs;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;
public class BurstMethodTreeViewTests
{
/*
Consists of a tree looking like:
Root
BurstMethodTreeViewTests
Job1
TestMethod1
Job2
TestMethod1
Job3
TestMethod1(System.IntPtr)
Job4
TestMethod1
Job5 - (IJob)
*/
private BurstMethodTreeView _treeView;
private List<BurstCompileTarget> _targets;
private string _filter;
[SetUp]
public void SetUp()
{
_filter = string.Empty;
_treeView = new BurstMethodTreeView(
new TreeViewState(),
() => _filter,
() => (true, true)
);
string name = "TestMethod1";
_targets = new List<BurstCompileTarget>
{
new BurstCompileTarget(typeof(Job1).GetMethod(name), typeof(Job1), null, true),
new BurstCompileTarget(typeof(Job2).GetMethod(name), typeof(Job2), null, true),
new BurstCompileTarget(typeof(Job3).GetMethod(name), typeof(Job3), null, true),
new BurstCompileTarget(typeof(Job4).GetMethod(name), typeof(Job4), null, true),
new BurstCompileTarget(typeof(Job5).GetMethod("Execute"), typeof(Job5), typeof(IJob), false),
};
}
private void testEquality<T>(List<T> exp, List<T> act)
{
Assert.AreEqual(exp.Count, act.Count, "List length did not match.");
if (exp is List<TreeViewItem> elist && act is List<TreeViewItem> alist)
{
for (int i = 0; i < act.Count; i++)
{
Assert.IsTrue(alist[i].CompareTo(elist[i]) == 0,
$"expected: {elist[i].displayName}\nactual: {alist[i].displayName}");
}
}
else
{
for (int i = 0; i < act.Count; i++)
{
Assert.AreEqual(exp[i], act[i], $"list items did not match at index {i}");
}
}
}
[Test]
public void ProcessNewItemTest()
{
// Test for method containing . in name:
List<StringSlice> oldNameSpace = new List<StringSlice>();
int idJ = 0;
var (idn, newtarget, nameSpace) =
BurstMethodTreeView.ProcessNewItem(0, ++idJ, _targets[2], oldNameSpace);
Assert.AreEqual(-2, idn);
var expTargets = new List<TreeViewItem>
{
new TreeViewItem(-1, 0, $"{nameof(BurstMethodTreeViewTests)}"),
new TreeViewItem(-2,1,$"{nameof(Job3)}"),
new TreeViewItem(1, 2, "TestMethod1(System.IntPtr)")
};
var expNS = new List<StringSlice>
{
new StringSlice($"{nameof(BurstMethodTreeViewTests)}"),
new StringSlice($"{nameof(Job3)}")
};
testEquality(expTargets, newtarget);
testEquality(expNS, nameSpace);
// Test for method with . and with thing in namespace:
(idn, newtarget, nameSpace) = BurstMethodTreeView.ProcessNewItem(idn, ++idJ, _targets[2], nameSpace);
Assert.AreEqual(-2, idn); // no new non-leafs added.
expTargets = new List<TreeViewItem>
{
new TreeViewItem(2, 2, "TestMethod1(System.IntPtr)")
};
testEquality(expTargets, newtarget);
testEquality(expNS, nameSpace);
// Test with IJob instead of static method:
(idn, newtarget, nameSpace) = BurstMethodTreeView.ProcessNewItem(0, ++idJ, _targets[4], oldNameSpace);
Assert.AreEqual(-1, idn); // no new non-leafs added.
expTargets = new List<TreeViewItem>
{
new TreeViewItem(-1, 0, $"{nameof(BurstMethodTreeViewTests)}"),
new TreeViewItem(2, 2, $"{nameof(Job5)} - ({nameof(IJob)})")
};
expNS = new List<StringSlice> { new StringSlice($"{nameof(BurstMethodTreeViewTests)}"), };
testEquality(expTargets, newtarget);
testEquality(expNS, nameSpace);
}
private readonly (string, string[])[] _findNameSpacesTestInput =
{
("Burst.Compiler.IL.Tests.TestGenerics+GenericStructOuter2`2+GenericStructInner`1[[Burst.Compiler.IL.Tests.TestGenerics+MyValueData1, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Burst.Compiler.IL.Tests.TestGenerics+MyValueData2, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Burst.Compiler.IL.Tests.TestGenerics+MyValueData2, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]",
new []{ "Burst.Compiler.IL.Tests.TestGenerics", "GenericStructOuter2`2" })
};
[Test]
public void ExtractNameSpacesTest()
{
foreach (var (displayName, expectedNameSpaces) in _findNameSpacesTestInput)
{
var (nameSpaces, methodNameIdx) = BurstMethodTreeView.ExtractNameSpaces(displayName);
Assert.AreEqual(2, nameSpaces.Count, "Amount of namespaces found is wrong.");
int len = expectedNameSpaces.Length;
int expectedNSIdx = 0;
for (int i = 0; i < len; i++)
{
expectedNSIdx += expectedNameSpaces[i].Length + 1;
Assert.AreEqual(expectedNameSpaces[i], nameSpaces[i].ToString(), "Wrong namespace name retrieval.");
}
Assert.AreEqual(expectedNSIdx, methodNameIdx, "Wrong index of method name.");
}
}
[Test]
public void InitializeTest()
{
const int numNodes = 1 + 5; // Root + internal nodes.
_treeView.Initialize(_targets, false);
Assert.AreEqual(numNodes, _treeView.GetExpanded().Count, "All menu items should be expanded");
_treeView.SetExpanded(-2, false);
Assert.AreEqual(numNodes-1, _treeView.GetExpanded().Count, "First Job should be folded.");
_treeView.Initialize(_targets, true);
Assert.AreEqual(numNodes-1, _treeView.GetExpanded().Count);
}
[Test]
public void BuildRootTest()
{
_treeView.Initialize(_targets, false);
int dexp = 0;
int idNexp = -1;
int idLexp = 1;
foreach (TreeViewItem twi in _treeView.GetRows())
{
Assert.AreEqual(dexp++, twi.depth, $"Depth of item \"{twi}\" was wrong.");
if (dexp > 2) { dexp = 1; }
Assert.AreEqual(twi.hasChildren ? idNexp-- : idLexp++, twi.id, $"ID of item \"{twi}\" was wrong.");
}
}
[Test]
public void GetSelection()
{
_treeView.Initialize(_targets, false);
IList<int> actual = _treeView.GetSelection();
Assert.IsEmpty(actual, "Selection count wrong.");
_treeView.SelectAllRows();
actual = _treeView.GetSelection();
Assert.IsEmpty(actual, "Selection count wrong. Multirow selection not allowed.");
_treeView.SetSelection(new List<int> { -2 });
actual = _treeView.GetSelection();
Assert.AreEqual(0, actual.Count, "Selection count wrong. Label selection not allowed.");
_treeView.SetSelection(new List<int> { 1 });
actual = _treeView.GetSelection();
Assert.AreEqual(1, actual.Count, "Selection count wrong.");
Assert.AreEqual(1, actual[0], "Selection ID wrong.");
}
[Test]
public void TrySelectByDisplayNameTest()
{
const string name = "BurstMethodTreeViewTests.Job1.TestMethod1()";
_treeView.Initialize(_targets, false);
Assert.IsFalse(_treeView.TrySelectByDisplayName("Not present"));
Assert.IsTrue(_treeView.TrySelectByDisplayName(name), "TreeView Could not find method.");
}
[Test]
public void ProcessEntireTestProject()
{
// Make it filter out some jobs:
_filter = "Unity";
// Get all target jobs!
var assemblyList = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
var result = BurstReflection.FindExecuteMethods(assemblyList, BurstReflectionAssemblyOptions.None).CompileTargets;
result.Sort((left, right) => string.Compare(left.GetDisplayName(), right.GetDisplayName(), StringComparison.Ordinal));
// Initialize the tree view:
_treeView.Initialize(result, false);
// Check if everything is good and ready:
var visibleJobs = _treeView.GetRows().Where(twi => !twi.hasChildren);
foreach (TreeViewItem twi in visibleJobs)
{
var actual = result[twi.id - 1];
var expected = twi.displayName;
Assert.IsTrue(actual.GetDisplayName().Contains(expected), $"Retrieved the wrong target base on id.\nGot \"{actual.GetDisplayName()}\"\nExpected \"{expected}\"");
}
}
[BurstCompile]
private struct Job1
{
[BurstCompile]
public static void TestMethod1() { }
}
[BurstCompile]
private struct Job2
{
[BurstCompile]
public static void TestMethod1() { }
}
[BurstCompile]
private struct Job3
{
[BurstCompile]
public static void TestMethod1(System.IntPtr ptr) { }
}
[BurstCompile]
private struct Job4
{
[BurstCompile]
public static void TestMethod1() { }
}
[BurstCompile]
private struct Job5 : IJob
{
public void Execute() { }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dec061e3cbd730ce8117c465ee02452c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,197 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Burst.Editor;
using Unity.Jobs;
// This concrete generic type is only referenced in this assembly-level attribute,
// not anywhere else in code. This is to test that such types can be picked up
// by BurstReflection.
[assembly: BurstReflectionTests.RegisterGenericJobType(typeof(BurstReflectionTests.GenericParallelForJob<int>))]
[TestFixture]
public class BurstReflectionTests
{
private List<System.Reflection.Assembly> _assemblies;
[OneTimeSetUp]
public void SetUp()
{
_assemblies = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
}
[Test]
public void CanGetAssemblyList()
{
Assert.That(_assemblies, Has.Count.GreaterThan(0));
}
[Test]
[TestCase("BurstReflectionTests.MyJob - (IJob)")]
[TestCase("BurstReflectionTests.MyGenericJob`1[System.Int32] - (IJob)")]
[TestCase("BurstReflectionTests.MyGenericJob2`1[System.Int32] - (BurstReflectionTests.IMyGenericJob`1[System.Int32])")]
[TestCase("BurstReflectionTests.MyGenericJob2`1[System.Double] - (BurstReflectionTests.IMyGenericJob`1[System.Double])")]
[TestCase("BurstReflectionTests.NonGenericType.TestMethod1()")]
[TestCase("BurstReflectionTests.GenericType`1[System.Int32].TestMethod1()")]
[TestCase("BurstReflectionTests.GenericType`1.NestedNonGeneric[System.Int32].TestMethod2()")]
[TestCase("BurstReflectionTests.GenericType`1.NestedGeneric`1[System.Int32,System.Single].TestMethod3()")]
[TestCase("BurstReflectionTests.MyGenericJobSeparateAssembly`1[System.Int32] - (BurstReflectionTestsSeparateAssembly.IMyGenericJobSeparateAssembly`1[System.Int32])")]
[TestCase("BurstReflectionTests.GenericParallelForJob`1[System.Int32] - (IJobParallelFor)")]
public void CanFindJobType(string compileTargetName)
{
var result = BurstReflection.FindExecuteMethods(_assemblies, BurstReflectionAssemblyOptions.None);
Assert.That(result.LogMessages, Is.Empty);
var compileTarget = result.CompileTargets.Find(x => x.GetDisplayName() == compileTargetName);
Assert.That(compileTarget, Is.Not.Null);
}
[BurstCompile]
private struct MyJob : IJob
{
public void Execute() { }
}
[BurstCompile]
private struct MyGenericJob<T> : IJob
{
public void Execute() { }
private static void UseConcreteType()
{
new MyGenericJob<int>().Execute();
}
}
[Unity.Jobs.LowLevel.Unsafe.JobProducerType(typeof(MyJobProducer<,>))]
private interface IMyGenericJob<T>
{
void Execute();
}
[BurstCompile]
private struct MyGenericJob2<T> : IMyGenericJob<T>
{
public void Execute() { }
private static void UseConcreteType()
{
new MyGenericJob2<int>().Execute();
}
}
private static class MyJobProducer<TJob, T>
{
public static void Execute(ref TJob job)
{
}
}
private struct MyGenericJob2Wrapper<T1, T2>
{
public MyGenericJob2<T2> Job;
private static void UseConcreteType()
{
var x = new MyGenericJob2Wrapper<float, double>();
x.Job.Execute();
}
}
[BurstCompile]
private struct NonGenericType
{
[BurstCompile]
public static void TestMethod1() { }
}
[BurstCompile]
private struct GenericType<T>
{
public static Action Delegate1;
public static Action Delegate2;
public static Action Delegate3;
[BurstCompile]
public static void TestMethod1() { }
[BurstCompile]
public class NestedNonGeneric
{
[BurstCompile]
public static void TestMethod2() { }
}
[BurstCompile]
public class NestedGeneric<T2>
{
[BurstCompile]
public static void TestMethod3() { }
}
private static void UseConcreteType()
{
// Store the delegates to static fields to avoid
// them being optimized-away in Release builds
Delegate1 = GenericType<int>.TestMethod1;
Delegate2 = GenericType<int>.NestedNonGeneric.TestMethod2;
Delegate3 = GenericType<int>.NestedGeneric<float>.TestMethod3;
}
}
[BurstCompile]
private struct MyGenericJobSeparateAssembly<T> : BurstReflectionTestsSeparateAssembly.IMyGenericJobSeparateAssembly<T>
{
public void Execute() { }
private static void UseConcreteType()
{
new MyGenericJobSeparateAssembly<int>().Execute();
}
}
[Test]
[TestCase("BurstReflectionTests.GenericMethodContainer.GenericMethod(T)")]
public void ExcludesGenericMethods(string compileTargetName)
{
var result = BurstReflection.FindExecuteMethods(_assemblies, BurstReflectionAssemblyOptions.None);
Assert.That(result.LogMessages, Is.Empty);
var compileTarget = result.CompileTargets.Find(x => x.GetDisplayName() == compileTargetName);
Assert.That(compileTarget, Is.Null);
}
[BurstCompile]
private static class GenericMethodContainer
{
[BurstCompile]
private static void GenericMethod<T>(T p)
{
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal class RegisterGenericJobTypeAttribute : Attribute
{
public Type ConcreteType;
public RegisterGenericJobTypeAttribute(Type type)
{
ConcreteType = type;
}
}
[BurstCompile]
internal struct GenericParallelForJob<T> : IJobParallelFor
where T : struct
{
public void Execute(int index)
{
throw new System.NotImplementedException();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fde1edfe51dc3deaa53f0952dddf2b02
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,131 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Unity.Burst;
using Unity.Burst.Editor;
public class BurstStringSearchTests
{
private BurstDisassembler GetDisassemblerandText(string compileTargetName, int debugLvl, out string textToRender)
{
// Get target job assembly:
var assemblies = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
var result = BurstReflection.FindExecuteMethods(assemblies, BurstReflectionAssemblyOptions.None);
var compileTarget = result.CompileTargets.Find(x => x.GetDisplayName() == compileTargetName);
Assert.IsTrue(compileTarget != default, $"Could not find compile target: {compileTarget}");
BurstDisassembler disassembler = new BurstDisassembler();
var options = new StringBuilder();
compileTarget.Options.TryGetOptions(compileTarget.JobType, true, out string defaultOptions);
options.AppendLine(defaultOptions);
// Disables the 2 current warnings generated from code (since they clutter up the inspector display)
// BC1370 - throw inside code not guarded with ConditionalSafetyCheck attribute
// BC1322 - loop intrinsic on loop that has been optimised away
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, BurstTargetCpu.X64_SSE4)}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, $"{debugLvl}")}");
var baseOptions = options.ToString();
var append = BurstInspectorGUI.GetDisasmOptions()[(int)DisassemblyKind.Asm];
// Setup disAssembler with the job:
compileTarget.RawDisassembly = BurstInspectorGUI.GetDisassembly(compileTarget.Method, baseOptions + append);
textToRender = compileTarget.RawDisassembly.TrimStart('\n');
return disassembler;
}
[Test]
public void FindLineNrTest()
{
// Load in a test text
var disassembler = GetDisassemblerandText("BurstInspectorGUITests.MyJob - (IJob)", 1, out string textToRender);
disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel, true, true);
var text = disassembler.GetOrRenderBlockToText(0);
// Call find line nr for:
// first line
// Around middle
// Last line
Assert.AreEqual(0, BurstStringSearch.FindLineNr(text, text.IndexOf('\n') - 1), "Couldn't find line 0");
Assert.AreEqual(2, BurstStringSearch.FindLineNr(text, text.IndexOf('\n', text.IndexOf('\n') + 1) + 1), "Couldn't find line in middle");
Assert.AreEqual(disassembler.Blocks[0].Length-1, BurstStringSearch.FindLineNr(text, text.Length-1), "Couldn't find last line");
}
[Test]
public void GetEndIndexOfPlainLineTest()
{
Assert.AreEqual(("This\nIs\nPerfect".Length-1, 6),
BurstStringSearch.GetEndIndexOfPlainLine("This\nIs\nPerfect", 2),
"Failed finding in well formed string");
const string text1 = "No line endings";
Assert.AreEqual((text1.Length-1, text1.Length-1),
BurstStringSearch.GetEndIndexOfPlainLine(text1, 0),
"Failed for missing line ending");
const string text2 = "No Line endings too many lines";
Assert.Throws<ArgumentOutOfRangeException>(() => BurstStringSearch.GetEndIndexOfPlainLine(text2, 2),
"Failed for missing line ending and too high line number");
const string text3 = "Line ending\n";
Assert.AreEqual((text3.Length-1, text3.Length-1),
BurstStringSearch.GetEndIndexOfPlainLine(text3, 0),
"Failed with line ending");
const string text4 = "Line ending too many lines\n";
Assert.Throws<ArgumentOutOfRangeException>(() => BurstStringSearch.GetEndIndexOfPlainLine(text4, 2),
"Failed with line endings and too high line number");
}
[Test]
public void FindMatchTest()
{
_ = GetDisassemblerandText("BurstInspectorGUITests.MyJob - (IJob)", 2, out var textToRender);
var expectedNormal = textToRender.IndexOf("def");
var tmp = Regex.Match(textToRender, @"\bdef\b");
var expectedWhole = tmp.Success ? tmp.Index : -1;
// Normal search
Assert.AreEqual((expectedNormal, 3),
BurstStringSearch.FindMatch(textToRender,
new SearchCriteria("def", false, false, false), default),
"Standard search failed"); // standard search: Match def in .endef
Assert.AreEqual((expectedWhole, 3),
BurstStringSearch.FindMatch(textToRender,
new SearchCriteria("def", false, true, false), default),
"Standard whole words failed"); // whole word search: Match def in .def
// Regex search
const RegexOptions opt = RegexOptions.CultureInvariant;
Assert.AreEqual((expectedNormal, 3),
BurstStringSearch.FindMatch(textToRender,
new SearchCriteria("def", false, false, true),
new Regex("def", opt | RegexOptions.IgnoreCase)),
"Regex search failed"); // standard search: Match def in .endef
Assert.AreEqual((expectedWhole, tmp.Success ? 3 : 0),
BurstStringSearch.FindMatch(textToRender,
new SearchCriteria(@"\bdef\b", false, true, true),
new Regex(@"\bdef\b", opt)),
"Regex whole word failed"); // whole word search: Match def in .def
// Across lines and blocks
Assert.AreEqual((12, 4),
BurstStringSearch.FindMatch(textToRender,
new SearchCriteria(@"t[\n]+..", false, false, true),
new Regex(@"t[\n]+..", opt | RegexOptions.IgnoreCase)),
"Regex across lines failed");
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d306d2ea7783099abe61e86c377ca92
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,712 @@
using System;
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using Unity.Jobs.LowLevel.Unsafe;
using UnityEngine.TestTools;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using System.Threading;
using System.Diagnostics;
using UnityEditor;
using Debug = UnityEngine.Debug;
using System.Text.RegularExpressions;
using Unity.Profiling;
using UnityEditor.Compilation;
using System.IO;
[TestFixture]
public class EditModeTest
{
private const int MaxIterations = 500;
[UnityTest]
public IEnumerator CheckBurstJobEnabledDisabled()
{
BurstCompiler.Options.EnableBurstCompileSynchronously = true;
try
{
foreach(var item in CheckBurstJobDisabled()) yield return item;
foreach(var item in CheckBurstJobEnabled()) yield return item;
}
finally
{
BurstCompiler.Options.EnableBurstCompilation = true;
}
}
private IEnumerable CheckBurstJobEnabled()
{
BurstCompiler.Options.EnableBurstCompilation = true;
yield return null;
using (var jobTester = new BurstJobTester2())
{
var result = jobTester.Calculate();
Assert.AreNotEqual(0.0f, result);
}
}
private IEnumerable CheckBurstJobDisabled()
{
BurstCompiler.Options.EnableBurstCompilation = false;
yield return null;
using (var jobTester = new BurstJobTester2())
{
var result = jobTester.Calculate();
Assert.AreEqual(0.0f, result);
}
}
[UnityTest]
public IEnumerator CheckJobWithNativeArray()
{
BurstCompiler.Options.EnableBurstCompileSynchronously = true;
BurstCompiler.Options.EnableBurstCompilation = true;
yield return null;
var job = new BurstJobTester2.MyJobCreatingAndDisposingNativeArray()
{
Length = 128,
Result = new NativeArray<int>(16, Allocator.TempJob)
};
var handle = job.Schedule();
handle.Complete();
try
{
Assert.AreEqual(job.Length, job.Result[0]);
}
finally
{
job.Result.Dispose();
}
}
#if UNITY_BURST_BUG_FUNCTION_POINTER_FIXED
[UnityTest]
public IEnumerator CheckBurstFunctionPointerException()
{
BurstCompiler.Options.EnableBurstCompileSynchronously = true;
BurstCompiler.Options.EnableBurstCompilation = true;
yield return null;
using (var jobTester = new BurstJobTester())
{
var exception = Assert.Throws<InvalidOperationException>(() => jobTester.CheckFunctionPointer());
StringAssert.Contains("Exception was thrown from a function compiled with Burst", exception.Message);
}
}
#endif
[BurstCompile(CompileSynchronously = true)]
private struct HashTestJob : IJob
{
public NativeArray<int> Hashes;
public void Execute()
{
Hashes[0] = BurstRuntime.GetHashCode32<int>();
Hashes[1] = TypeHashWrapper.GetIntHash();
Hashes[2] = BurstRuntime.GetHashCode32<TypeHashWrapper.SomeStruct<int>>();
Hashes[3] = TypeHashWrapper.GetGenericHash<int>();
}
}
[Test]
public static void TestTypeHash()
{
HashTestJob job = new HashTestJob
{
Hashes = new NativeArray<int>(4, Allocator.TempJob)
};
job.Schedule().Complete();
var hash0 = job.Hashes[0];
var hash1 = job.Hashes[1];
var hash2 = job.Hashes[2];
var hash3 = job.Hashes[3];
job.Hashes.Dispose();
Assert.AreEqual(hash0, hash1, "BurstRuntime.GetHashCode32<int>() has returned two different hashes");
Assert.AreEqual(hash2, hash3, "BurstRuntime.GetHashCode32<SomeStruct<int>>() has returned two different hashes");
}
[UnityTest]
public IEnumerator CheckSafetyChecksWithDomainReload()
{
{
var job = new SafetyCheckJobWithDomainReload();
{
// Run with safety-checks true
BurstCompiler.Options.EnableBurstSafetyChecks = true;
job.Result = new NativeArray<int>(1, Allocator.TempJob);
try
{
var handle = job.Schedule();
handle.Complete();
Assert.AreEqual(2, job.Result[0]);
}
finally
{
job.Result.Dispose();
}
}
{
// Run with safety-checks false
BurstCompiler.Options.EnableBurstSafetyChecks = false;
job.Result = new NativeArray<int>(1, Allocator.TempJob);
bool hasException = false;
try
{
var handle = job.Schedule();
handle.Complete();
Assert.AreEqual(1, job.Result[0]);
}
catch
{
hasException = true;
throw;
}
finally
{
job.Result.Dispose();
if (hasException)
{
BurstCompiler.Options.EnableBurstSafetyChecks = true;
}
}
}
}
// Ask for domain reload
EditorUtility.RequestScriptReload();
// Wait for the domain reload to be completed
yield return new WaitForDomainReload();
{
// The safety checks should have been disabled by the previous code
Assert.False(BurstCompiler.Options.EnableBurstSafetyChecks);
// Restore safety checks
BurstCompiler.Options.EnableBurstSafetyChecks = true;
}
}
[BurstCompile(CompileSynchronously = true)]
private struct DebugLogJob : IJob
{
public int Value;
public void Execute()
{
UnityEngine.Debug.Log($"This is a string logged from a job with burst with the following {Value}");
}
}
[Test]
public static void TestDebugLog()
{
var job = new DebugLogJob
{
Value = 256
};
job.Schedule().Complete();
}
[BurstCompile(CompileSynchronously = true, Debug = true)]
struct DebugLogErrorJob : IJob
{
public void Execute()
{
UnityEngine.Debug.LogError("X");
}
}
[UnityTest]
public IEnumerator DebugLogError()
{
LogAssert.Expect(LogType.Error, "X");
var jobData = new DebugLogErrorJob();
jobData.Run();
yield return null;
}
[BurstCompile(CompileSynchronously = true)]
private struct SafetyCheckJobWithDomainReload : IJob
{
public NativeArray<int> Result;
public void Execute()
{
Result[0] = 1;
SetResultWithSafetyChecksOnly();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private void SetResultWithSafetyChecksOnly()
{
Result[0] = 2;
}
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void SafelySetSomeBool(ref bool b)
{
b = true;
}
[BurstCompile(DisableSafetyChecks = false)]
private struct EnabledSafetyChecksJob : IJob
{
[WriteOnly] public NativeArray<int> WasHit;
public void Execute()
{
var b = false;
SafelySetSomeBool(ref b);
WasHit[0] = b ? 1 : 0;
}
}
[BurstCompile(DisableSafetyChecks = true)]
private struct DisabledSafetyChecksJob : IJob
{
[WriteOnly] public NativeArray<int> WasHit;
public void Execute()
{
var b = false;
SafelySetSomeBool(ref b);
WasHit[0] = b ? 1 : 0;
}
}
[UnityTest]
public IEnumerator CheckSafetyChecksOffGloballyAndOnInJob()
{
BurstCompiler.Options.EnableBurstSafetyChecks = false;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var job = new EnabledSafetyChecksJob()
{
WasHit = new NativeArray<int>(1, Allocator.TempJob)
};
job.Schedule().Complete();
try
{
// Safety checks are off globally which overwrites the job having safety checks on.
Assert.AreEqual(0, job.WasHit[0]);
}
finally
{
job.WasHit.Dispose();
}
}
[UnityTest]
public IEnumerator CheckSafetyChecksOffGloballyAndOffInJob()
{
BurstCompiler.Options.EnableBurstSafetyChecks = false;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var job = new DisabledSafetyChecksJob()
{
WasHit = new NativeArray<int>(1, Allocator.TempJob)
};
job.Schedule().Complete();
try
{
// Safety checks are off globally and off in job.
Assert.AreEqual(0, job.WasHit[0]);
}
finally
{
job.WasHit.Dispose();
}
}
[UnityTest]
public IEnumerator CheckSafetyChecksOnGloballyAndOnInJob()
{
BurstCompiler.Options.EnableBurstSafetyChecks = true;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var job = new EnabledSafetyChecksJob()
{
WasHit = new NativeArray<int>(1, Allocator.TempJob)
};
job.Schedule().Complete();
try
{
// Safety checks are on globally and on in job.
Assert.AreEqual(1, job.WasHit[0]);
}
finally
{
job.WasHit.Dispose();
}
}
[UnityTest]
public IEnumerator CheckSafetyChecksOnGloballyAndOffInJob()
{
BurstCompiler.Options.EnableBurstSafetyChecks = true;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var job = new DisabledSafetyChecksJob()
{
WasHit = new NativeArray<int>(1, Allocator.TempJob)
};
job.Schedule().Complete();
try
{
// Safety checks are on globally but off in job.
Assert.AreEqual(0, job.WasHit[0]);
}
finally
{
job.WasHit.Dispose();
}
}
[UnityTest]
public IEnumerator CheckForceSafetyChecksWorks()
{
BurstCompiler.Options.ForceEnableBurstSafetyChecks = true;
yield return null;
var job = new DisabledSafetyChecksJob()
{
WasHit = new NativeArray<int>(1, Allocator.TempJob)
};
job.Schedule().Complete();
try
{
// Even though the job has set disabled safety checks, the menu item 'Force On'
// has been set which overrides any other requested behaviour.
Assert.AreEqual(1, job.WasHit[0]);
}
finally
{
job.WasHit.Dispose();
}
}
[UnityTest]
public IEnumerator CheckSharedStaticWithDomainReload()
{
// Check that on a first access, SharedStatic is always empty
AssertTestSharedStaticEmpty();
// Fill with some data
TestSharedStatic.SharedValue.Data = new TestSharedStatic(1, 2, 3, 4);
Assert.AreEqual(1, TestSharedStatic.SharedValue.Data.Value1);
Assert.AreEqual(2, TestSharedStatic.SharedValue.Data.Value2);
Assert.AreEqual(3, TestSharedStatic.SharedValue.Data.Value3);
Assert.AreEqual(4, TestSharedStatic.SharedValue.Data.Value4);
// Ask for domain reload
EditorUtility.RequestScriptReload();
// Wait for the domain reload to be completed
yield return new WaitForDomainReload();
// Make sure that after a domain reload everything is initialized back to zero
AssertTestSharedStaticEmpty();
}
private static void AssertTestSharedStaticEmpty()
{
Assert.AreEqual(0, TestSharedStatic.SharedValue.Data.Value1);
Assert.AreEqual(0, TestSharedStatic.SharedValue.Data.Value2);
Assert.AreEqual(0, TestSharedStatic.SharedValue.Data.Value3);
Assert.AreEqual(0, TestSharedStatic.SharedValue.Data.Value4);
}
private struct TestSharedStatic
{
public static readonly SharedStatic<TestSharedStatic> SharedValue = SharedStatic<TestSharedStatic>.GetOrCreate<TestSharedStatic>();
public TestSharedStatic(int value1, long value2, long value3, long value4)
{
Value1 = value1;
Value2 = value2;
Value3 = value3;
Value4 = value4;
}
public int Value1;
public long Value2;
public long Value3;
public long Value4;
}
static EditModeTest()
{
// UnityEngine.Debug.Log("Domain Reload");
}
[BurstCompile]
private static class FunctionPointers
{
public delegate int SafetyChecksDelegate();
[BurstCompile(DisableSafetyChecks = false)]
public static int WithSafetyChecksEnabled()
{
var b = false;
SafelySetSomeBool(ref b);
return b ? 1 : 0;
}
[BurstCompile(DisableSafetyChecks = true)]
public static int WithSafetyChecksDisabled()
{
var b = false;
SafelySetSomeBool(ref b);
return b ? 1 : 0;
}
}
[UnityTest]
public IEnumerator CheckSafetyChecksOffGloballyAndOffInFunctionPointer()
{
BurstCompiler.Options.EnableBurstSafetyChecks = false;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var funcPtr = BurstCompiler.CompileFunctionPointer<FunctionPointers.SafetyChecksDelegate>(FunctionPointers.WithSafetyChecksDisabled);
// Safety Checks are off globally and off in the job.
Assert.AreEqual(0, funcPtr.Invoke());
}
[UnityTest]
public IEnumerator CheckSafetyChecksOffGloballyAndOnInFunctionPointer()
{
BurstCompiler.Options.EnableBurstSafetyChecks = false;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var funcPtr = BurstCompiler.CompileFunctionPointer<FunctionPointers.SafetyChecksDelegate>(FunctionPointers.WithSafetyChecksEnabled);
// Safety Checks are off globally and on in job, but the global setting takes precedence.
Assert.AreEqual(0, funcPtr.Invoke());
}
[UnityTest]
public IEnumerator CheckSafetyChecksOnGloballyAndOffInFunctionPointer()
{
BurstCompiler.Options.EnableBurstSafetyChecks = true;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var funcPtr = BurstCompiler.CompileFunctionPointer<FunctionPointers.SafetyChecksDelegate>(FunctionPointers.WithSafetyChecksDisabled);
// Safety Checks are on globally and off in the job, so the job takes predence.
Assert.AreEqual(0, funcPtr.Invoke());
}
[UnityTest]
public IEnumerator CheckSafetyChecksOnGloballyAndOnInFunctionPointer()
{
BurstCompiler.Options.EnableBurstSafetyChecks = true;
BurstCompiler.Options.ForceEnableBurstSafetyChecks = false;
yield return null;
var funcPtr = BurstCompiler.CompileFunctionPointer<FunctionPointers.SafetyChecksDelegate>(FunctionPointers.WithSafetyChecksEnabled);
// Safety Checks are on globally and on in the job.
Assert.AreEqual(1, funcPtr.Invoke());
}
[UnityTest]
public IEnumerator CheckFunctionPointerForceSafetyChecksWorks()
{
BurstCompiler.Options.ForceEnableBurstSafetyChecks = true;
yield return null;
var funcPtr = BurstCompiler.CompileFunctionPointer<FunctionPointers.SafetyChecksDelegate>(FunctionPointers.WithSafetyChecksDisabled);
// Even though the job has set disabled safety checks, the menu item 'Force On'
// has been set which overrides any other requested behaviour.
Assert.AreEqual(1, funcPtr.Invoke());
}
[BurstCompile(CompileSynchronously = true)]
private struct DebugDrawLineJob : IJob
{
public void Execute()
{
Debug.DrawLine(new Vector3(0, 0, 0), new Vector3(5, 0, 0), Color.green);
}
}
[Test]
public void TestDebugDrawLine()
{
var job = new DebugDrawLineJob();
job.Schedule().Complete();
}
[BurstCompile]
private static class ProfilerMarkerWrapper
{
private static readonly ProfilerMarker StaticMarker = new ProfilerMarker("TestStaticBurst");
[BurstCompile(CompileSynchronously = true)]
public static int CreateAndUseProfilerMarker(int start)
{
using (StaticMarker.Auto())
{
var p = new ProfilerMarker("TestBurst");
p.Begin();
var result = 0;
for (var i = start; i < start + 100000; i++)
{
result += i;
}
p.End();
return result;
}
}
}
private delegate int IntReturnIntDelegate(int param);
[Test]
public void TestCreateProfilerMarker()
{
var fp = BurstCompiler.CompileFunctionPointer<IntReturnIntDelegate>(ProfilerMarkerWrapper.CreateAndUseProfilerMarker);
fp.Invoke(5);
}
[BurstCompile]
private static class EnsureAssemblyBuilderDoesNotInvalidFunctionPointers
{
[BurstDiscard]
private static void MessOnManaged(ref int x) => x = 42;
[BurstCompile(CompileSynchronously = true)]
public static int WithBurst()
{
int x = 13;
MessOnManaged(ref x);
return x;
}
}
#if !UNITY_2023_1_OR_NEWER
[Test]
public void TestAssemblyBuilder()
{
var preBuilder = EnsureAssemblyBuilderDoesNotInvalidFunctionPointers.WithBurst();
Assert.AreEqual(13, preBuilder);
var tempDirectory = Path.GetTempPath();
var script = Path.Combine(tempDirectory, "BurstGeneratedAssembly.cs");
File.WriteAllText(script, @"
using Unity.Burst;
namespace BurstGeneratedAssembly
{
[BurstCompile]
public static class MyStuff
{
[BurstCompile(CompileSynchronously = true)]
public static int BurstedFunction(int x) => x + 1;
}
}
");
var dll = Path.Combine(tempDirectory, "BurstGeneratedAssembly.dll");
var builder = new AssemblyBuilder(dll, script);
Assert.IsTrue(builder.Build());
// Busy wait for the build to be done.
while (builder.status != AssemblyBuilderStatus.Finished)
{
Assert.AreEqual(preBuilder, EnsureAssemblyBuilderDoesNotInvalidFunctionPointers.WithBurst());
Thread.Sleep(10);
}
Assert.AreEqual(preBuilder, EnsureAssemblyBuilderDoesNotInvalidFunctionPointers.WithBurst());
}
#endif
[UnityTest]
public IEnumerator CheckChangingScriptOptimizationMode()
{
static void CheckBurstIsEnabled()
{
using (var jobTester = new BurstJobTester2())
{
var result = jobTester.Calculate();
Assert.AreNotEqual(0.0f, result);
}
}
CheckBurstIsEnabled();
// Switch scripting code optimization mode from Release to Debug.
Assert.AreEqual(CodeOptimization.Release, CompilationPipeline.codeOptimization);
CompilationPipeline.codeOptimization = CodeOptimization.Debug;
// Wait for the domain reload to be completed
yield return new WaitForDomainReload();
CheckBurstIsEnabled();
// Set scripting code optimization mode back to Release.
CompilationPipeline.codeOptimization = CodeOptimization.Release;
// Wait for the domain reload to be completed
yield return new WaitForDomainReload();
CheckBurstIsEnabled();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 581193df6bcc393285dee7fedd436195
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,777 @@
using System;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using UnityEditor;
using Unity.Burst.Editor;
using System.Text;
using Unity.Burst;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.TestTools.Utils;
using System.Runtime.CompilerServices;
[TestFixture]
public class LongTextAreaTests
{
private LongTextArea _textArea;
[OneTimeSetUp]
public void SetUp()
{
_textArea = new LongTextArea();
}
[Test]
[TestCase("", " push rbp\n .seh_pushreg rbp\n", 7, true)]
[TestCase("<color=#CCCCCC>", " push rbp\n .seh_pushreg rbp\n", 25, true)]
[TestCase("<color=#d7ba7d>", " push rbp\n .seh_pushreg rbp\n", 21 + 15 + 8 + 15, true)]
[TestCase("", "\n# hulahop hejsa\n", 5, false)]
public void GetStartingColorTagTest(string tag, string text, int textIdx, bool syntaxHighlight)
{
var disAssembler = new BurstDisassembler();
_textArea.SetText("", text, true, disAssembler, disAssembler.Initialize(text, BurstDisassembler.AsmKind.Intel, true, syntaxHighlight));
if (!_textArea.CopyColorTags) _textArea.ChangeCopyMode();
Assert.That(_textArea.GetStartingColorTag(0, textIdx), Is.EqualTo(tag));
}
[Test]
[TestCase("", " push rbp\n .seh_pushreg rbp\n", 7, true)]
[TestCase("</color>", " push rbp\n .seh_pushreg rbp\n", 25, true)]
[TestCase("</color>", " push rbp\n .seh_pushreg rbp\n", 21 + 15 + 8 + 15, true)]
[TestCase("", " push rbp\n .seh_pushreg rbp\n", 14 + 15 + 8, true)]
[TestCase("", "\n# hulahop hejsa\n", 5, false)]
public void GetEndingColorTagTest(string tag, string text, int textIdx, bool syntaxHighlight)
{
var disAssembler = new BurstDisassembler();
_textArea.SetText("", text, true, disAssembler, disAssembler.Initialize(text, BurstDisassembler.AsmKind.Intel, true, syntaxHighlight));
if (!_textArea.CopyColorTags) _textArea.ChangeCopyMode();
Assert.That(_textArea.GetEndingColorTag(0, textIdx), Is.EqualTo(tag));
}
[Test]
[TestCase("<color=#FFFF00>hulahop</color> <color=#DCDCAA>hejsa</color>\n", 0, 16, 16)]
[TestCase("<color=#FFFF00>hulahop</color>\n <color=#DCDCAA>hejsa</color>\n", 1, 40, 9)]
[TestCase("<color=#FFFF00>hulahop</color>\n <color=#DCDCAA>hejsa</color>\n hej", 2, 67, 3)]
[TestCase("<color=#FFFF00>hulahop</color> <color=#DCDCAA>hejsa</color>", 0, 15, 15)]
[TestCase("\n <color=#4EC9B0>je</color> <color=#d4d4d4>.LBB11_4</color>", 1, 34, 33)]
// Test cases for when on enhanced text and not coloured.
[TestCase("hulahop hejsa\n", 0, 16, 16)]
[TestCase("hulahop\n hejsa\n", 1, 17, 9)]
[TestCase("hulahop\n hejsa\n hej", 2, 21, 3)]
[TestCase("hulahop hejsa", 0, 15, 15)]
public void GetEndIndexOfColoredLineTest(string text, int line, int resTotal, int resRel)
{
Assert.That(_textArea.GetEndIndexOfColoredLine(text, line), Is.EqualTo((resTotal, resRel)));
}
[Test]
[TestCase("hulahop hejsa\n", 0, 16, 16)]
[TestCase("hulahop\n hejsa\n", 1, 17, 9)]
[TestCase("hulahop\n hejsa\n hej", 2, 21, 3)]
[TestCase("hulahop hejsa", 0, 15, 15)]
[TestCase("\nhulahop hejsa", 1, 16, 15)]
public void GetEndIndexOfPlainLineTest(string text, int line, int resTotal, int resRel)
{
Assert.That(_textArea.GetEndIndexOfPlainLine(text, line), Is.EqualTo((resTotal, resRel)));
}
[Test]
[TestCase("<color=#FFFF00>hulahop</color>\n <color=#DCDCAA>hejsa</color>\n hej", 2, 2, 0)]
[TestCase("<color=#FFFF00>hulahop</color>\n <color=#DCDCAA>hejsa</color>\n hej", 1, 5, 15)]
[TestCase("<color=#FFFF00>hulahop</color> <color=#DCDCAA>hejsa</color>:", 0, 17, 46)]
public void BumpSelectionXByColortagTest(string text, int lineNum, int charsIn, int colourTagFiller)
{
var (idxTotal, idxRel) = _textArea.GetEndIndexOfColoredLine(text, lineNum);
Assert.That(_textArea.BumpSelectionXByColorTag(text, idxTotal - idxRel, charsIn), Is.EqualTo(charsIn + colourTagFiller));
}
[Test]
[TestCase(" push rbp\n .seh_pushreg rbp\n", false)]
[TestCase(" push rbp\n .seh_pushreg rbp\n", true)]
public void SelectAllTest(string text, bool useDisassembler)
{
if (useDisassembler)
{
var disAssembler = new BurstDisassembler();
_textArea.SetText("", text, true, disAssembler, disAssembler.Initialize(text, BurstDisassembler.AsmKind.Intel));
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, true);
}
else
{
_textArea.SetText("", text, true, null, false);
}
_textArea.selectPos = new Vector2(2, 2);
// There is no inserted comments or similar in my test example, so finalAreaSize, should be equivalent for the two.
_textArea.finalAreaSize = new Vector2(7.5f * text.Length, 15.2f);
_textArea.SelectAll();
Assert.That(_textArea.selectPos, Is.EqualTo(Vector2.zero));
Assert.That(_textArea.selectDragPos, Is.EqualTo(new Vector2(7.5f * text.Length, 15.2f)));
if (!useDisassembler)
{
Assert.That(_textArea.textSelectionIdx, Is.EqualTo((0, text.Length)));
}
}
private BurstDisassembler GetDisassemblerandText(string compileTargetName, int debugLvl, out string textToRender)
{
// Get target job assembly:
var assemblies = BurstReflection.EditorAssembliesThatCanPossiblyContainJobs;
var result = BurstReflection.FindExecuteMethods(assemblies, BurstReflectionAssemblyOptions.None);
var compileTarget = result.CompileTargets.Find(x => x.GetDisplayName() == compileTargetName);
Assert.IsTrue(compileTarget != default, $"Could not find compile target: {compileTarget}");
BurstDisassembler disassembler = new BurstDisassembler();
var options = new StringBuilder();
compileTarget.Options.TryGetOptions(compileTarget.JobType, true, out string defaultOptions);
options.AppendLine(defaultOptions);
// Disables the 2 current warnings generated from code (since they clutter up the inspector display)
// BC1370 - throw inside code not guarded with ConditionalSafetyCheck attribute
// BC1322 - loop intrinsic on loop that has been optimised away
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDisableWarnings, "BC1370;BC1322")}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionTarget, BurstTargetCpu.X64_SSE4)}");
options.AppendLine($"{BurstCompilerOptions.GetOption(BurstCompilerOptions.OptionDebug, $"{debugLvl}")}");
var baseOptions = options.ToString();
var append = BurstInspectorGUI.GetDisasmOptions()[(int)DisassemblyKind.Asm];
// Setup disAssembler with the job:
compileTarget.RawDisassembly = BurstInspectorGUI.GetDisassembly(compileTarget.Method, baseOptions + append);
textToRender = compileTarget.RawDisassembly.TrimStart('\n');
return disassembler;
}
[Test]
[TestCase(true, true, 2)]
[TestCase(true, true, 1)]
[TestCase(true, false, 2)]
[TestCase(true, false, 1)]
[TestCase(false, true, 2)]
[TestCase(false, true, 1)]
[TestCase(false, false, 2)]
[TestCase(false, false, 1)]
public void CopyAllTest(bool useDisassembler, bool coloured, int debugLvl)
{
// Get target job assembly:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
if (useDisassembler)
{
_textArea.SetText("", textToRender, true, disassembler, disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel, true, coloured));
_textArea.ExpandAllBlocks();
var builder = new StringBuilder();
for (int i = 0; i < disassembler.Blocks.Count; i++)
{
builder.Append(disassembler.GetOrRenderBlockToText(i));
}
textToRender = builder.ToString();
}
else
{
_textArea.SetText("", textToRender, true, null, false);
}
_textArea.Layout(GUIStyle.none, _textArea.horizontalPad);
_textArea.SelectAll();
_textArea.DoSelectionCopy();
Assert.AreEqual(textToRender, EditorGUIUtility.systemCopyBuffer);
}
[Test]
public void CopyAllTextWithoutColorTagsTest()
{
// Setup:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel));
_textArea.Layout(GUIStyle.none, _textArea.horizontalPad);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, true);
// Actual test to reproduce error:
_textArea.ChangeCopyMode();
_textArea.SelectAll();
Assert.DoesNotThrow(_textArea.DoSelectionCopy);
}
[Test]
public void CopyTextAfterSelectionMovedTest()
{
// Setup:
const bool sbm = true;
var wa = Rect.zero;
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel));
_textArea.Layout(GUIStyle.none, _textArea.horizontalPad);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, sbm);
// Actual test to reproduce error:
_textArea.ChangeCopyMode();
_textArea.MoveSelectionDown(wa, sbm);
_textArea.MoveSelectionDown(wa, sbm);
_textArea.MoveSelectionLeft(wa, sbm);
Assert.DoesNotThrow(_textArea.DoSelectionCopy);
_textArea.MoveSelectionRight(wa, sbm);
Assert.DoesNotThrow(_textArea.DoSelectionCopy);
}
[Test]
public void CopyTextIdenticalWithAndWithoutColorTags()
{
// We don't wanna go messing with the users system buffer. At least if user didn't break anything.
var savedSystemBuffer = EditorGUIUtility.systemCopyBuffer;
// Get target job assembly:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel));
_textArea.Layout(GUIStyle.none, _textArea.horizontalPad);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, true);
for (var i=0; i<disassembler.Blocks[0].Length+50; i++) _textArea.MoveSelectionDown(Rect.zero, true);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, true);
_textArea.UpdateEnhancedSelectTextIdx(_textArea.horizontalPad);
_textArea.DoSelectionCopy();
var copiedText1 = EditorGUIUtility.systemCopyBuffer;
_textArea.ChangeCopyMode();
_textArea.DoSelectionCopy();
var copiedText2 = EditorGUIUtility.systemCopyBuffer;
var regx = new Regex(@"(<color=#[0-9A-Za-z]*>)|(</color>)");
if (!_textArea.CopyColorTags)
{
(copiedText1, copiedText2) = (copiedText2, copiedText1);
}
copiedText2 = regx.Replace(copiedText2, "");
EditorGUIUtility.systemCopyBuffer = savedSystemBuffer;
Assert.AreEqual(copiedText1, copiedText2,
"Copy with color tags did not match copy without " +
"(Color tags is removed from the copy to make it comparable with the color-tag-less copy).");
}
// Disabled due to https://jira.unity3d.com/browse/BUR-2207
[Test]
[TestCase(true)]
[TestCase(false)]
public void KeepingSelectionWhenMovingTest(bool useDisassembler)
{
const string jobName = "BurstInspectorGUITests.MyJob - (IJob)";
BurstDisassembler disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
Rect workingArea = new Rect();
if (useDisassembler)
{
_textArea.SetText(jobName, textToRender, true, disassembler, disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel));
_textArea.LayoutEnhanced(GUIStyle.none, workingArea, true);
}
else
{
_textArea.SetText(jobName, textToRender, false, null, false);
}
_textArea.Layout(GUIStyle.none, _textArea.horizontalPad);
Assert.IsFalse(_textArea.HasSelection);
Vector2 start = _textArea.selectDragPos;
if (useDisassembler) start.x = _textArea.horizontalPad + _textArea.fontWidth / 2;
// Horizontal movement:
_textArea.MoveSelectionRight(workingArea, true);
Assert.IsTrue(_textArea.HasSelection);
Assert.AreEqual(start + new Vector2(_textArea.fontWidth, 0), _textArea.selectDragPos);
_textArea.MoveSelectionLeft(workingArea, true);
Assert.IsTrue(_textArea.HasSelection);
Assert.AreEqual(start, _textArea.selectDragPos);
// Vertical movement:
_textArea.MoveSelectionDown(workingArea, true);
Assert.IsTrue(_textArea.HasSelection);
Assert.AreEqual(start + new Vector2(0, _textArea.fontHeight), _textArea.selectDragPos);
_textArea.MoveSelectionUp(workingArea, true);
Assert.IsTrue(_textArea.HasSelection);
Assert.AreEqual(start, _textArea.selectDragPos);
}
[Test]
public void GetFragNrFromBlockIdxTest()
{
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel, false, false));
var garbageVariable = 0f;
var numBlocks = disassembler.Blocks.Count;
// Want to get the last fragment possible
var expectedFragNr = 0;
for (var i = 0; i < _textArea.blocksFragmentsPlain.Length-1; i++)
{
expectedFragNr += _textArea.GetPlainFragments(i).Count;
}
Assert.AreEqual(expectedFragNr, _textArea.GetFragNrFromBlockIdx(numBlocks-1, 0, 0, ref garbageVariable));
Assert.AreEqual(3, _textArea.GetFragNrFromBlockIdx(3, 1, 1, ref garbageVariable));
}
[Test]
public void GetFragNrFromEnhancedTextIdxTest()
{
const string jobName = "BurstJobTester2.MyJob - (IJob)";
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText(jobName, textToRender, true, disassembler,
disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel, false, false));
var garbageVariable = 0f;
const int blockIdx = 2;
var fragments = _textArea.RecomputeFragmentsFromBlock(blockIdx);
var text = _textArea.GetText;
var expectedFrag = blockIdx + fragments.Count - 1;
var info = disassembler.BlockIdxs[blockIdx];
var extraFragLen = fragments.Count > 1
? fragments[0].text.Length + 1 // job only contains 2 fragments at max
: 0;
var idx = info.startIdx + extraFragLen + 1;
var expected = (expectedFrag, info.startIdx + extraFragLen);
var actual = _textArea.GetFragNrFromEnhancedTextIdx(idx, 0, 0, 0, ref garbageVariable);
Assert.AreEqual(expected, actual);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void SearchTextEnhancedTest(bool colored)
{
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
_textArea.SetText("", textToRender, true, disassembler, disassembler.Initialize(textToRender, BurstDisassembler.AsmKind.Intel, true, colored));
var workingArea = new Rect(0, 0, 10, 10);
_textArea.SearchText(new SearchCriteria(".Ltmp.:", true, false, true), new Regex(@"\.Ltmp.:"), ref workingArea);
Assert.AreEqual(10, _textArea.NrSearchHits);
// Check that they are filled out probably
int nr = 0;
foreach (var fragHits in _textArea.searchHits.Values)
{
foreach (var hit in fragHits)
{
Assert.AreEqual((0, 7, nr++), hit);
}
}
}
[Test]
public void SelectOnOneLineTest()
{
const string testCase = "\n<color=#d4d4d4>.Ltmp12</color>: ...";
var disassembler = new BurstDisassembler();
_textArea.SetText("", testCase, false, disassembler, disassembler.Initialize(testCase, BurstDisassembler.AsmKind.Intel));
// Set fontWidth and fontHeight
_textArea.Layout(GUIStyle.none, 20f);
// Set selection markers.
// Error happened when selection started at the lowest point of a line.
_textArea.selectPos = new Vector2(0, _textArea.fontHeight);
// Select further down to make sure it wont be switched with selectPos.
_textArea.selectDragPos = new Vector2(10 * _textArea.fontWidth, _textArea.fontHeight*2);
// Hopefully it wont throw anything
Assert.DoesNotThrow(() =>
_textArea.PrepareInfoForSelection(0, 0, _textArea.fontHeight,
new LongTextArea.Fragment() { text = testCase.TrimStart('\n'), lineCount = 1 },
_textArea.GetEndIndexOfColoredLine));
}
[Test]
public void GetLineHighlightTest()
{
const float hPad = 20f;
const int linePressed = 4 + 13;
// Get target job assembly:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
// Set up dependencies for GetLineHighlight(.)
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel)
);
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,0, _textArea.fontWidth*50,_textArea.fontHeight*50),
false
);
// Setup simple cache.
var cache = new LongTextArea.LineRegRectsCache();
var rect = _textArea.GetLineHighlight(ref cache, hPad, linePressed);
Assert.IsFalse(cache.IsRegistersCached(linePressed));
Assert.IsTrue(cache.IsLineHighlightCached(linePressed, false));
var expectedX = hPad;
var b = 0;
for (; b < disassembler.Blocks.Count; b++)
{
if (disassembler.Blocks[b].LineIndex > linePressed)
{
b--;
break;
}
}
var expectedY = (_textArea.blockLine[b] + (linePressed - disassembler.Blocks[b].LineIndex)) * _textArea.fontHeight + _textArea.fontHeight;
var lineStr = _textArea.GetLineString(disassembler.Lines[linePressed]);
var lineLen = lineStr.Length * _textArea.fontWidth;
var expected = new Rect(expectedX,
expectedY,
lineLen,
2f
);
var result = Mathf.Approximately(expectedX, rect.x)
&& Mathf.Approximately(expectedY, rect.y)
&& Mathf.Approximately(lineLen, rect.width)
&& Mathf.Approximately(2f, rect.height);
Assert.IsTrue(result, $"line highlight for \"{lineStr}\" was wrong.\nExpected: {expected}\nBut was: {rect}");
}
[Test]
public void GetRegRectsTest()
{
#region Initialize-test-states
const float hPad = 20f;
const int linePressed = 8 + 13;
// Get target job assembly:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
// Set up dependencies for GetLineHighlight(.)
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel)
);
// Setting up variables to determine view size:
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, false);
#endregion
// Find the block index to put within view:
var blockIdx = disassembler.Blocks.Count/2;
for (; blockIdx > 0; blockIdx--)
{
// Take the first block where we know the lastLine will be in the next block.
if (!_textArea._folded[blockIdx + 1] && disassembler.Blocks[blockIdx].Length >= 5) break;
}
// Initialize states with regards to view:
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,0, _textArea.fontWidth*100,_textArea.fontHeight*(_textArea.blockLine[blockIdx]+1)),
false
);
#region Function-to-test-call
var cache = new LongTextArea.LineRegRectsCache();
var registersUsed = new List<string> { "rbp", "rsp" };
var rects = _textArea.GetRegisterRects(hPad, ref cache, linePressed, registersUsed);
#endregion
#region Expected-variables
var lastLine = disassembler.Blocks[_textArea._renderBlockEnd+1].LineIndex + 4;
var expectedRbp =
(from pair in disassembler._registersUsedAtLine._linesRegisters.TakeWhile(x => x.Key < lastLine)
where pair.Value.Contains("rbp") && disassembler.Lines[pair.Key].Kind != BurstDisassembler.AsmLineKind.Directive
select pair);
var expectedRsp =
(from pair in disassembler._registersUsedAtLine._linesRegisters.TakeWhile(x => x.Key < lastLine)
where pair.Value.Contains("rsp") && disassembler.Lines[pair.Key].Kind != BurstDisassembler.AsmLineKind.Directive
select pair);
// Check that they are correctly placed!
// Only check the last here, as under development the "hardest" behaviour was from within the lowest blocks.
var lastRectLineIdx = expectedRbp.Last().Key;
var lastRectLine = disassembler.Lines[lastRectLineIdx];
var lastRectLineStr = _textArea.GetLineString(lastRectLine);
var expectedX = lastRectLineStr.Substring(0, lastRectLineStr.IndexOf("rbp")).Length * _textArea.fontWidth + hPad + 2f;
#endregion
Assert.IsTrue(cache.IsRegistersCached(linePressed), "Register Rect cache not probarly setup.");
Assert.IsFalse(cache.IsLineHighlightCached(linePressed, false), "Line highlight cache faultily set to cached.");
Assert.AreEqual(2, rects.Length, "Register Rect cache does not have correct number of registered registers.");
Assert.AreEqual(expectedRbp.Count(), rects[0].Count, "Did not find all \"rbp\" registers.");
Assert.AreEqual(expectedRsp.Count(), rects[1].Count, "Did not find all \"rsp\" registers.");
Assert.That(rects[0][rects[0].Count - 1].x, Is.EqualTo(expectedX).Using(FloatEqualityComparer.Instance),
"Wrong x position for last found \"rbp\" rect.");
// Note: Does not check Y position, as this is highly dependent on architecture, making it annoyingly hard
// to reason about.
}
[Test]
public void RegsRectCacheTest()
{
const float hPad = 20f;
const int linePressed = 8 + 13;
// Get target job assembly:
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
// Set up dependencies for GetLineHighlight(.)
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel)
);
_textArea.Layout(GUIStyle.none, hPad);
var yStart = 0f;
var yHeight = _textArea.fontHeight*44;
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,yStart, _textArea.fontWidth*100,yHeight),
false
);
var cache = new LongTextArea.LineRegRectsCache();
var registersUsed = new List<string> { "rbp", "rsp" };
var rects = _textArea.GetRegisterRects(hPad, ref cache, linePressed, registersUsed);
Assert.IsTrue(cache.IsRegistersCached(linePressed));
var cachedItems =
(from elm in rects
select elm.Count).Sum();
yStart = yHeight;
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,yStart, _textArea.fontWidth*100,yHeight),
false
);
rects = _textArea.GetRegisterRects(hPad, ref cache, linePressed, registersUsed);
Assert.IsTrue(cache.IsRegistersCached(linePressed));
var cachedItems2 =
(from elm in rects
select elm.Count).Sum();
Assert.IsTrue(cachedItems2 >= cachedItems);
}
[Test]
[TestCase("\n xor r9d, r9d\n", "r9d")]
[TestCase("\n push edx rdx\n", "rdx")]
public void SameRegisterUsedTwiceTest(string line, string reg)
{
const float hPad = 20f;
const int linePressed = 0;
// Get target job assembly:
var disassembler = new BurstDisassembler();
// Set up dependencies for GetLineHighlight(.)
_textArea.SetText("", line, true, disassembler,
disassembler.Initialize(
line,
BurstDisassembler.AsmKind.Intel)
);
_textArea.Layout(GUIStyle.none, hPad);
var yStart = 0f;
var yHeight = _textArea.fontHeight;
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,yStart, _textArea.fontWidth*100,yHeight),
false
);
var cache = new LongTextArea.LineRegRectsCache();
var registersUsed = new List<string> { reg };
var rects = _textArea.GetRegisterRects(hPad, ref cache, linePressed, registersUsed);
Assert.IsTrue(cache.IsRegistersCached(linePressed));
Assert.IsTrue(rects.Length == 1);
Assert.IsTrue(rects[0].Count == 2, "Did not find exactly both registers.");
}
/// <summary>
/// This test should check whether line press information is cleared when it is necessary.
/// It does not check whether it is unnecessarily cleared.
/// </summary>
[Test]
public void ClearLinePressTest()
{
void SetupCache(float pad, int lineNr, ref LongTextArea.LineRegRectsCache cache, List<string> regsUsed)
{
_textArea._pressedLine = lineNr;
_ = _textArea.GetRegisterRects(pad, ref cache, lineNr, regsUsed);
_ = _textArea.GetLineHighlight(ref cache, pad, lineNr);
}
// Test setup:
var registersUsed = new List<string> { "rbp", "rsp" };
const float hPad = 20f;
const int linePressed = 4 + 13;
var disassembler = new BurstDisassembler();
var thisPath = Path.GetDirectoryName(GetThisFilePath());
Assert.NotNull(thisPath, "Could not retrieve path for current directory.");
var textToRender = File.ReadAllText(Path.Combine(thisPath, _burstJobPath));
// Set up dependencies for GetLineHighlight(.)
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel)
);
// Setting up variables to determine view size:
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none, Rect.zero, false);
var blockIdx = _textArea.GetLinesBlockIdx(linePressed);
_textArea.Layout(GUIStyle.none, hPad);
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,0, _textArea.fontWidth*100,_textArea.fontHeight*(_textArea.blockLine[blockIdx]+1)),
false);
void TestCache(bool isLineRect, bool isRect, bool isLine, string msg)
{
Assert.AreEqual(isLineRect,
_textArea._lineRegCache.IsLineHighlightCached(linePressed, _textArea._folded[blockIdx]),
msg + " Line highlight failed.");
Assert.AreEqual(isRect,
_textArea._lineRegCache.IsRegistersCached(linePressed),
msg + " Register cache failed.");
msg += " Line press failed.";
if (!isLine)
{
Assert.AreEqual(-1, _textArea._pressedLine, msg);
}
else
{
Assert.AreNotEqual(-1, _textArea._pressedLine, msg);
}
SetupCache(hPad, linePressed, ref _textArea._lineRegCache, registersUsed);
}
SetupCache(hPad, linePressed, ref _textArea._lineRegCache, registersUsed);
TestCache(true, true, true, "Initial setup failed.");
// Following changes should result in clearing everything, as assembly text might have changed:
// * Expand all.
_textArea.ExpandAllBlocks();
TestCache(false, false, false, "Expanding blocks failed.");
// * Focus code.
_textArea.FocusCodeBlocks();
TestCache(false, false, false, "Focusing code blocks failed.");
// * disassembly kind, Target change, Safety check changes, Assembly kind changes e.g. by amount of debug info.
_textArea.SetText("", textToRender, true, disassembler,
disassembler.Initialize(
textToRender,
BurstDisassembler.AsmKind.Intel)
);
TestCache(false, false, false, "Setting up new text failed.");
// Following changes should only result in Rec change clear, as line number still resembles same line:
// * Font size.
_textArea.Invalidate();
TestCache(false, false, true, "Changing font size failed.");
// * Show branch flow.
_textArea.LayoutEnhanced(GUIStyle.none,
new Rect(0,0, _textArea.fontWidth*100,_textArea.fontHeight*(_textArea.blockLine[blockIdx]+1)),
true);
TestCache(false, false, true, "Changing font size failed.");
// * Smell test (This will however clear everything as ´SetText()´ required).
// Hence tested in the cases for fill clear.
}
private static string GetThisFilePath([CallerFilePath] string path = null) => path;
private readonly string _burstJobPath = "burstTestTarget.txt";
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b866f2ae2314368d97e916270883416e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa45014e48e13a5ea49fd421043ee143
folderAsset: yes

View file

@ -0,0 +1,16 @@
public class BurstReflectionTestsSeparateAssembly
{
[Unity.Jobs.LowLevel.Unsafe.JobProducerType(typeof(MyJobProducerSeparateAssembly<,>))]
public interface IMyGenericJobSeparateAssembly<T>
{
void Execute();
}
private static class MyJobProducerSeparateAssembly<TJob, T>
{
public static void Execute(ref TJob job)
{
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c07004be90d3814b9a09f7d08b26eb1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
{
"name": "SeparateAssembly",
"references": [
"Unity.Burst"
],
"includePlatforms": [],
"optionalUnityReferences": [
"TestAssemblies"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3ce0cf2654ad3464aa58eb2e27f77f0d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
using Unity.Burst;
public static class TypeHashWrapper
{
public static int GetIntHash()
{
return BurstRuntime.GetHashCode32<int>();
}
public static int GetGenericHash<T>()
{
return BurstRuntime.GetHashCode32<SomeStruct<T>>();
}
public struct SomeStruct<T> { }
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 581a69eadade3f4985faa44d7b02aa3d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
using System;
using NUnit.Framework;
using Unity.Burst.Editor;
public class StringSliceTests
{
private const string _someText = "This is some text we are going to take StringSlice from.";
private const string _target = "StringSlice";
private readonly StringSlice _ssTarget = new StringSlice(_someText, _someText.IndexOf(_target, StringComparison.InvariantCulture), _target.Length);
[Test]
public void StringSliceStringRepresentationTest()
{
Assert.AreEqual(_target, _ssTarget.ToString());
Assert.AreEqual('S', _ssTarget[0]);
Assert.IsTrue(_ssTarget == new StringSlice(_target));
}
[Test]
public void StartsWithTest()
{
Assert.IsFalse(_ssTarget.StartsWith("This"));
Assert.IsTrue(_ssTarget.StartsWith("S"));
Assert.IsTrue(_ssTarget.StartsWith(_target));
}
[Test]
public void ContainsTest()
{
Assert.IsFalse(_ssTarget.Contains('T'));
Assert.IsFalse(_ssTarget.Contains('s'));
Assert.IsTrue(_ssTarget.Contains('S'));
Assert.IsTrue(_ssTarget.Contains('g'));
Assert.IsTrue(_ssTarget.Contains('e'));
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8361860cac3b3a12bc041994c9c1638c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
{
"name": "Unity.Burst.Editor.Tests",
"references": [
"Unity.Burst",
"Unity.Mathematics",
"Unity.Burst.Tests.UnitTests",
"SeparateAssembly",
"Unity.Burst.Editor"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 15ce69d7d9d9370c9704e0797a0ab06b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,662 @@
.text
.def @feat.00;
.scl 3;
.type 0;
.endef
.globl @feat.00
.set @feat.00, 0
.intel_syntax noprefix
.file "main"
.def burst.initialize;
.scl 2;
.type 32;
.endef
.globl burst.initialize
.p2align 4, 0x90
burst.initialize:
.Lfunc_begin0:
.seh_proc burst.initialize
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
.seh_endprologue
pop rbp
ret
.Lfunc_end0:
.seh_endproc
.def burst.initialize.externals;
.scl 2;
.type 32;
.endef
.globl burst.initialize.externals
.p2align 4, 0x90
burst.initialize.externals:
.Lfunc_begin1:
.seh_proc burst.initialize.externals
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
.seh_endprologue
pop rbp
ret
.Lfunc_end1:
.seh_endproc
.def burst.initialize.statics;
.scl 2;
.type 32;
.endef
.globl burst.initialize.statics
.p2align 4, 0x90
burst.initialize.statics:
.Lfunc_begin2:
.seh_proc burst.initialize.statics
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
.seh_endprologue
pop rbp
ret
.Lfunc_end2:
.seh_endproc
.def d675c2aa053244579b646ec09368a505;
.scl 2;
.type 32;
.endef
.globl d675c2aa053244579b646ec09368a505
.p2align 4, 0x90
d675c2aa053244579b646ec09368a505:
.Lfunc_begin3:
.seh_proc d675c2aa053244579b646ec09368a505
push rbp
.seh_pushreg rbp
sub rsp, 48
.seh_stackalloc 48
lea rbp, [rsp + 48]
.seh_setframe rbp, 48
.seh_endprologue
mov eax, dword ptr [rbp + 48]
mov dword ptr [rsp + 32], eax
call "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
nop
add rsp, 48
pop rbp
ret
.Lfunc_end3:
.seh_endproc
.def "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
.scl 3;
.type 32;
.endef
.p2align 4, 0x90
"Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null":
.Lfunc_begin4:
.cv_func_id 0
.cv_file 1 "C:\\UnitySrc\\unity\\Runtime\\Jobs\\Managed\\IJob.cs"
.cv_loc 0 1 57 0
.seh_proc "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
.seh_endprologue
.Ltmp0:
.cv_file 2 "C:\\UnitySrc\\unity\\Runtime\\Export\\NativeArray\\NativeArray.cs"
.cv_inline_site_id 1 within 0 inlined_at 1 58 0
.cv_file 3 "C:\\UnitySrc\\burst\\src\\com.unity.burst\\Tests\\Editor\\BurstInspectorGUITests.cs"
.cv_inline_site_id 2 within 1 inlined_at 3 393 0
.cv_loc 2 2 130 0
mov eax, dword ptr [rcx + 8]
test rax, rax
.Ltmp1:
.cv_loc 1 3 393 0
je .LBB4_1
mov rdx, qword ptr [rcx]
vxorps xmm0, xmm0, xmm0
.p2align 4, 0x90
.LBB4_3:
.cv_loc 1 3 395 0
vaddss xmm0, xmm0, dword ptr [rdx]
.cv_loc 1 3 393 0
add rdx, 4
dec rax
jne .LBB4_3
jmp .LBB4_4
.LBB4_1:
vxorps xmm0, xmm0, xmm0
.LBB4_4:
.Ltmp2:
.cv_inline_site_id 3 within 1 inlined_at 3 397 0
.cv_loc 3 2 194 0
mov rax, qword ptr [rcx + 48]
vmovss dword ptr [rax], xmm0
.Ltmp3:
.cv_loc 0 1 59 0
pop rbp
ret
.Ltmp4:
.Lfunc_end4:
.seh_endproc
.section .drectve,"yn"
.ascii " /EXPORT:\"burst.initialize\""
.ascii " /EXPORT:\"burst.initialize.externals\""
.ascii " /EXPORT:\"burst.initialize.statics\""
.ascii " /EXPORT:d675c2aa053244579b646ec09368a505"
.section .debug$S,"dr"
.p2align 2
.long 4
.long 241
.long .Ltmp6-.Ltmp5
.Ltmp5:
.short .Ltmp8-.Ltmp7
.Ltmp7:
.short 4353
.long 0
.byte 0
.p2align 2
.Ltmp8:
.short .Ltmp10-.Ltmp9
.Ltmp9:
.short 4412
.long 0
.short 208
.short 0
.short 0
.short 91
.short 0
.short 14006
.short 0
.short 0
.short 0
.asciz "Burst 0.0.91.0 (Frontend Version : 040e20f6-d3e4-45d8-b45b-06c6d673cedb)"
.p2align 2
.Ltmp10:
.Ltmp6:
.p2align 2
.long 246
.long .Ltmp12-.Ltmp11
.Ltmp11:
.long 0
.long 4112
.cv_filechecksumoffset 3
.long 391
.long 4115
.cv_filechecksumoffset 2
.long 129
.long 4118
.cv_filechecksumoffset 2
.long 192
.Ltmp12:
.p2align 2
.long 241
.long .Ltmp14-.Ltmp13
.Ltmp13:
.short .Ltmp16-.Ltmp15
.Ltmp15:
.short 4422
.long 0
.long 0
.long 0
.long .Lfunc_end4-"Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
.long 0
.long 0
.long 4126
.secrel32 "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
.secidx "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
.byte 0
.asciz "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
.p2align 2
.Ltmp16:
.short .Ltmp18-.Ltmp17
.Ltmp17:
.short 4114
.long 8
.long 0
.long 0
.long 0
.long 0
.short 0
.long 1212416
.p2align 2
.Ltmp18:
.short .Ltmp20-.Ltmp19
.Ltmp19:
.short 4429
.long 0
.long 0
.long 4112
.cv_inline_linetable 1 3 391 .Lfunc_begin4 .Lfunc_end4
.p2align 2
.Ltmp20:
.short .Ltmp22-.Ltmp21
.Ltmp21:
.short 4429
.long 0
.long 0
.long 4115
.cv_inline_linetable 2 2 129 .Lfunc_begin4 .Lfunc_end4
.p2align 2
.Ltmp22:
.short 2
.short 4430
.short .Ltmp24-.Ltmp23
.Ltmp23:
.short 4429
.long 0
.long 0
.long 4118
.cv_inline_linetable 3 2 192 .Lfunc_begin4 .Lfunc_end4
.p2align 2
.Ltmp24:
.short 2
.short 4430
.short 2
.short 4430
.short 2
.short 4431
.Ltmp14:
.p2align 2
.cv_linetable 0, "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", .Lfunc_end4
.long 241
.long .Ltmp26-.Ltmp25
.Ltmp25:
.short .Ltmp28-.Ltmp27
.Ltmp27:
.short 4360
.long 4099
.asciz "BurstInspectorGUITests/MyJob"
.p2align 2
.Ltmp28:
.short .Ltmp30-.Ltmp29
.Ltmp29:
.short 4360
.long 4104
.asciz "Unity.Collections.NativeArray`1<System.Single>"
.p2align 2
.Ltmp30:
.short .Ltmp32-.Ltmp31
.Ltmp31:
.short 4360
.long 4107
.asciz "Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle"
.p2align 2
.Ltmp32:
.short .Ltmp34-.Ltmp33
.Ltmp33:
.short 4360
.long 4124
.asciz "Unity.Jobs.LowLevel.Unsafe.JobRanges"
.p2align 2
.Ltmp34:
.Ltmp26:
.p2align 2
.cv_filechecksums
.cv_stringtable
.long 241
.long .Ltmp36-.Ltmp35
.Ltmp35:
.short .Ltmp38-.Ltmp37
.Ltmp37:
.short 4428
.long 4130
.p2align 2
.Ltmp38:
.Ltmp36:
.p2align 2
.section .debug$T,"dr"
.p2align 2
.long 4
.short 0x32
.short 0x1505
.short 0x0
.short 0x80
.long 0x0
.long 0x0
.long 0x0
.short 0x0
.asciz "BurstInspectorGUITests/MyJob"
.byte 241
.short 0x46
.short 0x1505
.short 0x0
.short 0x80
.long 0x0
.long 0x0
.long 0x0
.short 0x0
.asciz "Unity.Collections.NativeArray`1<System.Single>"
.byte 243
.byte 242
.byte 241
.short 0x2a
.short 0x1203
.short 0x150d
.short 0x3
.long 0x1001
.short 0x0
.asciz "Inp\303\272t"
.byte 243
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x1001
.short 0x30
.asciz "Output"
.byte 243
.byte 242
.byte 241
.short 0x32
.short 0x1505
.short 0x2
.short 0x0
.long 0x1002
.long 0x0
.long 0x0
.short 0x60
.asciz "BurstInspectorGUITests/MyJob"
.byte 241
.short 0x42
.short 0x1605
.long 0x0
.asciz "C:\\UnitySrc\\burst\\src\\Unity.Burst.Tester\\unknown\\unknown"
.byte 243
.byte 242
.byte 241
.short 0xe
.short 0x1606
.long 0x1003
.long 0x1004
.long 0x0
.short 0x4a
.short 0x1505
.short 0x0
.short 0x80
.long 0x0
.long 0x0
.long 0x0
.short 0x0
.asciz "Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle"
.byte 241
.short 0x8a
.short 0x1203
.short 0x150d
.short 0x3
.long 0x620
.short 0x0
.asciz "m_Buffer"
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0x8
.asciz "m_Length"
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0xc
.asciz "m_MinIndex"
.byte 243
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0x10
.asciz "m_MaxIndex"
.byte 243
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x1006
.short 0x18
.asciz "m_Safety"
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0x28
.asciz "m_AllocatorLabel"
.byte 241
.short 0x46
.short 0x1505
.short 0x6
.short 0x0
.long 0x1007
.long 0x0
.long 0x0
.short 0x30
.asciz "Unity.Collections.NativeArray`1<System.Single>"
.byte 243
.byte 242
.byte 241
.short 0xe
.short 0x1606
.long 0x1008
.long 0x1004
.long 0x0
.short 0x4a
.short 0x1203
.short 0x150d
.short 0x3
.long 0x620
.short 0x0
.asciz "versionNode"
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0x8
.asciz "version"
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0xc
.asciz "staticSafetyId"
.byte 243
.byte 242
.byte 241
.short 0x4a
.short 0x1505
.short 0x3
.short 0x0
.long 0x100a
.long 0x0
.long 0x0
.short 0x10
.asciz "Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle"
.byte 241
.short 0xe
.short 0x1606
.long 0x100b
.long 0x1004
.long 0x0
.short 0xa
.short 0x1002
.long 0x1000
.long 0x1000c
.short 0x6
.short 0x1201
.long 0x0
.short 0x1a
.short 0x1009
.long 0x3
.long 0x1000
.long 0x100d
.byte 0x0
.byte 0x0
.short 0x0
.long 0x100e
.long 0x0
.short 0x32
.short 0x1602
.long 0x1000
.long 0x100f
.asciz "BurstInspectorGUITests.MyJob.Execute"
.byte 243
.byte 242
.byte 241
.short 0xa
.short 0x1002
.long 0x1001
.long 0x1000c
.short 0x1a
.short 0x1009
.long 0x74
.long 0x1001
.long 0x1011
.byte 0x0
.byte 0x0
.short 0x0
.long 0x100e
.long 0x0
.short 0x3e
.short 0x1602
.long 0x1001
.long 0x1012
.asciz "Unity.Collections.NativeArray`1<float>.get_Length"
.byte 242
.byte 241
.short 0xe
.short 0x1201
.long 0x2
.long 0x74
.long 0x40
.short 0x1a
.short 0x1009
.long 0x3
.long 0x1001
.long 0x1011
.byte 0x0
.byte 0x0
.short 0x2
.long 0x1014
.long 0x0
.short 0x3a
.short 0x1602
.long 0x1001
.long 0x1015
.asciz "Unity.Collections.NativeArray`1<float>.set_Item"
.short 0x3a
.short 0x1505
.short 0x0
.short 0x80
.long 0x0
.long 0x0
.long 0x0
.short 0x0
.asciz "Unity.Jobs.LowLevel.Unsafe.JobRanges"
.byte 241
.short 0xa
.short 0x1002
.long 0x1017
.long 0x1000c
.short 0x1a
.short 0x1201
.long 0x5
.long 0x100d
.long 0x620
.long 0x620
.long 0x1018
.long 0x74
.short 0xe
.short 0x1008
.long 0x3
.byte 0x0
.byte 0x0
.short 0x5
.long 0x1019
.short 0x62
.short 0x1203
.short 0x150d
.short 0x3
.long 0x74
.short 0x0
.asciz "BatchSize"
.short 0x150d
.short 0x3
.long 0x74
.short 0x4
.asciz "NumJobs"
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x74
.short 0x8
.asciz "TotalIterationCount"
.byte 242
.byte 241
.short 0x150d
.short 0x3
.long 0x620
.short 0x10
.asciz "StartEndIndex"
.short 0x3a
.short 0x1505
.short 0x4
.short 0x0
.long 0x101b
.long 0x0
.long 0x0
.short 0x18
.asciz "Unity.Jobs.LowLevel.Unsafe.JobRanges"
.byte 241
.short 0xe
.short 0x1606
.long 0x101c
.long 0x1004
.long 0x0
.short 0x17a
.short 0x1601
.long 0x0
.long 0x101a
.asciz "Unity.Jobs.IJobExtensions.JobStruct`1<BurstInspectorGUITests.MyJob>.Execute(ref BurstInspectorGUITests.MyJob data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) -> void_930e313844f708dd8e72e0cb41431524 from UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
.byte 241
.short 0x3a
.short 0x1605
.long 0x0
.asciz "C:\\UnitySrc\\burst\\src\\Unity.Burst.Tester\\unknown"
.byte 243
.byte 242
.byte 241
.short 0xe
.short 0x1605
.long 0x0
.asciz "unknown"
.short 0xa
.short 0x1605
.long 0x0
.byte 0
.byte 243
.byte 242
.byte 241
.short 0x1a
.short 0x1603
.short 0x5
.long 0x101f
.long 0x0
.long 0x1020
.long 0x1021
.long 0x0
.byte 242
.byte 241
.globl _fltused

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e98313e850883648a977ddbceaaca946
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dfcfd8358b733176a9227f0cf438af69
folderAsset: yes

View file

@ -0,0 +1,21 @@
{
"name": "OverloadedFunctionPointers",
"rootNamespace": "",
"references": [
"Unity.Burst"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"optionalUnityReferences": [
"TestAssemblies"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: ee94adce07250884391eeae8a50c225e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
namespace OverloadedFunctionPointers
{
#if UNITY_2021_2_OR_NEWER && UNITY_EDITOR
public unsafe struct Callable
{
public int Value;
private Callable(int x)
{
Value = x;
}
public static Callable Create<T1, T2>(delegate* unmanaged[Cdecl] < T1, T2, void > function) => new Callable(2);
public static Callable Create<T1, TRet>(delegate* unmanaged[Cdecl] < T1, TRet > function) => new Callable(3);
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 66ab7ac009493f37ae82d74f0f5f952e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc9ed20d6388316e806d5f03d0ab3b25
folderAsset: yes

View file

@ -0,0 +1,223 @@
using System;
using System.Diagnostics;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
/// <summary>
/// Shared class used for Unit tests and <see cref="MyBurstBehavior"/>
/// </summary>
[BurstCompile] // attribute added just to check that static methods are getting compiled
public class BurstJobTester2 : IDisposable
{
private NativeArray<float> _array;
private NativeArray<float> _arrayAsyncJobDefault;
private NativeArray<float> _arrayAsyncJobFast;
public BurstJobTester2()
{
_array = new NativeArray<float>(10, Allocator.Persistent);
_arrayAsyncJobDefault = new NativeArray<float>(10, Allocator.Persistent);
_arrayAsyncJobFast = new NativeArray<float>(10, Allocator.Persistent);
}
public void Dispose()
{
_array.Dispose();
_arrayAsyncJobDefault.Dispose();
_arrayAsyncJobFast.Dispose();
}
public float Calculate()
{
// Schedule the job on each frame to make sure that it will be compiled async on the next frame
_array[0] = 0.0f;
// Launch synchronous job
var job = new MyJob { Result = _array };
job.Schedule().Complete();
var rotation = job.Result[0];
// Launch an async compilation
var asyncJobNoOptim = new MyJobWithDefaultOptimizations() {Result = _arrayAsyncJobDefault};
var asyncJobFastOptim = new MyJobWithFastOptimizations() {Result = _arrayAsyncJobFast};
var asyncJobNoOptimHandle = asyncJobNoOptim.Schedule();
var asyncJobFastOptimHandle = asyncJobFastOptim.Schedule();
// Wait for async completion
asyncJobNoOptimHandle.Complete();
asyncJobFastOptimHandle.Complete();
return rotation;
}
public float CheckFunctionPointer()
{
var functionPointer1 = BurstCompiler.CompileFunctionPointer<Add2NumbersDelegate>(Add2Numbers);
var result = functionPointer1.Invoke(1.0f, 2.0f);
var functionPointer2 = BurstCompiler.CompileFunctionPointer<Add2NumbersDelegate>(Add2NumbersThrows);
return functionPointer2.Invoke(1.0f, 2.0f);
}
[BurstCompile(CompileSynchronously = true)] // attribute used for a static method
public static float Add2Numbers(float a, float b)
{
DiscardFunction(ref a);
DiscardFunction(ref b);
return a + b;
}
[BurstCompile(CompileSynchronously = true)] // attribute used for a static method
public static float Add2NumbersThrows(float a, float b)
{
DiscardFunction(ref a);
DiscardFunction(ref b);
if (a > 0) ThrowNewArgumentException();
return a + b;
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewArgumentException()
{
throw new ArgumentException("Invalid a must be < 0");
}
[BurstDiscard]
private static void DiscardFunction(ref float x)
{
x = 0;
}
public delegate float Add2NumbersDelegate(float a, float b);
[BurstCompile(CompileSynchronously = true)]
public struct MyJob : IJob
{
[WriteOnly]
public NativeArray<float> Result;
public void Execute()
{
Result[0] = ChangeValue();
EraseRotation();
}
// Use an indirection: Execute -> instance method -> static method
// (to check caching manually, change "1.0f" in ChangeValue() and 2.0f in ChangeValueStatic())
private float ChangeValue()
{
return 1.0f + ChangeValueStatic();
}
private static float ChangeValueStatic()
{
return 2.0f;
}
// Use BurstDiscard, if burst is not available, this method will get executed and it will make the cube static on the screen.
[BurstDiscard]
private void EraseRotation()
{
Result[0] = 0.0f;
}
// static method in a burst job, but we still want to compile separately
[BurstCompile(FloatMode = FloatMode.Deterministic, CompileSynchronously = true)]
public static float CheckFmaSlow(float a, float b, float c)
{
return a * b + c + math.sin(c);
}
// static method in a burst job, but we still want to compile separately
// Used only to check that compilation is working for different burst compile options
[BurstCompile(FloatPrecision.Low, FloatMode.Fast, CompileSynchronously = true)]
public static float CheckFmaFast(float a, float b, float c)
{
return a * b + c + math.sin(c);
}
}
[BurstCompile(CompileSynchronously = false)]
public struct MyJobAsync : IJob
{
[WriteOnly]
public NativeArray<float> Result;
public void Execute()
{
Result[0] = ChangeValue();
EraseRotation();
}
private float ChangeValue()
{
return 1.0f + ChangeValueStatic();
}
private static float ChangeValueStatic()
{
return 2.0f;
}
[BurstDiscard]
private void EraseRotation()
{
Result[0] = 0.0f;
}
}
[BurstCompile]
public struct MyJobWithDefaultOptimizations : IJob
{
public NativeArray<float> Result;
public void Execute()
{
Result[0] = math.cos(Result[0]);
}
}
/// <summary>
/// This Job is checking that we can allocate and dispose a NativeArray from a Burst Job
/// </summary>
[BurstCompile(CompileSynchronously = true)]
public struct MyJobCreatingAndDisposingNativeArray : IJob
{
public int Length;
public NativeArray<int> Result;
public void Execute()
{
var array = new NativeArray<float>(Length, Allocator.Temp);
for (int i = 0; i < array.Length; i++)
{
array[i] = i;
}
int result = array.Length;
array.Dispose();
DiscardFromManaged(ref result);
Result[0] = result;
}
[BurstDiscard]
public static void DiscardFromManaged(ref int result)
{
result = 0;
}
}
// Used only to check that compilation is working for different burst compile options
[BurstCompile(FloatPrecision.Low, FloatMode.Fast)]
public struct MyJobWithFastOptimizations : IJob
{
public NativeArray<float> Result;
public void Execute()
{
Result[0] = math.cos(Result[0]);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3decc8d6366d3d668648f9b79f2a980f
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,237 @@
using System;
using System.Runtime.InteropServices;
using AOT;
using NUnit.Framework;
using Unity.Burst;
using UnityEngine;
using UnityEngine.TestTools;
#if UNITY_2021_2_OR_NEWER
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
#if UNITY_EDITOR
using OverloadedFunctionPointers;
#endif
#endif
[TestFixture, BurstCompile]
public class FunctionPointerTests
{
[BurstCompile(CompileSynchronously = true)]
private static T StaticFunctionNoArgsGenericReturnType<T>()
{
return default;
}
private delegate int DelegateNoArgsIntReturnType();
[Test]
public void TestCompileFunctionPointerNoArgsGenericReturnType()
{
Assert.Throws<InvalidOperationException>(
() => BurstCompiler.CompileFunctionPointer<DelegateNoArgsIntReturnType>(StaticFunctionNoArgsGenericReturnType<int>),
"The method `Int32 StaticFunctionNoArgsGenericReturnType[Int32]()` must be a non-generic method");
}
[BurstCompile(CompileSynchronously = true)]
private static int StaticFunctionConcreteReturnType()
{
return default;
}
private delegate T DelegateGenericReturnType<T>();
[Test]
public void TestCompileFunctionPointerDelegateNoArgsGenericReturnType()
{
Assert.Throws<InvalidOperationException>(
() => BurstCompiler.CompileFunctionPointer<DelegateGenericReturnType<int>>(StaticFunctionConcreteReturnType),
"The delegate type `FunctionPointerTests+DelegateGenericReturnType`1[System.Int32]` must be a non-generic type");
}
private static class GenericClass<T>
{
public delegate int DelegateNoArgsIntReturnType();
}
[Test]
public void TestCompileFunctionPointerDelegateNoArgsGenericDeclaringType()
{
Assert.Throws<InvalidOperationException>(
() => BurstCompiler.CompileFunctionPointer<GenericClass<int>.DelegateNoArgsIntReturnType>(StaticFunctionConcreteReturnType),
"The delegate type `FunctionPointerTests+GenericClass`1+DelegateNoArgsIntReturnType[System.Int32]` must be a non-generic type");
}
// Doesn't work with IL2CPP yet - waiting for Unity fix to land. Once it does, remove `&& UNITY_EDITOR`
#if UNITY_2021_2_OR_NEWER && UNITY_EDITOR
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
[BurstCompile]
private static int CSharpFunctionPointerCallback(int value) => value * 2;
[BurstCompile(CompileSynchronously = true)]
public unsafe struct StructWithCSharpFunctionPointer : IJob
{
[NativeDisableUnsafePtrRestriction]
[ReadOnly]
public IntPtr Callback;
[ReadOnly]
public NativeArray<int> Input;
[WriteOnly]
public NativeArray<int> Output;
public void Execute()
{
delegate* unmanaged[Cdecl]<int, int> callback = (delegate* unmanaged[Cdecl]<int, int>)Callback;
Output[0] = callback(Input[0]);
}
}
[Test]
public unsafe void CSharpFunctionPointerInsideJobStructTest()
{
using (var input = new NativeArray<int>(new int[1] { 40 }, Allocator.Persistent))
using (var output = new NativeArray<int>(new int[1], Allocator.Persistent))
{
delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
var job = new StructWithCSharpFunctionPointer
{
Callback = (IntPtr)callback,
Input = input,
Output = output
};
job.Run();
Assert.AreEqual(40 * 2, output[0]);
}
}
[Test]
public unsafe void CSharpFunctionPointerInStaticMethodSignature()
{
var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerParameter>(EntryPointWithCSharpFunctionPointerParameter);
delegate* unmanaged[Cdecl]<int, int> callback = &CSharpFunctionPointerCallback;
var result = fp.Invoke((IntPtr)callback);
Assert.AreEqual(10, result);
}
[BurstCompile(CompileSynchronously = true)]
private static unsafe int EntryPointWithCSharpFunctionPointerParameter(IntPtr callback)
{
delegate* unmanaged[Cdecl]<int, int> typedCallback = (delegate* unmanaged[Cdecl]<int, int>)callback;
return typedCallback(5);
}
private unsafe delegate int DelegateWithCSharpFunctionPointerParameter(IntPtr callback);
[Test]
public unsafe void FunctionPointerReturnedFromBurstFunction()
{
var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerReturn>(EntryPointWithCSharpFunctionPointerReturn);
var fpInner = fp.Invoke();
delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> callback = (delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float>)fpInner;
var result = callback(1, 2, 4, 8, 16, 32);
Assert.AreEqual((float)(1 + 2 + 4 + 8 + 16 + 32), result);
}
[BurstCompile(CompileSynchronously = true)]
private static unsafe IntPtr EntryPointWithCSharpFunctionPointerReturn()
{
delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> fp = &EntryPointWithCSharpFunctionPointerReturnHelper;
return (IntPtr)fp;
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
[BurstCompile(CompileSynchronously = true)]
private static unsafe float EntryPointWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6)
{
return p1 + p2 + p3 + p4 + p5 + p6;
}
[BurstCompile]
[UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl)})]
static long UnmanagedFunction(long burstCount) => 1;
[BurstCompile]
static unsafe void GetUnmanagedCallableWithReturn(out Callable fn)
{
fn = Callable.Create<long, long>(&UnmanagedFunction);
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
public void CallOverloadedFunctionWithFpArg()
{
GetUnmanagedCallableWithReturn(out var a);
Assert.AreEqual(3, a.Value);
}
private delegate int Doer(int x);
static int DoCompileFunctionPointerNestedStaticMethod(int x)
{
[BurstCompile]
static int DoIt(int x) => x * 2 - 1;
return BurstCompiler.CompileFunctionPointer<Doer>(DoIt).Invoke(x);
}
[Test]
public void TestCompileFunctionPointerNestedStaticMethod()
{
Assert.AreEqual(3, DoCompileFunctionPointerNestedStaticMethod(2));
}
private unsafe delegate IntPtr DelegateWithCSharpFunctionPointerReturn();
// Note that there are 6 float parameters to try to catch any issues with calling conventions.
private unsafe delegate float DelegateWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6);
#endif
[Test]
public void TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttribute()
{
var fp = BurstCompiler.CompileFunctionPointer<TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate>(TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper);
var result = fp.Invoke(42);
Assert.AreEqual(43, result);
}
[BurstCompile(CompileSynchronously = true)]
private static int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeHelper(int x) => x + 1;
[MyCustomAttribute("Foo")]
private delegate int TestDelegateWithCustomAttributeThatIsNotUnmanagedFunctionPointerAttributeDelegate(int x);
private sealed class MyCustomAttributeAttribute : Attribute
{
public MyCustomAttributeAttribute(string param) { }
}
}
#if UNITY_2021_2_OR_NEWER
// UnmanagedCallersOnlyAttribute is new in .NET 5.0. This attribute is required
// when you declare an unmanaged function pointer with an explicit calling convention.
// Fortunately, Roslyn lets us declare the attribute class ourselves, and it will be used.
// Users will need this same declaration in their own projects, in order to use
// C# 9.0 function pointers.
namespace System.Runtime.InteropServices
{
[AttributeUsage(System.AttributeTargets.Method, Inherited = false)]
public sealed class UnmanagedCallersOnlyAttribute : Attribute
{
public Type[] CallConvs;
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c02f830887253c6eaf1f7780c59c5432
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,433 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using NUnit.Framework;
using System.Text.RegularExpressions;
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.TestTools;
using System.Runtime.CompilerServices;
namespace ExceptionsFromBurstJobs
{
[BurstCompile]
class ManagedExceptionsBurstJobs
{
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewArgumentException()
{
throw new ArgumentException("A");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowArgumentExceptionJob : IJob
{
public void Execute()
{
ThrowNewArgumentException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowArgumentException()
{
LogAssert.Expect(LogType.Exception, new Regex("ArgumentException: A"));
var jobData = new ThrowArgumentExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewArgumentNullException()
{
throw new ArgumentNullException("N");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowArgumentNullExceptionJob : IJob
{
public void Execute()
{
ThrowNewArgumentNullException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowArgumentNullException()
{
LogAssert.Expect(LogType.Exception, new Regex("System.ArgumentNullException: N"));
var jobData = new ThrowArgumentNullExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewNullReferenceException()
{
throw new NullReferenceException("N");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowNullReferenceExceptionJob : IJob
{
public void Execute()
{
ThrowNewNullReferenceException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowNullReferenceException()
{
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: N"));
var jobData = new ThrowNullReferenceExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewInvalidOperationException()
{
throw new InvalidOperationException("IO");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowInvalidOperationExceptionJob : IJob
{
public void Execute()
{
ThrowNewInvalidOperationException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowInvalidOperationException()
{
LogAssert.Expect(LogType.Exception, new Regex("InvalidOperationException: IO"));
var jobData = new ThrowInvalidOperationExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewNotSupportedException()
{
throw new NotSupportedException("NS");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowNotSupportedExceptionJob : IJob
{
public void Execute()
{
ThrowNewNotSupportedException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowNotSupportedException()
{
LogAssert.Expect(LogType.Exception, new Regex("NotSupportedException: NS"));
var jobData = new ThrowNotSupportedExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewUnityException()
{
throw new UnityException("UE");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowUnityExceptionJob : IJob
{
public void Execute()
{
ThrowNewUnityException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowUnityException()
{
LogAssert.Expect(LogType.Exception, new Regex("UnityException: UE"));
var jobData = new ThrowUnityExceptionJob();
jobData.Run();
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
private static void ThrowNewIndexOutOfRangeException()
{
throw new IndexOutOfRangeException("IOOR");
}
[BurstCompile(CompileSynchronously = true)]
struct ThrowIndexOutOfRangeExceptionJob : IJob
{
public void Execute()
{
ThrowNewIndexOutOfRangeException();
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowIndexOutOfRange()
{
LogAssert.Expect(LogType.Exception, new Regex("IndexOutOfRangeException: IOOR"));
var jobData = new ThrowIndexOutOfRangeExceptionJob();
jobData.Run();
}
[BurstCompile(CompileSynchronously = true)]
private unsafe struct ThrowFromDereferenceNullJob : IJob
{
[NativeDisableUnsafePtrRestriction]
public int* Ptr;
public void Execute()
{
*Ptr = 42;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowFromDereferenceNull()
{
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
var jobData = new ThrowFromDereferenceNullJob() { Ptr = null };
jobData.Run();
}
[BurstCompile(CompileSynchronously = true)]
private unsafe struct ThrowFromDivideByZeroJob : IJob
{
public int Int;
public void Execute()
{
Int = 42 / Int;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowFromDivideByZero()
{
if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
{
// Arm64 does not throw a divide-by-zero exception, instead it flushes the result to zero.
return;
}
LogAssert.Expect(LogType.Exception, new Regex("DivideByZeroException: Attempted to divide by zero"));
var jobData = new ThrowFromDivideByZeroJob() { Int = 0 };
jobData.Run();
}
private unsafe delegate void ExceptionDelegate(int* a);
[BurstCompile(CompileSynchronously = true)]
private static unsafe void DereferenceNull(int* a)
{
*a = 42;
}
[BurstCompile(CompileSynchronously = true)]
unsafe struct ThrowFromFunctionPointerJob : IJob
{
#pragma warning disable 649
[NativeDisableUnsafePtrRestriction] public IntPtr FuncPtr;
[NativeDisableUnsafePtrRestriction] public int* Ptr;
#pragma warning restore 649
public void Execute()
{
new FunctionPointer<ExceptionDelegate>(FuncPtr).Invoke(Ptr);
// Set Ptr to non null which should never be hit because the above will throw.
Ptr = (int*)0x42;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public unsafe void ThrowFromFunctionPointer()
{
var funcPtr = BurstCompiler.CompileFunctionPointer<ExceptionDelegate>(DereferenceNull);
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
var job = new ThrowFromFunctionPointerJob { FuncPtr = funcPtr.Value, Ptr = null };
job.Run();
Assert.AreEqual((IntPtr)job.Ptr, (IntPtr)0);
}
[BurstCompile(CompileSynchronously = true)]
private unsafe struct ThrowFromDereferenceNullParallelJob : IJobParallelFor
{
[NativeDisableUnsafePtrRestriction]
public int* Ptr;
public void Execute(int index)
{
*Ptr = index;
}
}
[Test]
// No RuntimePlatform.OSXEditor in this list because of a subtle Mojave only bug.
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowFromDereferenceNullParallel()
{
var messageCount = 0;
void OnMessage(string message, string stackTrace, LogType type)
{
Assert.AreEqual(LogType.Exception, type);
StringAssert.Contains("NullReferenceException: Object reference not set to an instance of an object", message);
messageCount++;
}
LogAssert.ignoreFailingMessages = true;
Application.logMessageReceivedThreaded += OnMessage;
try
{
var jobData = new ThrowFromDereferenceNullParallelJob() { Ptr = null };
jobData.Schedule(128, 1).Complete();
Assert.GreaterOrEqual(messageCount, 1);
}
finally
{
Application.logMessageReceivedThreaded -= OnMessage;
LogAssert.ignoreFailingMessages = false;
}
}
private unsafe struct ThrowFromDereferenceNullManagedJob : IJob
{
[NativeDisableUnsafePtrRestriction]
public int* Ptr;
public void Execute()
{
*Ptr = 42;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowFromDereferenceNullManaged()
{
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
var jobData = new ThrowFromDereferenceNullManagedJob() { Ptr = null };
jobData.Run();
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void ThrowFromDereferenceNullBurstDisabled()
{
var previous = BurstCompiler.Options.EnableBurstCompilation;
BurstCompiler.Options.EnableBurstCompilation = false;
LogAssert.Expect(LogType.Exception, new Regex("NullReferenceException: Object reference not set to an instance of an object"));
var jobData = new ThrowFromDereferenceNullJob() { Ptr = null };
jobData.Run();
BurstCompiler.Options.EnableBurstCompilation = previous;
}
[BurstCompile]
struct Thrower : IJob
{
public int X;
[BurstCompile(CompileSynchronously = true)]
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ConditionalThrowWithSideEffect(int x)
{
if (x == -1)
throw new InvalidOperationException();
UnityEngine.Debug.Log("wow");
throw new InvalidOperationException();
}
public void Execute()
{
ConditionalThrowWithSideEffect(X);
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void TestConditionalThrowWithSideEffect()
{
LogAssert.Expect(LogType.Log, "wow");
LogAssert.Expect(LogType.Exception, new Regex(".+InvalidOperation.+"));
new Thrower() { X = 0 }.Run();
}
private unsafe struct ThrowFromManagedStackOverflowJob : IJob
{
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static int DoStackOverflow(ref int x)
{
// Copy just to make the stack grow.
var copy = x;
return copy + DoStackOverflow(ref x);
}
public int Int;
public void Execute()
{
Int = DoStackOverflow(ref Int);
}
}
//[Test]
//[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
public void ThrowFromManagedStackOverflow()
{
LogAssert.Expect(LogType.Exception, new Regex("StackOverflowException: The requested operation caused a stack overflow"));
var jobData = new ThrowFromManagedStackOverflowJob() { Int = 1 };
jobData.Run();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d54dbb53d4313cc8b7d8289a75202585
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,94 @@
using NUnit.Framework;
using System.Text.RegularExpressions;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.TestTools;
namespace ExceptionsFromBurstJobs
{
class NativeTriggeredManagedExceptionsBurstJobs
{
[BurstCompile(CompileSynchronously = true)]
struct RaiseMonoExceptionJob : IJob
{
public float output;
public void Execute()
{
output = Time.deltaTime;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void RaiseMonoException()
{
var job = new RaiseMonoExceptionJob();
LogAssert.Expect(LogType.Exception, new Regex(
"UnityEngine::UnityException: get_deltaTime can only be called from the main thread." + "[\\s]*" +
"Constructors and field initializers will be executed from the loading thread when loading a scene." + "[\\s]*" +
"Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function." + "[\\s]*" +
"This Exception was thrown from a job compiled with Burst, which has limited exception support."
));
job.Run();
}
[BurstCompile(CompileSynchronously = true)]
struct RaiseInvalidOperationExceptionJob : IJob
{
[ReadOnly]
public NativeArray<int> test;
public void Execute()
{
test[0] = 5;
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
public void RaiseInvalidOperationException()
{
var jobData = new RaiseInvalidOperationExceptionJob();
var testArray = new NativeArray<int>(1, Allocator.Persistent);
jobData.test = testArray;
LogAssert.Expect(LogType.Exception, new Regex(
"System::InvalidOperationException: The .+ has been declared as \\[ReadOnly\\] in the job( .+)?, but you are writing to it\\." + "[\\s]*" +
"This Exception was thrown from a job compiled with Burst, which has limited exception support."
));
jobData.Run();
testArray.Dispose();
}
[BurstCompile(CompileSynchronously = true)]
unsafe struct RaiseArgumentNullExceptionJob : IJob
{
#pragma warning disable 649
[NativeDisableUnsafePtrRestriction] public void* dst;
#pragma warning restore 649
public void Execute()
{
UnsafeUtility.MemCpy(dst, null, 10);
}
}
[Test]
[UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)]
[Description("Requires ENABLE_UNITY_COLLECTIONS_CHECKS which is currently only enabled in the Editor")]
unsafe public void RaiseArgumentNullException()
{
var jobData = new RaiseArgumentNullExceptionJob();
jobData.dst = UnsafeUtility.Malloc(10, 4, Allocator.Temp);
LogAssert.Expect(LogType.Exception, new Regex(
"System.ArgumentNullException: source" + "[\\s]*" +
"This Exception was thrown from a job compiled with Burst, which has limited exception support."
));
jobData.Run();
UnsafeUtility.Free(jobData.dst, Allocator.Temp);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac74b11ef46b35938b5d3ca8a4109efa
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,289 @@
using System.Collections;
using NUnit.Framework;
using Unity.Burst;
using UnityEngine;
using Unity.Jobs.LowLevel.Unsafe;
using UnityEngine.TestTools;
using System;
using Unity.Jobs;
[TestFixture]
public class PlaymodeTest
{
// [UnityTest]
public IEnumerator CheckBurstJobEnabledDisabled()
{
BurstCompiler.Options.EnableBurstCompileSynchronously = true;
foreach(var item in CheckBurstJobDisabled()) yield return item;
foreach(var item in CheckBurstJobEnabled()) yield return item;
}
private IEnumerable CheckBurstJobEnabled()
{
BurstCompiler.Options.EnableBurstCompilation = true;
yield return null;
using (var jobTester = new BurstJobTester2())
{
var result = jobTester.Calculate();
Assert.AreNotEqual(0.0f, result);
}
}
private IEnumerable CheckBurstJobDisabled()
{
BurstCompiler.Options.EnableBurstCompilation = false;
yield return null;
using (var jobTester = new BurstJobTester2())
{
var result = jobTester.Calculate();
Assert.AreEqual(0.0f, result);
}
}
[BurstCompile(CompileSynchronously = true)]
private struct ThrowingJob : IJob
{
public int I;
public void Execute()
{
if (I < 0)
{
throw new System.Exception("Some Exception!");
}
}
}
[Test]
public void NoSafetyCheckExceptionWarningInEditor()
{
var job = new ThrowingJob { I = 42 };
job.Schedule().Complete();
// UNITY_BURST_DEBUG enables additional logging which messes with our check.
if (null == System.Environment.GetEnvironmentVariable("UNITY_BURST_DEBUG"))
{
LogAssert.NoUnexpectedReceived();
}
}
private struct MyKey { public struct MySubKey0 { } public struct MySubKey1 { } }
private struct SomeGenericStruct<T> {}
private static readonly SharedStatic<int> SharedStaticOneType = SharedStatic<int>.GetOrCreate<MyKey>();
private static readonly SharedStatic<double> SharedStaticTwoTypes0 = SharedStatic<double>.GetOrCreate<MyKey, MyKey.MySubKey0>();
private static readonly SharedStatic<double> SharedStaticTwoTypes1 = SharedStatic<double>.GetOrCreate<MyKey, MyKey.MySubKey1>();
private struct MyGenericContainingStruct<T>
{
public static readonly SharedStatic<int> Data0 = SharedStatic<int>.GetOrCreate<T>();
public static readonly SharedStatic<int> Data1 = SharedStatic<int>.GetOrCreate<SomeGenericStruct<MyKey>, T>();
public static readonly SharedStatic<int> Data2 = SharedStatic<int>.GetOrCreate<SomeGenericStruct<T>, MyKey>();
}
private static readonly SharedStatic<int> SharedStaticWithSystemTypes0 = SharedStatic<int>.GetOrCreate<IntPtr>();
private static readonly SharedStatic<int> SharedStaticWithSystemTypes1 = SharedStatic<int>.GetOrCreate<IntPtr, MyKey>();
private static readonly SharedStatic<int> SharedStaticWithSystemTypes2 = SharedStatic<int>.GetOrCreate<MyKey, IntPtr>();
private static readonly SharedStatic<int> SharedStaticWithSystemTypes3 = SharedStatic<int>.GetOrCreate<IntPtr, IntPtr>();
[Test]
public unsafe void SharedStaticPostProcessedTests()
{
var oneType = SharedStatic<int>.GetOrCreate(typeof(MyKey));
Assert.AreEqual((IntPtr)oneType.UnsafeDataPointer, (IntPtr)SharedStaticOneType.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)oneType.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes0.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)oneType.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes1.UnsafeDataPointer);
var twoTypes0 = SharedStatic<double>.GetOrCreate(typeof(MyKey), typeof(MyKey.MySubKey0));
Assert.AreEqual((IntPtr)twoTypes0.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes0.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)twoTypes0.UnsafeDataPointer, (IntPtr)SharedStaticOneType.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)twoTypes0.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes1.UnsafeDataPointer);
var twoTypes1 = SharedStatic<double>.GetOrCreate(typeof(MyKey), typeof(MyKey.MySubKey1));
Assert.AreEqual((IntPtr)twoTypes1.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes1.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)twoTypes1.UnsafeDataPointer, (IntPtr)SharedStaticOneType.UnsafeDataPointer);
Assert.AreNotEqual((IntPtr)twoTypes1.UnsafeDataPointer, (IntPtr)SharedStaticTwoTypes0.UnsafeDataPointer);
// A shared static in a generic struct, that uses the same type for `GetOrCreate`, will resolve to the same shared static.
Assert.AreEqual((IntPtr)oneType.UnsafeDataPointer, (IntPtr)MyGenericContainingStruct<MyKey>.Data0.UnsafeDataPointer);
// These two test partial evaluations of shared statics (where we can evaluate one of the template arguments at ILPP time
// but not both).
Assert.AreEqual(
(IntPtr)MyGenericContainingStruct<MyKey>.Data1.UnsafeDataPointer,
(IntPtr)MyGenericContainingStruct<MyKey>.Data2.UnsafeDataPointer);
// Check that system type evaluations all match up.
Assert.AreEqual(
(IntPtr)SharedStatic<int>.GetOrCreate(typeof(IntPtr)).UnsafeDataPointer,
(IntPtr)SharedStaticWithSystemTypes0.UnsafeDataPointer);
Assert.AreEqual(
(IntPtr)SharedStatic<int>.GetOrCreate(typeof(IntPtr), typeof(MyKey)).UnsafeDataPointer,
(IntPtr)SharedStaticWithSystemTypes1.UnsafeDataPointer);
Assert.AreEqual(
(IntPtr)SharedStatic<int>.GetOrCreate(typeof(MyKey), typeof(IntPtr)).UnsafeDataPointer,
(IntPtr)SharedStaticWithSystemTypes2.UnsafeDataPointer);
Assert.AreEqual(
(IntPtr)SharedStatic<int>.GetOrCreate(typeof(IntPtr), typeof(IntPtr)).UnsafeDataPointer,
(IntPtr)SharedStaticWithSystemTypes3.UnsafeDataPointer);
}
[BurstCompile]
public struct SomeFunctionPointers
{
[BurstDiscard]
private static void MessWith(ref int a) => a += 13;
[BurstCompile]
public static int A(int a, int b)
{
MessWith(ref a);
return a + b;
}
[BurstCompile(DisableDirectCall = true)]
public static int B(int a, int b)
{
MessWith(ref a);
return a - b;
}
[BurstCompile(CompileSynchronously = true)]
public static int C(int a, int b)
{
MessWith(ref a);
return a * b;
}
[BurstCompile(CompileSynchronously = true, DisableDirectCall = true)]
public static int D(int a, int b)
{
MessWith(ref a);
return a / b;
}
public delegate int Delegate(int a, int b);
}
[Test]
public void TestDirectCalls()
{
Assert.IsTrue(BurstCompiler.IsEnabled);
// a can either be (42 + 13) + 53 or 42 + 53 (depending on whether it was burst compiled).
var a = SomeFunctionPointers.A(42, 53);
Assert.IsTrue((a == ((42 + 13) + 53)) || (a == (42 + 53)));
// b can only be (42 + 13) - 53, because direct call is disabled and so we always call the managed method.
var b = SomeFunctionPointers.B(42, 53);
Assert.AreEqual((42 + 13) - 53, b);
// c can only be 42 * 53, because synchronous compilation is enabled.
var c = SomeFunctionPointers.C(42, 53);
Assert.AreEqual(42 * 53, c);
// d can only be (42 + 13) / 53, because even though synchronous compilation is enabled, direct call is disabled.
var d = SomeFunctionPointers.D(42, 53);
Assert.AreEqual((42 + 13) / 53, d);
}
[Test]
public void TestDirectCallInNamespacedClass()
{
void onCompileILPPMethod2()
{
Assert.Fail("BurstCompiler.CompileILPPMethod2 should not have been called at this time");
}
// We expect BurstCompiler.CompileILPPMethod2 to have been called at startup, via
// [InitializeOnLoad] or [RuntimeInitializeOnLoadMethod]. If it's called when we invoke
// N.C.A(), then something has gone wrong.
try
{
BurstCompiler.OnCompileILPPMethod2 += onCompileILPPMethod2;
var result = N.C.A();
Assert.AreEqual(42, result);
}
finally
{
BurstCompiler.OnCompileILPPMethod2 -= onCompileILPPMethod2;
}
}
[Test]
public void TestFunctionPointers()
{
Assert.IsTrue(BurstCompiler.IsEnabled);
var A = BurstCompiler.CompileFunctionPointer<SomeFunctionPointers.Delegate>(SomeFunctionPointers.A);
var B = BurstCompiler.CompileFunctionPointer<SomeFunctionPointers.Delegate>(SomeFunctionPointers.B);
var C = BurstCompiler.CompileFunctionPointer<SomeFunctionPointers.Delegate>(SomeFunctionPointers.C);
var D = BurstCompiler.CompileFunctionPointer<SomeFunctionPointers.Delegate>(SomeFunctionPointers.D);
// a can either be (42 + 13) + 53 or 42 + 53 (depending on whether it was burst compiled).
var a = A.Invoke(42, 53);
Assert.IsTrue((a == ((42 + 13) + 53)) || (a == (42 + 53)));
// b can either be (42 + 13) - 53 or 42 - 53 (depending on whether it was burst compiled).
var b = B.Invoke(42, 53);
Assert.IsTrue((b == ((42 + 13) - 53)) || (b == (42 - 53)));
// c can only be 42 * 53, because synchronous compilation is enabled.
var c = C.Invoke(42, 53);
Assert.AreEqual(42 * 53, c);
// d can only be 42 / 53, because synchronous compilation is enabled.
var d = D.Invoke(42, 53);
Assert.AreEqual(42 / 53, d);
}
[BurstCompile]
public static class GenericClass<T>
{
[BurstCompile]
public static int ConcreteMethod() => 3;
}
public delegate int NoArgsIntReturnDelegate();
[Test]
public void TestGenericClassConcreteMethodFunctionPointer()
{
Assert.IsTrue(BurstCompiler.IsEnabled);
var F = BurstCompiler.CompileFunctionPointer<NoArgsIntReturnDelegate>(GenericClass<int>.ConcreteMethod);
Assert.AreEqual(3, F.Invoke());
}
}
// This test class is intentionally in a namespace to ensure that our
// direct-call [RuntimeInitializeOnLoadMethod] works correctly in that
// scenario.
namespace N
{
[BurstCompile]
internal static class C
{
public static int A() => B();
[BurstCompile(CompileSynchronously = true)]
private static int B()
{
var x = 42;
DiscardedMethod(ref x);
return x;
}
[BurstDiscard]
private static void DiscardedMethod(ref int x)
{
x += 1;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb7078968c7e3322a80bb099e19fb2e8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 30b8c33e57eb30adbfd9b640a4edaef8
folderAsset: yes

View file

@ -0,0 +1,246 @@
using NUnit.Framework;
using System;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Mathematics;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests types
/// </summary>
[BurstCompile]
internal class NotSupported
{
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_OnlyStaticMethodsAllowed)]
public int InstanceMethod()
{
return 1;
}
[TestCompiler(1, ExpectCompilerException = true, ExpectedDiagnosticIds = new[] { DiagnosticId.ERR_CallingManagedMethodNotSupported })]
public static int TestDelegate(int data)
{
return ProcessData(i => i + 1, data);
}
private static int ProcessData(Func<int, int> yo, int value)
{
return yo(value);
}
public struct HasMarshalAttribute
{
[MarshalAs(UnmanagedType.U1)] public bool A;
}
//[TestCompiler(ExpectCompilerException = true)]
[TestCompiler] // Because MarshalAs is used in mathematics we cannot disable it for now
public static void TestStructWithMarshalAs()
{
#pragma warning disable 0219
var x = new HasMarshalAttribute();
#pragma warning restore 0219
}
public struct HasMarshalAsSysIntAttribute
{
[MarshalAs(UnmanagedType.SysInt)] public bool A;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MarshalAsOnFieldNotSupported)]
public static void TestStructWithMarshalAsSysInt()
{
#pragma warning disable 0219
var x = new HasMarshalAsSysIntAttribute();
#pragma warning restore 0219
}
[TestCompiler(42, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MarshalAsOnParameterNotSupported)]
public static void TestMethodWithMarshalAsParameter([MarshalAs(UnmanagedType.I8)] int x)
{
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MarshalAsOnReturnTypeNotSupported)]
[return: MarshalAs(UnmanagedType.I8)]
public static int TestMethodWithMarshalAsReturnType()
{
return 42;
}
private static float3 a = new float3(1, 2, 3);
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromNonReadonlyStaticFieldNotSupported)]
public static bool TestStaticLoad()
{
var cmp = a == new float3(1, 2, 3);
return cmp.x && cmp.y && cmp.z;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromManagedNonReadonlyStaticFieldNotSupported)]
public static void TestStaticStore()
{
a.x = 42;
}
private interface ISomething
{
void DoSomething();
}
private struct Something : ISomething
{
public byte A;
public void DoSomething()
{
A = 42;
}
}
private static ISomething something = new Something { A = 13 };
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromManagedNonReadonlyStaticFieldNotSupported)]
public static void TestStaticInterfaceStore()
{
something.DoSomething();
}
private static int i = 42;
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromNonReadonlyStaticFieldNotSupported)]
public static int TestStaticIntLoad()
{
return i;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_InstructionStsfldNotSupported)]
public static void TestStaticIntStore()
{
i = 13;
}
public delegate char CharbyValueDelegate(char c);
#if BURST_TESTS_ONLY
[BurstCompile]
#endif
public static char CharbyValue(char c)
{
return c;
}
public struct CharbyValueFunc : IFunctionPointerProvider
{
public FunctionPointer<CharbyValueDelegate> FunctionPointer;
public object FromIntPtr(IntPtr ptr)
{
return new CharbyValueFunc() { FunctionPointer = new FunctionPointer<CharbyValueDelegate>(ptr) };
}
}
[TestCompiler(nameof(CharbyValue), 0x1234, ExpectCompilerException = true, ExpectedDiagnosticIds = new[] { DiagnosticId.ERR_TypeNotBlittableForFunctionPointer, DiagnosticId.ERR_StructsWithNonUnicodeCharsNotSupported })]
public static int TestCharbyValue(ref CharbyValueFunc fp, int i)
{
var c = (char)i;
return fp.FunctionPointer.Invoke(c);
}
struct Halfs
{
public static readonly half3 h3_h = new half3(new half(42.0f));
public static readonly half3 h3_d = new half3(0.5);
public static readonly half3 h3_v2s = new half3(new half2(new half(1.0f), new half(2.0f)), new half(0.5f));
public static readonly half3 h3_sv2 = new half3(new half(0.5f), new half2(new half(1.0f), new half(2.0f)));
public static readonly half3 h3_v3 = new half3(new half(0.5f), new half(42.0f), new half(13.0f));
}
[TestCompiler]
public static float TestStaticHalf3()
{
var result = (float3)Halfs.h3_h + Halfs.h3_d + Halfs.h3_v2s + Halfs.h3_sv2 + Halfs.h3_v3;
return result.x + result.y + result.z;
}
[TestCompiler(42, 13, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_AssertTypeNotSupported)]
public static void TestAreEqual(int a, int b)
{
Assert.AreEqual(a, b, "unsupported", new object[0]);
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_InstructionLdtokenTypeNotSupported)]
public static bool TestTypeof()
{
return typeof(int).IsPrimitive;
}
public class AwfulClass
{
public int Foo;
}
public struct BetterStruct
{
public int Foo;
}
public struct MixedStaticInits
{
public static readonly AwfulClass AC = new AwfulClass { Foo = 42 };
public static readonly BetterStruct BS = new BetterStruct { Foo = 42 };
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticIds = new[] { DiagnosticId.ERR_InstructionNewobjWithManagedTypeNotSupported, DiagnosticId.ERR_ManagedStaticConstructor })]
public static int TestMixedStaticInits()
{
return MixedStaticInits.BS.Foo;
}
public struct StaticArrayWrapper
{
private const int ArrayLength = 4;
public static readonly int[] StaticArray = new int[4];
static StaticArrayWrapper()
{
for (int i = 0; i < ArrayLength; ++i)
{
StaticArray[i] = i;
}
}
}
[TestCompiler]
public unsafe static int TestStaticArrayWrapper()
{
return StaticArrayWrapper.StaticArray[0];
}
class NestedArrayHolder
{
public static readonly int4[][] SomeOffsetThing =
{
new[] {new int4(0), new int4(0, 0, 1, 0), new int4(0, 1, 0, 0), new int4(0, 1, 1, 0)},
new[] {new int4(0), new int4(1, 0, 0, 0), new int4(0, 0, 1, 0), new int4(1, 0, 1, 0)},
new[] {new int4(0), new int4(0, 1, 0, 0), new int4(1, 0, 0, 0), new int4(1, 1, 0, 0)},
};
}
[TestCompiler]
public unsafe static int TestNestedManagedArrays()
{
return NestedArrayHolder.SomeOffsetThing[0][0].x;
}
public static readonly int[,] SomeMultiDimensionalThing = new int[2, 4]
{
{ 1, 2, 3, 4 },
{ -1, -2, -3, -4 },
};
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticIds = new[] { DiagnosticId.ERR_ConstructorNotSupported, DiagnosticId.ERR_MultiDimensionalArrayUnsupported })]
public static int TestMultiDimensionalArray() => SomeMultiDimensionalThing[1, 1];
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5eb12402c8b53dbaa40d67a6ccd1b4f6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,158 @@
using System;
using Unity.Collections.LowLevel.Unsafe;
namespace Burst.Compiler.IL.Tests
{
#if UNITY_2021_2_OR_NEWER || BURST_INTERNAL
/// <summary>
/// Test <see cref="System.ReadOnlySpan{T}"/>.
/// </summary>
internal partial class ReadOnlySpan
{
[TestCompiler]
public static int CreateDefault()
{
var span = new ReadOnlySpan<int>();
return span.Length;
}
[TestCompiler]
public static int CreateStackalloc()
{
ReadOnlySpan<int> span = stackalloc int[42];
return span.Length;
}
[TestCompiler(42)]
public static int CreateFromNullPointer(int size)
{
ReadOnlySpan<double> span;
unsafe
{
span = new ReadOnlySpan<double>(null, size);
}
return span.Length;
}
[TestCompiler]
public static unsafe double CreateFromMalloc()
{
double* malloc = (double*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<double>(), UnsafeUtility.AlignOf<double>(), Unity.Collections.Allocator.Persistent);
*malloc = 42.0f;
var span = new ReadOnlySpan<double>(malloc, 1);
double result = span[0];
UnsafeUtility.Free(malloc, Unity.Collections.Allocator.Persistent);
return result;
}
[TestCompiler]
public static int GetItem()
{
ReadOnlySpan<int> span = stackalloc int[42];
return span[41];
}
[TestCompiler]
public static int SliceFromStart()
{
ReadOnlySpan<int> span = stackalloc int[42];
var newSpan = span.Slice(10);
return newSpan[0] + newSpan.Length;
}
[TestCompiler]
public static int SliceFromStartWithLength()
{
ReadOnlySpan<int> span = stackalloc int[42];
var newSpan = span.Slice(10, 4);
return newSpan[3] + newSpan.Length;
}
[TestCompiler]
public static int CopyTo()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
ReadOnlySpan<int> other = stackalloc int[4];
other.CopyTo(span);
int result = 0;
for (int i = 0; i < span.Length; i++)
{
result += span[i];
}
return result;
}
[TestCompiler]
public static int IsEmpty() => new ReadOnlySpan<int>().IsEmpty ? 1 : 0;
[TestCompiler]
public static int Empty() => ReadOnlySpan<double>.Empty.Length;
[TestCompiler]
public static int GetEnumerator()
{
ReadOnlySpan<int> span = stackalloc int[42];
int result = 0;
var enumerator = span.GetEnumerator();
while (enumerator.MoveNext())
{
result += enumerator.Current;
}
return result;
}
[TestCompiler]
public static int OperatorEquality() => new ReadOnlySpan<double>() == ReadOnlySpan<double>.Empty ? 1 : 0;
[TestCompiler]
public static int OperatorInEquality() => new ReadOnlySpan<double>() != ReadOnlySpan<double>.Empty ? 1 : 0;
[TestCompiler]
public static int Fixed()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
ReadOnlySpan<int> readOnlySpan = span;
unsafe
{
fixed (int* ptr = readOnlySpan)
{
return ptr[41];
}
}
}
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0bed5c1e3949386481f894d4b987c939
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,227 @@
using System;
using Unity.Collections.LowLevel.Unsafe;
namespace Burst.Compiler.IL.Tests
{
#if UNITY_2021_2_OR_NEWER || BURST_INTERNAL
/// <summary>
/// Test <see cref="System.Span{T}"/>.
/// </summary>
internal partial class Span
{
[TestCompiler]
public static int CreateDefault()
{
var span = new Span<int>();
return span.Length;
}
[TestCompiler]
public static int CreateStackalloc()
{
Span<int> span = stackalloc int[42];
return span.Length;
}
[TestCompiler(42)]
public static int CreateFromNullPointer(int size)
{
Span<double> span;
unsafe
{
span = new Span<double>(null, size);
}
return span.Length;
}
[TestCompiler]
public static unsafe double CreateFromMalloc()
{
double* malloc = (double*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<double>(), UnsafeUtility.AlignOf<double>(), Unity.Collections.Allocator.Persistent);
*malloc = 42.0f;
Span<double> span = new Span<double>(malloc, 1);
double result = span[0];
UnsafeUtility.Free(malloc, Unity.Collections.Allocator.Persistent);
return result;
}
[TestCompiler]
public static int GetItem()
{
Span<int> span = stackalloc int[42];
return span[41];
}
[TestCompiler]
public static int SetItem()
{
Span<int> span = stackalloc int[42];
span[41] = 13;
return span[41];
}
[TestCompiler]
public static int Clear()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
span.Clear();
int result = 0;
for (int i = 0; i < span.Length; i++)
{
result += span[i];
}
return result;
}
[TestCompiler]
public static int SliceFromStart()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
var newSpan = span.Slice(10);
return newSpan[0] + newSpan.Length;
}
[TestCompiler]
public static int SliceFromStartWithLength()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
var newSpan = span.Slice(10, 4);
return newSpan[3] + newSpan.Length;
}
[TestCompiler]
public static int CopyTo()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
Span<int> other = stackalloc int[4];
for (int i = 0; i < other.Length; i++)
{
other[i] = -i - 1;
}
other.CopyTo(span);
int result = 0;
for (int i = 0; i < span.Length; i++)
{
result += span[i];
}
return result;
}
[TestCompiler]
public static int Fill()
{
Span<int> span = stackalloc int[42];
span.Fill(123);
int result = 0;
for (int i = 0; i < span.Length; i++)
{
result += span[i];
}
return result;
}
[TestCompiler]
public static int IsEmpty() => new Span<int>().IsEmpty ? 1 : 0;
[TestCompiler]
public static int Empty() => Span<double>.Empty.Length;
[TestCompiler]
public static int GetEnumerator()
{
Span<int> span = stackalloc int[42];
int result = 0;
var enumerator = span.GetEnumerator();
while (enumerator.MoveNext())
{
result += enumerator.Current;
}
return result;
}
[TestCompiler]
public static int OperatorEquality() => new Span<double>() == Span<double>.Empty ? 1 : 0;
[TestCompiler]
public static int OperatorInEquality() => new Span<double>() != Span<double>.Empty ? 1 : 0;
[TestCompiler]
public static int OperatorImplicit()
{
ReadOnlySpan<double> span = new Span<double>();
return span.Length;
}
[TestCompiler]
public static int Fixed()
{
Span<int> span = stackalloc int[42];
for (int i = 0; i < span.Length; i++)
{
span[i] = i;
}
unsafe
{
fixed (int* ptr = span)
{
*ptr = 42;
return ptr[41];
}
}
}
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fec1bb67579b3feda3e2fddd7ecbc304
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,542 @@
using System;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst;
using UnityBenchShared;
using System.Runtime.CompilerServices;
#if UNITY_2022_1_OR_NEWER
// Enables support for { init; } keyword globally: https://docs.unity3d.com/2022.1/Documentation/Manual/CSharpCompiler.html
namespace System.Runtime.CompilerServices
{
[ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)]
public static class IsExternalInit { }
}
#endif
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests types
/// </summary>
internal partial class Types
{
[TestCompiler]
public static int Bool()
{
return sizeof(bool);
}
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolArgAndReturn(bool value)
{
return !value;
}
private static bool BoolArgAndReturnSubFunction(bool value)
{
return !value;
}
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolArgAndReturnCall(bool value)
{
return BoolArgAndReturnSubFunction(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
[return: MarshalAs(UnmanagedType.U1)]
private static bool BoolMarshalAsU1(bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsU1Call(bool value)
{
return BoolMarshalAsU1(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
[return: MarshalAs(UnmanagedType.I1)]
private static bool BoolMarshalAsI1(bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsI1Call(bool value)
{
return BoolMarshalAsI1(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
[return: MarshalAs(UnmanagedType.R4)]
private static bool BoolMarshalAsR4(bool b) => b;
[TestCompiler(true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MarshalAsNativeTypeOnReturnTypeNotSupported)]
public static bool BoolMarshalAsR4Call(bool value)
{
return BoolMarshalAsR4(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool BoolMarshalAsU1Param([MarshalAs(UnmanagedType.U1)] bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsU1CallParam(bool value)
{
return BoolMarshalAsU1Param(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool BoolMarshalAsI1Param([MarshalAs(UnmanagedType.I1)] bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsI1CallParam(bool value)
{
return BoolMarshalAsI1Param(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool BoolMarshalAsR4Param([MarshalAs(UnmanagedType.R4)] bool b) => b;
[TestCompiler(true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MarshalAsOnParameterNotSupported)]
public static bool BoolMarshalAsR4CallParam(bool value)
{
return BoolMarshalAsR4Param(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
[return: MarshalAs(UnmanagedType.U1)]
private static bool BoolMarshalAsU1AndI1Param([MarshalAs(UnmanagedType.I1)] bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsU1AndI1CallParam(bool value)
{
return BoolMarshalAsU1AndI1Param(value);
}
[MethodImpl(MethodImplOptions.NoInlining)]
[return: MarshalAs(UnmanagedType.I1)]
private static bool BoolMarshalAsI1AndU1Param([MarshalAs(UnmanagedType.U1)] bool b) => b;
[TestCompiler(true)]
[TestCompiler(false)]
public static bool BoolMarshalAsI1AndU1CallParam(bool value)
{
return BoolMarshalAsI1AndU1Param(value);
}
[TestCompiler]
public static int Char()
{
return sizeof(char);
}
[TestCompiler]
public static int Int8()
{
return sizeof(sbyte);
}
[TestCompiler]
public static int Int16()
{
return sizeof(short);
}
[TestCompiler]
public static int Int32()
{
return sizeof(int);
}
[TestCompiler]
public static int Int64()
{
return sizeof(long);
}
[TestCompiler]
public static int UInt8()
{
return sizeof(byte);
}
[TestCompiler]
public static int UInt16()
{
return sizeof(ushort);
}
[TestCompiler]
public static int UInt32()
{
return sizeof(uint);
}
[TestCompiler]
public static int UInt64()
{
return sizeof(ulong);
}
[TestCompiler]
public static int EnumSizeOf()
{
return sizeof(MyEnum);
}
[TestCompiler]
public static int EnumByteSizeOf()
{
return sizeof(MyEnumByte);
}
[TestCompiler(MyEnumByte.Tada2)]
public static MyEnumByte CheckEnumByte(ref MyEnumByte value)
{
// Check stloc for enum
value = MyEnumByte.Tada1;
return value;
}
[TestCompiler(MyEnum.Value15)]
public static int EnumByParam(MyEnum value)
{
return 1 + (int)value;
}
[TestCompiler]
public static float Struct()
{
var value = new MyStruct(1,2,3,4);
return value.x + value.y + value.z + value.w;
}
[TestCompiler]
public static long StructAccess()
{
var s = new InterleavedBoolStruct();
s.b1 = true;
s.i2 = -1;
s.b3 = true;
s.i5 = 3;
return s.i5;
}
[TestCompiler(true)]
[TestCompiler(false)]
public static bool StructWithBool(bool value)
{
// This method test that storage of boolean between local and struct is working
// (as they could have different layout)
var b = new BoolStruct();
b.b1 = !value;
return b.b1;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_CallingManagedMethodNotSupported)]
public static int TestUsingReferenceType()
{
return "this is not supported by burst".Length;
}
private struct MyStruct
{
public MyStruct(float x, float y, float z, float w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
public float x;
public float y;
public float z;
public float w;
}
private struct BoolStruct
{
#pragma warning disable 0649
public bool b1;
public bool b2;
#pragma warning restore 0649
}
private unsafe struct BoolFixedStruct
{
#pragma warning disable 0649
public fixed bool Values[16];
#pragma warning restore 0649
}
private struct InterleavedBoolStruct
{
#pragma warning disable 0649
public bool b1;
public int i2;
public bool b3;
public bool b4;
public long i5;
public MyEnum e6;
#pragma warning restore 0649
}
public enum MyEnum
{
Value1 = 1,
Value15 = 15,
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitLayoutStruct
{
[FieldOffset(0)]
public int FieldA;
[FieldOffset(0)]
public int FieldB;
}
[StructLayout(LayoutKind.Sequential, Size = 1024)]
private struct StructWithSize
{
public int FieldA;
public int FieldB;
}
private struct EmptyStruct
{
}
public enum MyEnumByte : byte
{
Tada1 = 1,
Tada2 = 2
}
private static ValueTuple<int> ReturnValueTuple1() => ValueTuple.Create(42);
[TestCompiler]
public static long TestValueTuple1Return()
{
var tuple = ReturnValueTuple1();
return tuple.Item1;
}
private static (int, uint) ReturnValueTuple2() => (42, 13);
[TestCompiler]
public static long TestValueTuple2Return()
{
var tuple = ReturnValueTuple2();
return tuple.Item1 + tuple.Item2;
}
private static (int, uint, byte) ReturnValueTuple3() => (42, 13, 13);
[TestCompiler]
public static long TestValueTuple3Return()
{
var tuple = ReturnValueTuple3();
return tuple.Item1 + tuple.Item2 + tuple.Item3;
}
private static (int, uint, byte, sbyte) ReturnValueTuple4() => (42, 13, 13, -13);
[TestCompiler]
public static long TestValueTuple4Return()
{
var tuple = ReturnValueTuple4();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4;
}
private static (int, uint, byte, sbyte, long) ReturnValueTuple5() => (42, 13, 13, -13, 53);
[TestCompiler]
public static long TestValueTuple5Return()
{
var tuple = ReturnValueTuple5();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5;
}
private struct SomeStruct
{
public int X;
}
private static (int, uint, byte, sbyte, long, SomeStruct) ReturnValueTuple6() => (42, 13, 13, -13, 535353, new SomeStruct { X = 42 } );
[TestCompiler]
public static long TestValueTuple6Return()
{
var tuple = ReturnValueTuple6();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X;
}
private static (int, uint, byte, sbyte, long, SomeStruct, short) ReturnValueTuple7() => (42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400);
[TestCompiler]
public static long TestValueTuple7Return()
{
var tuple = ReturnValueTuple7();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7;
}
private static (int, uint, byte, sbyte, long, SomeStruct, short, int) ReturnValueTuple8() => (42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400, -400);
[TestCompiler]
public static long TestValueTuple8Return()
{
var tuple = ReturnValueTuple8();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7 + tuple.Item8;
}
private static (int, uint, byte, sbyte, long, SomeStruct, short, int, long) ReturnValueTuple9() => (42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400, -400, 48);
[TestCompiler]
public static long TestValueTuple9Return()
{
var tuple = ReturnValueTuple9();
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7 + tuple.Item8 + tuple.Item9;
}
private static long ValueTuple1Arg(ValueTuple<int> tuple)
{
return tuple.Item1;
}
[TestCompiler]
public static long TestValueTuple1Arg()
{
return ValueTuple1Arg(ValueTuple.Create(42));
}
private static long ValueTuple2Arg((int, uint) tuple)
{
return tuple.Item1 + tuple.Item2;
}
[TestCompiler]
public static long TestValueTuple2Arg()
{
return ValueTuple2Arg((42, 13));
}
private static long ValueTuple3Arg((int, uint, byte) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3;
}
[TestCompiler]
public static long TestValueTuple3Arg()
{
return ValueTuple3Arg((42, 13, 13));
}
private static long ValueTuple4Arg((int, uint, byte, sbyte) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4;
}
[TestCompiler]
public static long TestValueTuple4Arg()
{
return ValueTuple4Arg((42, 13, 13, -13));
}
private static long ValueTuple5Arg((int, uint, byte, sbyte, long) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5;
}
[TestCompiler]
public static long TestValueTuple5Arg()
{
return ValueTuple5Arg((42, 13, 13, -13, 535353));
}
private static long ValueTuple6Arg((int, uint, byte, sbyte, long, SomeStruct) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X;
}
[TestCompiler]
public static long TestValueTuple6Arg()
{
return ValueTuple6Arg((42, 13, 13, -13, 535353, new SomeStruct { X = 42 }));
}
private static long ValueTuple7Arg((int, uint, byte, sbyte, long, SomeStruct, short) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7;
}
[TestCompiler]
public static long TestValueTuple7Arg()
{
return ValueTuple7Arg((42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400));
}
private static long ValueTuple8Arg((int, uint, byte, sbyte, long, SomeStruct, short, int) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7 + tuple.Item8;
}
[TestCompiler]
public static long TestValueTuple8Arg()
{
return ValueTuple8Arg((42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400, -400));
}
private static long ValueTuple9Arg((int, uint, byte, sbyte, long, SomeStruct, short, int, long) tuple)
{
return tuple.Item1 + tuple.Item2 + tuple.Item3 + tuple.Item4 + tuple.Item5 + tuple.Item6.X + tuple.Item7 + tuple.Item8 + tuple.Item9;
}
[TestCompiler]
public static long TestValueTuple9Arg()
{
return ValueTuple9Arg((42, 13, 13, -13, 535353, new SomeStruct { X = 42 }, 400, -400, 48));
}
// This needs to be here because the static delegate registry refers to it.
public struct SomeStructWithValueTuple
{
public ValueTuple<int, float> X;
public struct Provider : IArgumentProvider
{
public object Value => new SomeStructWithValueTuple { X = (42, 42.0f) };
}
}
#if UNITY_2022_1_OR_NEWER
public readonly struct InitOnly
{
public readonly float Value { get; init; }
}
[TestCompiler]
public static float TestInitOnly() => new InitOnly { Value = default }.Value;
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0499a50a9b63d9a90fc428e1f5c89e9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 953d1f4d7d69334fbc9657d7d82a855b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,928 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
internal class Pointers
{
[TestCompiler(1)]
[TestCompiler(4)]
[TestCompiler(5)]
public static int CheckAddressOf(int a)
{
var value = new MyIntValue(a);
ref int intValue = ref value.GetValuePtr();
return intValue * 10 + 1;
}
public struct MyIntValue
{
public MyIntValue(int value)
{
Value = value;
}
public int Value;
public unsafe ref int GetValuePtr()
{
fixed (void* ptr = &this)
{
return ref *(int*) ptr;
}
}
}
[TestCompiler(0, MyCastEnum.Value2)]
[TestCompiler(1, MyCastEnum.Value0)]
[TestCompiler(2, MyCastEnum.Value3)]
public static unsafe MyCastEnum PointerCastEnum(int value, MyCastEnum newValue)
{
var ptvalue = new IntPtr(&value);
var pEnum = (MyCastEnum*) ptvalue;
*pEnum = newValue;
return *pEnum;
}
[TestCompiler(0, 0)]
[TestCompiler(0, 1)]
[TestCompiler(1, 0)]
public static unsafe bool PointerCompare(IntPtr a, IntPtr b)
{
return a == b;
}
[TestCompiler(0)]
[TestCompiler(1)]
[TestCompiler(2)]
public static unsafe bool RawPointerCompare(IntPtr value)
{
return (void*)value == (void*)1;
}
[TestCompiler(0)]
[TestCompiler(1)]
[TestCompiler(42424242)]
public static unsafe int PointerHash(IntPtr value)
{
return value.GetHashCode();
}
[TestCompiler(0)]
[TestCompiler(1)]
[TestCompiler(42424242)]
public static unsafe IntPtr PointerToPointer(IntPtr value)
{
return new IntPtr(value.ToPointer());
}
[TestCompiler(0, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_MethodNotSupported)]
public static unsafe int PointerToString(IntPtr value)
{
return value.ToString().Length;
}
[TestCompiler(1)]
[TestCompiler(255)]
[TestCompiler(12351235)]
public static unsafe int PointerAdd(int a)
{
var pA = (byte*)&a;
var pDest = pA + 3;
*pDest = (byte)a;
return a;
}
[TestCompiler(1)]
[TestCompiler(255)]
[TestCompiler(12351235)]
public static unsafe int PointerSub(int a)
{
var pA = (byte*)&a;
var pDest = pA + 3;
*(pDest - 1) = (byte)a;
return a;
}
[TestCompiler]
public static unsafe int PointerPointerSub()
{
var value = new StructForPointerPointerSub();
int* pa = &value.A;
int* pb = &value.B;
var auto = (pb - pa);
return (int)auto;
}
[TestCompiler]
public static unsafe int WhileWithPointer()
{
var check = new CheckPointers { X = 1, Y = 2, Z = 3, W = 4 };
int* pstart = &check.X;
int* pend = &check.W;
int result = 0;
while (pstart <= pend)
{
result += *pstart;
pstart++;
}
return result;
}
struct StructForPointerPointerSub
{
public int A;
public int B;
}
[TestCompiler(1)]
[TestCompiler(255)]
[TestCompiler(12351235)]
public static IntPtr IntPtrConstructor(int a)
{
return new IntPtr(a);
}
[TestCompiler(1U)]
[TestCompiler(255U)]
[TestCompiler(12351235U)]
public static UIntPtr UIntPtrConstructor(uint a)
{
return new UIntPtr(a);
}
[TestCompiler(1)]
[TestCompiler(255)]
[TestCompiler(12351235)]
public static int IntPtrToInt32(int a)
{
return new IntPtr(a).ToInt32();
}
[TestCompiler(1)]
[TestCompiler(255)]
[TestCompiler(12351235)]
public static long IntPtrToInt64(int a)
{
return new IntPtr(a).ToInt64();
}
[TestCompiler(OverrideOn32BitNative = 4)]
public static int IntPtrSize()
{
return IntPtr.Size;
}
// asserted in IntPtrProcessor
[TestCompiler(OverrideOn32BitNative = true)]
public static bool IntPtrSizeCompared()
{
return IntPtr.Size == 4;
}
[TestCompiler]
public static IntPtr IntPtrZero()
{
return IntPtr.Zero;
}
[TestCompiler(1)]
[TestCompiler(5)]
public static IntPtr IntPtrAdd(IntPtr a)
{
return IntPtr.Add(a, 1);
}
[TestCompiler(1)]
[TestCompiler(5)]
public static IntPtr IntPtrAdd2(IntPtr a)
{
return a + 1;
}
[TestCompiler(1)]
[TestCompiler(5)]
public static IntPtr IntPtrSub(IntPtr a)
{
return IntPtr.Subtract(a, 1);
}
[TestCompiler(1)]
[TestCompiler(5)]
public static IntPtr IntPtrSub2(IntPtr a)
{
return a - 1;
}
[TestCompiler]
public static UIntPtr UIntPtrZero()
{
return UIntPtr.Zero;
}
[TestCompiler(1U)]
[TestCompiler(5U)]
public static UIntPtr UIntPtrAdd(UIntPtr a)
{
return UIntPtr.Add(a, 1);
}
[TestCompiler(1U)]
[TestCompiler(5U)]
public static UIntPtr UIntPtrSubstract(UIntPtr a)
{
return UIntPtr.Subtract(a, 1);
}
[TestCompiler(1)]
public static unsafe int PointerAccess(int a)
{
var value = a;
var pValue = &value;
pValue[0] = a + 5;
return value;
}
[TestCompiler(0)] // Keep it at 0 only!
public static unsafe int PointerAccess2(int a)
{
int value = 15;
var pValue = &value;
pValue[a] = value + 5;
return value;
}
[TestCompiler(0)] // Keep it at 0 only!
public static unsafe float PointerAccess3(int a)
{
float value = 15.0f;
var pValue = &value;
pValue[a] = value + 5.0f;
return value;
}
[TestCompiler(0)]
public static unsafe int PointerCompareViaInt(int a)
{
int b;
if (&a == &b)
return 1;
else
return 0;
}
[TestCompiler(0)]
public static unsafe int IntPtrCompare(int a)
{
int b;
IntPtr aPtr = (IntPtr)(&a);
IntPtr bPtr = (IntPtr)(&b);
if (aPtr == bPtr)
return 1;
else
return 0;
}
[TestCompiler(typeof(IntPtrZeroProvider), 1)]
[TestCompiler(typeof(IntPtrOneProvider), 2)]
public static unsafe int UnsafeCompare(int* a, int b)
{
if (a == null)
{
return 1 + b;
}
return 2 + b;
}
unsafe struct NativeQueueBlockHeader
{
#pragma warning disable 0649
public byte* nextBlock;
public int itemsInBlock;
#pragma warning restore 0649
}
[TestCompiler]
public static unsafe void PointerCastWithStruct()
{
byte* currentWriteBlock = null;
if (currentWriteBlock != null && ((NativeQueueBlockHeader*) currentWriteBlock)->itemsInBlock == 100)
{
((NativeQueueBlockHeader*) currentWriteBlock)->itemsInBlock = 5;
}
}
private class IntPtrZeroProvider : IArgumentProvider
{
public object Value => IntPtr.Zero;
}
private class IntPtrOneProvider : IArgumentProvider
{
public object Value => new IntPtr(1);
}
[TestCompiler]
public static unsafe int FixedField()
{
var fixedStruct = new MyStructWithFixed();
fixedStruct.Values[0] = 1;
fixedStruct.Values[1] = 2;
fixedStruct.Values[2] = 3;
fixedStruct.Values[9] = 9;
int result = 0;
for (int i = 0; i < 10; i++)
{
result += fixedStruct.Values[i];
}
return result;
}
[TestCompiler(typeof(MyStructWithFixedProvider), 1)]
//[TestCompiler(typeof(MyStructWithFixedProvider), 2)]
public static unsafe int FixedFieldViaPointer(ref MyStructWithFixed fixedStruct, int i)
{
fixed (MyStructWithFixed* check = &fixedStruct)
{
int* data = check->Values;
return data[i];
}
}
[TestCompiler(typeof(MyStructWithFixedProvider))]
public static unsafe int FixedInt32AndRefInt32(ref MyStructWithFixed fixedStruct)
{
fixed (int* data = &fixedStruct.Value)
{
// We do a call to ProcessInt after with a ref int
// to check that we don't collide with the PinnedType introduced by the previous
// fixed statement
ProcessInt(ref *data);
}
return fixedStruct.Value;
}
private static void ProcessInt(ref int value)
{
value += 5;
}
public unsafe struct ConditionalTestStruct
{
public void* a;
public void* b;
}
public unsafe struct PointerConditional : IJob, IDisposable
{
public ConditionalTestStruct* t;
public void Execute()
{
t->b = t->a != null ? t->a : null;
}
public struct Provider : IArgumentProvider
{
public object Value
{
get
{
var value = new PointerConditional();
value.t = (ConditionalTestStruct*)UnsafeUtility.Malloc(UnsafeUtility.SizeOf<ConditionalTestStruct>(), 4, Allocator.Persistent);
value.t->a = (void*)0x12345678;
value.t->b = null;
return value;
}
}
}
public void Dispose()
{
UnsafeUtility.Free(t, Allocator.Persistent);
}
}
[TestCompiler(typeof(PointerConditional.Provider))]
public static unsafe bool TestConditionalPointer([NoAlias] ref PointerConditional job)
{
job.Execute();
return job.t->a == job.t->b;
}
#if BURST_TESTS_ONLY
[TestCompiler]
public static int TestFieldOffset()
{
var t = default(StructWithFields);
return (int)Unsafe.ByteOffset(ref Unsafe.As<int, bool>(ref t.a), ref t.d);
}
#endif
public struct StructWithFields
{
public int a;
public int b;
public bool c;
public bool d;
public bool e;
public bool f;
}
public unsafe struct MyStructWithFixed
{
public fixed int Values[10];
public int Value;
}
private struct MyStructWithFixedProvider : IArgumentProvider
{
public unsafe object Value
{
get
{
var field = new MyStructWithFixed();
for (int i = 0; i < 10; i++)
{
field.Values[i] = (i + 1) * 5;
}
field.Value = 1235;
return field;
}
}
}
[TestCompiler(0)]
public static unsafe void TestCellVisibleInternal(int length)
{
int3* cellVisibleRequest = (int3*)0;
bool*cellVisibleResult = (bool*)0;
int3* visibleCells = (int3*)0;
IsCellVisibleInternal(cellVisibleRequest, cellVisibleResult, visibleCells, length, length);
}
static unsafe void IsCellVisibleInternal(int3* cellVisibleRequest, bool* cellVisibleResult, int3* visibleCells, int requestLength, int visibleCellsLength)
{
for (int r = 0; r < requestLength; r++)
{
cellVisibleResult[r] = false;
for (int i = 0; i < visibleCellsLength; i++)
{
if (visibleCells[i].x == cellVisibleRequest[r].x && visibleCells[i].y == cellVisibleRequest[r].y && visibleCells[i].z == cellVisibleRequest[r].z)
{
cellVisibleResult[r] = true;
break;
}
}
}
}
public enum MyCastEnum
{
Value0 = 0,
Value1 = 1,
Value2 = 2,
Value3 = 3,
}
public struct CheckPointers
{
public int X;
public int Y;
public int Z;
public int W;
}
// From https://github.com/Unity-Technologies/ECSJobDemos/issues/244
[TestCompiler]
public static unsafe int InitialiseViaCastedPointer()
{
int value = 0;
void* ptr = &value;
byte* asBytePtr = (byte*)ptr;
((int*)asBytePtr)[0] = -1;
return value;
}
[TestCompiler(1)]
public static unsafe int PointerWriteArg(int a)
{
return (int)TestPointerAndGeneric<float>((int*) a);
}
private static unsafe int* TestPointerAndGeneric<T>(int* p) where T : struct
{
p = (int*)(IntPtr)26;
return p;
}
[TestCompiler(ExpectedDiagnosticId = DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)]
public static void TestBlobAssetReferenceData()
{
var blob = new BlobAssetReferenceData(IntPtr.Zero);
blob.Validate();
}
[StructLayout(LayoutKind.Explicit, Size = 16)]
internal unsafe struct BlobAssetHeader
{
[FieldOffset(0)] public void* ValidationPtr;
[FieldOffset(8)] public int Length;
[FieldOffset(12)] public Allocator Allocator;
}
internal unsafe struct BlobAssetReferenceData
{
[NativeDisableUnsafePtrRestriction]
public byte* _ptr;
public BlobAssetReferenceData(IntPtr zero)
{
_ptr = (byte*)zero;
}
internal BlobAssetHeader* Header => ((BlobAssetHeader*)_ptr) - 1;
public void Validate()
{
if (_ptr != null)
if (Header->ValidationPtr != _ptr)
throw new InvalidOperationException("The BlobAssetReference is not valid. Likely it has already been unloaded or released");
}
}
internal unsafe struct StackAllocCheck
{
public int* ptr;
[MethodImpl(MethodImplOptions.NoInlining)]
public void AddToPtr(int* otherPtr)
{
*otherPtr = 42;
*ptr += 1;
*ptr += *otherPtr;
}
public class Provider : IArgumentProvider
{
public object Value => new StackAllocCheck();
}
}
[TestCompiler(typeof(StackAllocCheck.Provider))]
public static unsafe bool StackAllocAliasCheck([NoAlias] ref StackAllocCheck stackAllocCheck)
{
int* ptr = stackalloc int[1];
*ptr = 13;
stackAllocCheck.ptr = ptr;
stackAllocCheck.AddToPtr(ptr);
if (*ptr != 86)
{
return false;
}
*stackAllocCheck.ptr = -4;
*ptr += 1;
*ptr += *stackAllocCheck.ptr;
if (*ptr != -6)
{
return false;
}
return true;
}
[TestCompiler(1)]
public static unsafe int NativeIntAddCheck(int a)
{
return (int)(&a + 1) - (int)&a;
}
public unsafe struct PointerArithmetic : IJob, IDisposable
{
[NativeDisableUnsafePtrRestriction] public int** pointers;
public void Execute()
{
pointers[10] = pointers[10] + +1;
pointers[20] = pointers[20] - +1;
pointers[30] = pointers[30] - -1;
pointers[40] = pointers[40] + -1;
}
public struct Provider : IArgumentProvider
{
public object Value
{
get
{
var value = new PointerArithmetic();
value.pointers = (int**)UnsafeUtility.Malloc(1000*sizeof(int*), 8, Allocator.Persistent);
UnsafeUtility.MemClear(value.pointers, 1000 * sizeof(int*));
return value;
}
}
}
public void Dispose()
{
UnsafeUtility.Free(pointers, Allocator.Persistent);
}
}
// The arithmetic test has been split to make it easier to see the mismatched value (rather than true!=false)
// According to : https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/unsafe-code#pointer-types
// Conversion between pointers and integrals is "Implementation Defined".
[TestCompiler(typeof(PointerArithmetic.Provider))]
public static unsafe Int64 TestArithmeticPointerA(ref PointerArithmetic job)
{
job.Execute();
if (sizeof(int*) == 4)
return (Int64)(UInt32)(job.pointers[10]); // Workaround IL2CPP 32bit Bug : https://fogbugz.unity3d.com/f/cases/1254635/
return (Int64)job.pointers[10];
}
[TestCompiler(typeof(PointerArithmetic.Provider))]
public static unsafe Int64 TestArithmeticPointerB(ref PointerArithmetic job)
{
job.Execute();
if (sizeof(int*) == 4)
return (Int64)(UInt32)(job.pointers[20]); // Workaround IL2CPP 32bit Bug : https://fogbugz.unity3d.com/f/cases/1254635/
return (Int64)job.pointers[20];
}
[TestCompiler(typeof(PointerArithmetic.Provider))]
public static unsafe Int64 TestArithmeticPointerC(ref PointerArithmetic job)
{
job.Execute();
if (sizeof(int*) == 4)
return (Int64)(UInt32)(job.pointers[30]); // Workaround IL2CPP 32bit Bug : https://fogbugz.unity3d.com/f/cases/1254635/
return (Int64)job.pointers[30];
}
[TestCompiler(typeof(PointerArithmetic.Provider))]
public static unsafe Int64 TestArithmeticPointerD(ref PointerArithmetic job)
{
job.Execute();
if (sizeof(int*) == 4)
return (Int64)(UInt32)(job.pointers[40]); // Workaround IL2CPP 32bit Bug : https://fogbugz.unity3d.com/f/cases/1254635/
return (Int64)job.pointers[40];
}
private struct TestData
{
public int3 Min;
public int Size;
}
[TestCompiler]
public static unsafe int TestPointerWithIn()
{
var foo = stackalloc TestData[1];
*foo = new TestData { Min = new int3(0, 1, 2), Size = 3 };
return SubFunctionWithInPointer(in foo);
}
private static unsafe int SubFunctionWithInPointer(in TestData* node)
{
int3 data = node->Min;
return node->Size + data.x + data.y + data.z;
}
[TestCompiler]
public static unsafe int TestSystemBufferMemoryCopy()
{
var a = stackalloc int[2];
a[0] = 42;
System.Buffer.MemoryCopy(a + 0, a + 1, UnsafeUtility.SizeOf<int>(), UnsafeUtility.SizeOf<int>());
return a[1];
}
[TestCompiler(0ul, byte.MinValue)]
[TestCompiler(0ul, byte.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesByte(UInt64 p,byte a)
{
var pointer = (byte*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, byte.MinValue)]
[TestCompiler(0ul, byte.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesByte(UInt64 p,byte a)
{
var pointer = (byte*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, byte.MinValue)]
[TestCompiler(0ul, byte.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesByte(UInt64 p,byte a)
{
var pointer = (byte*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, sbyte.MinValue)]
[TestCompiler(0ul, sbyte.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesSByte(UInt64 p,sbyte a)
{
var pointer = (sbyte*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, sbyte.MinValue)]
[TestCompiler(0ul, sbyte.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesSByte(UInt64 p,sbyte a)
{
var pointer = (sbyte*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, sbyte.MinValue)]
[TestCompiler(0ul, sbyte.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesSByte(UInt64 p,sbyte a)
{
var pointer = (sbyte*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, short.MinValue)]
[TestCompiler(0ul, short.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesShort(UInt64 p,short a)
{
var pointer = (short*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, short.MinValue)]
[TestCompiler(0ul, short.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesShort(UInt64 p,short a)
{
var pointer = (short*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, short.MinValue)]
[TestCompiler(0ul, short.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesShort(UInt64 p,short a)
{
var pointer = (short*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, ushort.MinValue)]
[TestCompiler(0ul, ushort.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesUShort(UInt64 p,ushort a)
{
var pointer = (ushort*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, ushort.MinValue)]
[TestCompiler(0ul, ushort.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesUShort(UInt64 p,ushort a)
{
var pointer = (ushort*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, ushort.MinValue)]
[TestCompiler(0ul, ushort.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesUShort(UInt64 p,ushort a)
{
var pointer = (ushort*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, int.MinValue)]
[TestCompiler(0ul, int.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesInt(UInt64 p,int a)
{
var pointer = (int*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, int.MinValue)]
[TestCompiler(0ul, int.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesInt(UInt64 p,int a)
{
var pointer = (int*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, int.MinValue)]
[TestCompiler(0ul, int.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesInt(UInt64 p,int a)
{
var pointer = (int*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, uint.MinValue)]
[TestCompiler(0ul, uint.MaxValue)]
public static unsafe IntPtr PointerMathAddPNTypesUInt(UInt64 p,uint a)
{
var pointer = (uint*)p;
return new IntPtr(pointer + a); // Pointer LHS
}
[TestCompiler(0ul, uint.MinValue)]
[TestCompiler(0ul, uint.MaxValue)]
public static unsafe IntPtr PointerMathAddNPTypesUInt(UInt64 p,uint a)
{
var pointer = (uint*)p;
return new IntPtr(a + pointer); // Pointer RHS
}
[TestCompiler(0ul, uint.MinValue)]
[TestCompiler(0ul, uint.MaxValue)]
public static unsafe IntPtr PointerMathSubPNTypesUInt(UInt64 p,uint a)
{
var pointer = (uint*)p;
return new IntPtr(pointer - a); // Pointer LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, long.MinValue)]
[TestCompiler(0ul, long.MaxValue)]
public static unsafe IntPtr PolongerMathAddPNTypesLong(UInt64 p,long a)
{
var polonger = (long*)p;
return new IntPtr(polonger + a); // Polonger LHS
}
[TestCompiler(0ul, long.MinValue)]
[TestCompiler(0ul, long.MaxValue)]
public static unsafe IntPtr PolongerMathAddNPTypesLong(UInt64 p,long a)
{
var polonger = (long*)p;
return new IntPtr(a + polonger); // Polonger RHS
}
[TestCompiler(0ul, long.MinValue)]
[TestCompiler(0ul, long.MaxValue)]
public static unsafe IntPtr PolongerMathSubPNTypesLong(UInt64 p,long a)
{
var polonger = (long*)p;
return new IntPtr(polonger - a); // Polonger LHS (no RHS since not legal in C#)
}
[TestCompiler(0ul, ulong.MinValue)]
[TestCompiler(0ul, ulong.MaxValue)]
public static unsafe IntPtr PolongerMathAddPNTypesULong(UInt64 p,ulong a)
{
var polonger = (ulong*)p;
return new IntPtr(polonger + a); // Polonger LHS
}
[TestCompiler(0ul, ulong.MinValue)]
[TestCompiler(0ul, ulong.MaxValue)]
public static unsafe IntPtr PolongerMathAddNPTypesULong(UInt64 p,ulong a)
{
var polonger = (ulong*)p;
return new IntPtr(a + polonger); // Polonger RHS
}
[TestCompiler(0ul, ulong.MinValue)]
[TestCompiler(0ul, ulong.MaxValue)]
public static unsafe IntPtr PolongerMathSubPNTypesULong(UInt64 p,ulong a)
{
var polonger = (ulong*)p;
return new IntPtr(polonger - a); // Polonger LHS (no RHS since not legal in C#)
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d218d6a948d3d8c98c40cca55189572
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,66 @@
namespace Burst.Compiler.IL.Tests.Shared
{
public class Patterns
{
[TestCompiler(2)]
[TestCompiler(1)]
[TestCompiler(0)]
public static int PropertyPattern(int x)
{
var point = new Point { X = x, Y = 5 };
return point switch
{
{ X: 2 } => 10,
{ X: 1 } => 5,
_ => 0
};
}
private struct Point
{
public int X;
public int Y;
}
[TestCompiler(1, 2)]
[TestCompiler(2, 4)]
[TestCompiler(0, 0)]
public static int TuplePattern(int x, int y)
{
return (x, y) switch
{
(1, 2) => 10,
(2, 4) => 5,
_ => 0
};
}
private struct DeconstructablePoint
{
public int X;
public int Y;
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
[TestCompiler(1, -1)]
[TestCompiler(-1, 1)]
[TestCompiler(1, 1)]
[TestCompiler(-1, -1)]
public static int PositionalPattern(int pointX, int pointY)
{
var point = new DeconstructablePoint { X = pointX, Y = pointY };
return point switch
{
(0, 0) => 0,
var (x, y) when x > 0 && y > 0 => 1,
var (x, y) when x < 0 && y > 0 => 2,
var (x, y) when x < 0 && y < 0 => 3,
var (x, y) when x > 0 && y < 0 => 4,
var (_, _) => 5
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50c3d62ace6136cb9c85f8e2a44b7aec
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,144 @@
using System.Runtime.CompilerServices;
using Unity.Burst;
using Unity.Burst.CompilerServices;
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
internal class Functions
{
[TestCompiler]
public static int CheckFunctionCall()
{
return AnotherFunction();
}
private static int AnotherFunction()
{
return 150;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_UnableToAccessManagedMethod)]
public static void Boxing()
{
var a = new CustomStruct();
// This will box CustomStruct, so this method should fail when compiling
a.GetType();
}
private struct CustomStruct
{
}
public static int NotDiscardable()
{
return 3;
}
[BurstDiscard]
public static void Discardable()
{
}
[TestCompiler]
public static int TestCallsOfDiscardedMethodRegression()
{
// The regression was that we would queue all calls of a method, but if we encountered a discardable one
// We would stop visiting pending methods. This resulting in method bodies not being visited.
Discardable();
return NotDiscardable();
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int NoInline(int x)
{
return x;
}
[TestCompiler(42)]
public static int TestNoInline(int x)
{
return NoInline(x);
}
[MethodImpl(MethodImplOptions.NoOptimization)]
public static int NoOptimization(int x)
{
return x;
}
[TestCompiler(42)]
public static int TestNoOptimization(int x)
{
return NoOptimization(x);
}
[TestCompiler(42)]
public static int TestImplicitCapture(int x)
{
return SomeFunction();
int SomeFunction()
{
return x;
}
}
public struct Pair
{
public int X;
public int Y;
public struct Provider : IArgumentProvider
{
public object Value => new Pair { X = 13, Y = 42 };
}
}
[TestCompiler(42, typeof(Pair.Provider))]
public static int TestImplicitCaptureInLoop(int x, ref Pair rp)
{
int total = 0;
Pair p = rp;
for (int i = 0; i < x; i++)
{
total += SomeFunction(42, 42, 42, 42, 42, i);
int SomeFunction(int a, int b, int c, int d, int e, int otherI)
{
if (p.Y != 0)
{
return (otherI == i) ? 56 : -13;
}
return 0;
}
}
return total;
}
[TestCompiler(42)]
[IgnoreWarning((int)DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)]
public static void NoWarningsWithSingle(int i)
{
if ((6 * 8) == i)
{
throw new System.Exception("Not the meaning of life!");
}
}
[TestCompiler(42)]
[IgnoreWarning((int)DiagnosticId.WRN_LoopIntrinsicCalledButLoopOptimizedAway)]
[IgnoreWarning((int)DiagnosticId.WRN_ExceptionThrownInNonSafetyCheckGuardedFunction)]
public static void NoWarningsWithMultiple(int i)
{
if ((6 * 8) == i)
{
throw new System.Exception("Not the meaning of life!");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6f9aa18fd4b3d1d9ef249e3c61af783
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,260 @@
// Doesn't work with IL2CPP yet - waiting for Unity fix to land.
#if BURST_INTERNAL //|| UNITY_2021_2_OR_NEWER
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using NUnit.Framework;
using Unity.Burst;
using UnityBenchShared;
#if BURST_INTERNAL
using System.IO;
using System.Reflection;
using Burst.Compiler.IL.Aot;
#endif
namespace Burst.Compiler.IL.Tests
{
[RestrictPlatform("Mono on linux crashes to what appears to be a mono bug", Platform.Linux, exclude: true)]
internal class TestCSharpFunctionPointers
{
[TestCompiler]
public static unsafe int TestCSharpFunctionPointer()
{
delegate* unmanaged[Cdecl]<int, int> callback = &TestCSharpFunctionPointerCallback;
return TestCSharpFunctionPointerHelper(callback);
}
private static unsafe int TestCSharpFunctionPointerHelper(delegate* unmanaged[Cdecl]<int, int> callback)
{
return callback(5);
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static int TestCSharpFunctionPointerCallback(int value) => value + 1;
[TestCompiler]
public static unsafe int TestCSharpFunctionPointerCastingParameterPtrFromVoid()
{
delegate* unmanaged[Cdecl]<void*, int> callback = &TestCSharpFunctionPointerCallbackVoidPtr;
delegate* unmanaged[Cdecl]<int*, int> callbackCasted = callback;
int i = 5;
return callbackCasted(&i);
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe int TestCSharpFunctionPointerCallbackVoidPtr(void* value) => *((int*)value) + 1;
[TestCompiler]
public static unsafe int TestCSharpFunctionPointerCastingParameterPtrToVoid()
{
delegate* unmanaged[Cdecl]<int*, int> callback = &TestCSharpFunctionPointerCallbackIntPtr;
delegate* unmanaged[Cdecl]<void*, int> callbackCasted = (delegate* unmanaged[Cdecl]<void*, int>)callback;
int i = 5;
return callbackCasted(&i);
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe int TestCSharpFunctionPointerCallbackIntPtr(int* value) => *value + 1;
[TestCompiler]
public static unsafe int TestCSharpFunctionPointerCastingToAndFromVoidPtr()
{
delegate* unmanaged[Cdecl]<int*, int> callback = &TestCSharpFunctionPointerCallbackIntPtr;
void* callbackAsVoidPtr = callback;
delegate* unmanaged[Cdecl]<int*, int> callbackCasted = (delegate* unmanaged[Cdecl]<int*, int>)callbackAsVoidPtr;
int i = 5;
return callbackCasted(&i);
}
public struct CSharpFunctionPointerProvider : IArgumentProvider
{
public unsafe object Value
{
get
{
delegate* unmanaged[Cdecl]<int, int> callback = &TestCSharpFunctionPointerCallback;
return (IntPtr)callback;
}
}
}
[TestCompiler(typeof(CSharpFunctionPointerProvider))]
public static unsafe int TestCSharpFunctionPointerPassedInFromOutside(IntPtr callbackAsIntPtr)
{
delegate* unmanaged[Cdecl]<int, int> callback = (delegate* unmanaged[Cdecl]<int, int>)callbackAsIntPtr;
return TestCSharpFunctionPointerHelper(callback);
}
private struct TestCSharpFunctionPointerWithStructParameterStruct
{
public int X;
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static int TestCSharpFunctionPointerWithStructParameterCallback(TestCSharpFunctionPointerWithStructParameterStruct value) => value.X + 1;
public struct CSharpFunctionPointerWithStructParameterProvider : IArgumentProvider
{
public unsafe object Value
{
get
{
delegate* unmanaged[Cdecl]<TestCSharpFunctionPointerWithStructParameterStruct, int> callback = &TestCSharpFunctionPointerWithStructParameterCallback;
return (IntPtr)callback;
}
}
}
[TestCompiler(typeof(CSharpFunctionPointerWithStructParameterProvider))]
public static unsafe int TestCSharpFunctionPointerPassedInFromOutsideWithStructParameter(IntPtr untypedFp)
{
return TestHashingFunctionPointerTypeHelper((delegate* unmanaged[Cdecl]<TestCSharpFunctionPointerWithStructParameterStruct, int>)untypedFp);
}
private static unsafe int TestHashingFunctionPointerTypeHelper(delegate* unmanaged[Cdecl]<TestCSharpFunctionPointerWithStructParameterStruct, int> fp)
{
return fp(new TestCSharpFunctionPointerWithStructParameterStruct { X = 42 });
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_CalliNonCCallingConventionNotSupported)]
public static unsafe int TestCSharpFunctionPointerInvalidCallingConvention()
{
delegate*<int, int> callback = &TestCSharpFunctionPointerInvalidCallingConventionCallback;
return callback(5);
}
private static int TestCSharpFunctionPointerInvalidCallingConventionCallback(int value) => value + 1;
[TestCompiler]
public static unsafe int TestCSharpFunctionPointerMissingBurstCompileAttribute()
{
delegate* unmanaged[Cdecl]<int, int> callback = &TestCSharpFunctionPointerCallbackMissingBurstCompileAttribute;
return callback(5);
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static int TestCSharpFunctionPointerCallbackMissingBurstCompileAttribute(int value) => value + 1;
[Test]
public unsafe void TestFunctionPointerReturnedFromBurstFunction()
{
#if BURST_INTERNAL
var libraryCacheFolderName = Path.Combine(
Path.GetDirectoryName(GetType().Assembly.Location),
nameof(TestCSharpFunctionPointers),
nameof(TestFunctionPointerReturnedFromBurstFunction));
if (Directory.Exists(libraryCacheFolderName))
{
Directory.Delete(libraryCacheFolderName, true);
}
using var globalContext = new Server.GlobalContext(libraryCacheFolderName);
var jitOptions = new AotCompilerOptions();
using var methodCompiler = new Helpers.MethodCompiler(globalContext, jitOptions.BackendName, name => IntPtr.Zero);
BurstCompiler.InternalCompiler = del =>
{
var getMethod = del.GetType().GetMethod("get_Method", BindingFlags.Public | BindingFlags.Instance);
var methodInfo = (MethodInfo)getMethod.Invoke(del, new object[0]);
var compiledResult = methodCompiler.CompileMethod(methodInfo, jitOptions);
return compiledResult.FunctionPointer;
};
#endif
var fp = BurstCompiler.CompileFunctionPointer<DelegateWithCSharpFunctionPointerReturn>(EntryPointWithCSharpFunctionPointerReturn);
var fpInner = fp.Invoke();
delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> callback = (delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float>)fpInner;
var result = callback(1, 2, 4, 8, 16, 32);
Assert.AreEqual((float)(1 + 2 + 4 + 8 + 16 + 32), result);
}
[BurstCompile(CompileSynchronously = true)]
private static unsafe IntPtr EntryPointWithCSharpFunctionPointerReturn()
{
delegate* unmanaged[Cdecl]<float, float, float, float, float, float, float> fp = &EntryPointWithCSharpFunctionPointerReturnHelper;
return (IntPtr)fp;
}
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe float EntryPointWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6)
{
return p1 + p2 + p3 + p4 + p5 + p6;
}
private unsafe delegate IntPtr DelegateWithCSharpFunctionPointerReturn();
// Note that there are 6 float parameters to try to catch any issues with calling conventions.
private unsafe delegate float DelegateWithCSharpFunctionPointerReturnHelper(float p1, float p2, float p3, float p4, float p5, float p6);
// Note that this test previously had a `out int i` parameter, but a bugfix in Roslyn
// means that ref parameters in UnmanagedCallersOnly methods now result in a compilation error:
// https://github.com/dotnet/roslyn/issues/57025
// So we've updated this test to use a pointer.
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe void TestCSharpFunctionPointerCallbackWithOut(int* i)
{
TestCSharpFunctionPointerCallbackWithOut(out *i);
}
private static void TestCSharpFunctionPointerCallbackWithOut(out int i)
{
i = 42;
}
[TestCompiler]
public static unsafe int TestCSharpFunctionPointerWithOut()
{
delegate* unmanaged[Cdecl]<int*, void> callback = &TestCSharpFunctionPointerCallbackWithOut;
int i;
callback(&i);
return i;
}
#if BURST_TESTS_ONLY
[DllImport("burst-dllimport-native")]
private static extern unsafe int callFunctionPointer(delegate* unmanaged[Cdecl]<int, int> f);
// Ignored on wasm since dynamic linking is not supported at present.
// Override result on Mono because it throws a StackOverflowException for some reason related to the function pointer.
// We should use OverrideResultOnMono, but OverrideResultOnMono still runs the managed version, which causes a crash,
// so we use OverrideManagedResult.
[TestCompiler(IgnoreOnPlatform = Backend.TargetPlatform.Wasm, OverrideManagedResult = 43)]
public static unsafe int TestPassingFunctionPointerToNativeCode()
{
return callFunctionPointer(&TestCSharpFunctionPointerCallback);
}
#endif
}
}
// This attribute is also included in com.unity.burst/Tests/Runtime/FunctionPointerTests.cs,
// so we want to exclude it here when we're running inside Unity otherwise we'll get a
// duplicate definition error.
#if BURST_TESTS_ONLY
// UnmanagedCallersOnlyAttribute is new in .NET 5.0. This attribute is required
// when you declare an unmanaged function pointer with an explicit calling convention.
// Fortunately, Roslyn lets us declare the attribute class ourselves, and it will be used.
// Users will need this same declaration in their own projects, in order to use
// C# 9.0 function pointers.
namespace System.Runtime.InteropServices
{
[AttributeUsage(System.AttributeTargets.Method, Inherited = false)]
public sealed class UnmanagedCallersOnlyAttribute : Attribute
{
public Type[] CallConvs;
}
}
#endif
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a3d2b4e287138d6b5a68635fcda35ae
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,112 @@
#if UNITY_ANDROID && !UNITY_EDITOR
using System.Runtime.InteropServices;
namespace Burst.Compiler.IL.Tests
{
public class DllImportAndroid
{
public unsafe struct HandleStruct
{
public void* Handle;
}
public struct NestedHandleStruct
{
public HandleStruct Handle;
}
public unsafe struct TypedHandleStruct
{
public byte* Handle;
}
public struct IntInStruct
{
public int Handle;
}
public struct LongInStruct
{
public long Handle;
}
[DllImport("burst-dllimport-native")]
public static extern void allVoid();
[TestCompiler]
public static void AllVoid()
{
allVoid();
}
[DllImport("burst-dllimport-native")]
public static extern int incrementByOne(int x);
[TestCompiler]
public static int UseDllImportedFunction()
{
return incrementByOne(41);
}
[DllImport("burst-dllimport-native")]
public static extern int readFromPtr(ref int x);
[TestCompiler]
public static int ReadFromPtr()
{
int x = 37;
return readFromPtr(ref x);
}
[DllImport("burst-dllimport-native")]
public static extern HandleStruct handleStruct(HandleStruct handle);
[TestCompiler]
public unsafe static long HandleStructByVal()
{
var handle = new HandleStruct { Handle = (void*)0x42 };
return (long)handleStruct(handle).Handle;
}
[DllImport("burst-dllimport-native")]
public static extern NestedHandleStruct nestedHandleStruct(NestedHandleStruct handle);
[TestCompiler]
public unsafe static long NestedHandleStructByVal()
{
var handle = new NestedHandleStruct { Handle = new HandleStruct { Handle = (void*)0x42 } };
return (long)nestedHandleStruct(handle).Handle.Handle;
}
[DllImport("burst-dllimport-native")]
public static extern TypedHandleStruct typedHandleStruct(TypedHandleStruct handle);
[TestCompiler]
public unsafe static long TypedHandleStructByVal()
{
var handle = new TypedHandleStruct { Handle = (byte*)0x42 };
return (long)typedHandleStruct(handle).Handle;
}
[DllImport("burst-dllimport-native")]
public static extern IntInStruct intInStruct(IntInStruct handle);
[TestCompiler]
public unsafe static long IntInStructByVal()
{
var handle = new IntInStruct { Handle = 0x42424242 };
return (long)intInStruct(handle).Handle;
}
[DllImport("burst-dllimport-native")]
public static extern LongInStruct longInStruct(LongInStruct handle);
[TestCompiler]
public unsafe static long LongInStructByVal()
{
var handle = new LongInStruct { Handle = 0x4242424242424242 };
return (long)longInStruct(handle).Handle;
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb0c92e22ce4395fae3821dcc85a3113
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,852 @@
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,
}
[TestCompiler(long.MaxValue)]
[TestCompiler(long.MinValue)]
[TestCompiler(0)]
public static byte SwitchExpression(long a)
{
return a switch
{
long.MaxValue => 1,
long.MinValue => 2,
_ => 0,
};
}
[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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 945f727e6eb43c81ae49f4e92e3d98c2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,372 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using System.Runtime.CompilerServices;
namespace Burst.Compiler.IL.Tests
{
internal class ControlFlowsTryCatchFinally
{
[TestCompiler(-10)]
[TestCompiler(0)]
[TestCompiler(10)]
public static int TryFinallySimple(int i)
{
try
{
if (i == 0) // case 0
{
return 1;
}
else if (i > 0) // case 10
{
i = i * 2;
}
else
{
i = i * 3; // case -10
}
}
finally
{
i = i + 1;
}
return i; // both case 10 and -10
}
static void Oof()
{
}
[TestCompiler]
public static void TryFinallyFirstBlock()
{
try
{
}
finally
{
Oof();
}
}
static int MagicA(int b, int f, int h, CustomBuffer s)
{
return b+s.Hash()+f-h;
}
static bool MagicB(int c,out int t)
{
t = 0;
if (c>10)
{
t = c;
return true;
}
return false;
}
// This test catches an issue with the de-stackifier. (see ILBuilder.cs:1254 (flushStack))
// Needs to be unoptimised to trigger
[TestCompiler(0)]
[TestCompiler(99)]
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
public static int TryUnbalancedFinally(int i)
{
// this if is required to force the destackifier to process the final block, before processing the block the contains the endfinally
if (i == 99)
{
return default;
}
int resultB = i;
using var buffer = new CustomBuffer(32);
return resultB + MagicA(i,
MagicB(i*2, out var r) ? r : default,
MagicB(i, out var t) ? t : default,
buffer);
}
[TestCompiler(-3)]
[TestCompiler(0)]
[TestCompiler(3)]
public static int TryFinallyComplex1(int i)
{
try
{
try
{
if (i == 0)
{
return i - 1;
}
i += 3;
}
finally
{
if (i == 0) // case i: -3
{
i = 1;
}
else
{
i = i * 10; // case i: 3
}
}
}
finally
{
i = i * 2; // both -3 and 3
}
return i + 1;
}
[TestCompiler(-10)]
[TestCompiler(0)] // case 0
[TestCompiler(10)]
public static int TryFinallyComplex2(int i)
{
// First block of nested try/catch
try
{
try
{
if (i == 0) // case 0
{
return i - 1;
}
i = i * 2;
}
finally
{
i++;
}
}
finally
{
i = i * 3;
}
// Second block of nested try/catch
try
{
i = i - 2;
try
{
if (i < 0) // case -10
{
return i * 5;
}
i += 3; // case 10
}
finally
{
i += 11;
}
}
finally
{
i = i * 3;
}
return i + 1; // case 10
}
[TestCompiler(0)]
[TestCompiler(1)]
[TestCompiler(10)]
[TestCompiler(20)]
public static int TryFinallyComplex3(int x)
{
bool k = true;
int num = 0;
try
{
while (k)
{
if (x < 10)
{
num |= 2;
try
{
if (x == 1) return num;
}
finally
{
k = false;
}
continue;
}
num |= 1;
try
{
if (x == 20) return num;
}
finally
{
k = false;
}
}
}
finally
{
num |= 4;
}
return num;
}
[TestCompiler]
public static int TryUsingDispose()
{
using (var buffer = new CustomBuffer(32))
{
return buffer.Hash();
}
}
[TestCompiler]
public static int ForEachTryFinally()
{
int hashCode = 0;
foreach (var value in new RangeEnumerable(1, 100))
{
hashCode = (hashCode * 397) ^ value;
}
return hashCode;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_CatchConstructionNotSupported)]
public static int TryCatch()
{
try
{
return default(int);
}
catch (InvalidOperationException)
{
return 1;
}
}
private unsafe struct CustomBuffer : IDisposable
{
private readonly int _size;
private byte* _buffer;
public CustomBuffer(int size)
{
_size = size;
_buffer = (byte*)UnsafeUtility.Malloc(size, 4, Allocator.Persistent);
for (int i = 0; i < size; i++)
{
_buffer[i] = (byte)(i + 1);
}
}
public int Hash()
{
int hashCode = _size;
for (int i = 0; i < _size; i++)
{
hashCode = (hashCode * 397) ^ (byte)_buffer[i];
}
return hashCode;
}
public unsafe void Dispose()
{
if (_buffer != null)
{
UnsafeUtility.Free(_buffer, Allocator.Persistent);
_buffer = (byte*) 0;
}
}
}
private struct RangeEnumerable : IEnumerable<int>
{
private readonly int _from;
private readonly int _to;
public RangeEnumerable(int from, int to)
{
_from = @from;
_to = to;
}
public Enumerator GetEnumerator()
{
return new Enumerator();
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public struct Enumerator : IEnumerator<int>
{
private readonly int _from;
private readonly int _to;
public Enumerator(int from, int to)
{
_from = @from;
_to = to;
Current = -1;
}
public void Dispose()
{
// nothing to do
}
public bool MoveNext()
{
if (Current < 0)
{
Current = _from;
return true;
}
int nextIndex = Current + 1;
if (nextIndex >= _from && nextIndex <= _to)
{
Current = nextIndex;
return true;
}
return false;
}
public void Reset()
{
}
public int Current { get; private set; }
object IEnumerator.Current => Current;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 701a30a4d0f73d51a92f908b568cea9d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06cc7660b5c739eca022daad2ed2e0a4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,731 @@
// -----------------------------------------------------------
// This file was generated automatically from 050-TestStructsLayout.cs
// DO NOT EDIT THIS FILE MANUALLY
// -----------------------------------------------------------
using Unity.Collections.LowLevel.Unsafe;
namespace Burst.Compiler.IL.Tests
{
partial class TestStructsLayout
{
[TestCompiler]
public static int Test_CheckHoleInner_Size()
{
return UnsafeUtility.SizeOf<CheckHoleInner>();
}
[TestCompiler]
public static unsafe int Test_CheckHoleInner_FieldOffset_m_Ptr()
{
var value = new CheckHoleInner();
var addressStart = &value;
var addressField = &value.m_Ptr;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler(OverrideOn32BitNative = 20)]
public static int Test_CheckHoleOuter_Size()
{
return UnsafeUtility.SizeOf<CheckHoleOuter>();
}
#endif
[TestCompiler]
public static unsafe int Test_CheckHoleOuter_FieldOffset_a()
{
var value = new CheckHoleOuter();
var addressStart = &value;
var addressField = &value.a;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_CheckHoleOuter_FieldOffset_b()
{
var value = new CheckHoleOuter();
var addressStart = &value;
var addressField = &value.b;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler(OverrideOn32BitNative = 12)]
public static unsafe int Test_CheckHoleOuter_FieldOffset_c()
{
var value = new CheckHoleOuter();
var addressStart = &value;
var addressField = &value.c;
return (int)((byte*)addressField - (byte*)addressStart);
}
#endif
[TestCompiler]
public static int Test_ExplicitStructWithoutSize2_Size()
{
return UnsafeUtility.SizeOf<ExplicitStructWithoutSize2>();
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize2_FieldOffset_a()
{
var value = new ExplicitStructWithoutSize2();
var addressStart = &value;
var addressField = &value.a;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize2_FieldOffset_b()
{
var value = new ExplicitStructWithoutSize2();
var addressStart = &value;
var addressField = &value.b;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize2_FieldOffset_c()
{
var value = new ExplicitStructWithoutSize2();
var addressStart = &value;
var addressField = &value.c;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_ExplicitStructWithoutSize_Size()
{
return UnsafeUtility.SizeOf<ExplicitStructWithoutSize>();
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize_FieldOffset_a()
{
var value = new ExplicitStructWithoutSize();
var addressStart = &value;
var addressField = &value.a;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize_FieldOffset_b()
{
var value = new ExplicitStructWithoutSize();
var addressStart = &value;
var addressField = &value.b;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithoutSize_FieldOffset_c()
{
var value = new ExplicitStructWithoutSize();
var addressStart = &value;
var addressField = &value.c;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_SequentialStructWithSize3_Size()
{
return UnsafeUtility.SizeOf<SequentialStructWithSize3>();
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithSize3_FieldOffset_a()
{
var value = new SequentialStructWithSize3();
var addressStart = &value;
var addressField = &value.a;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithSize3_FieldOffset_b()
{
var value = new SequentialStructWithSize3();
var addressStart = &value;
var addressField = &value.b;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithSize3_FieldOffset_c()
{
var value = new SequentialStructWithSize3();
var addressStart = &value;
var addressField = &value.c;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_SequentialStructWithoutSize_Size()
{
return UnsafeUtility.SizeOf<SequentialStructWithoutSize>();
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithoutSize_FieldOffset_a()
{
var value = new SequentialStructWithoutSize();
var addressStart = &value;
var addressField = &value.a;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithoutSize_FieldOffset_b()
{
var value = new SequentialStructWithoutSize();
var addressStart = &value;
var addressField = &value.b;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_SequentialStructWithoutSize_FieldOffset_c()
{
var value = new SequentialStructWithoutSize();
var addressStart = &value;
var addressField = &value.c;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_SequentialStructEmptyNoAttributes_Size()
{
return UnsafeUtility.SizeOf<SequentialStructEmptyNoAttributes>();
}
[TestCompiler]
public static int Test_ExplicitStructWithEmptySequentialFields_Size()
{
return UnsafeUtility.SizeOf<ExplicitStructWithEmptySequentialFields>();
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithEmptySequentialFields_FieldOffset_FieldA()
{
var value = new ExplicitStructWithEmptySequentialFields();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStructWithEmptySequentialFields_FieldOffset_FieldB()
{
var value = new ExplicitStructWithEmptySequentialFields();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_ExplicitStrictWithEmptyAndNonEmptySequentialFields_Size()
{
return UnsafeUtility.SizeOf<ExplicitStrictWithEmptyAndNonEmptySequentialFields>();
}
[TestCompiler]
public static unsafe int Test_ExplicitStrictWithEmptyAndNonEmptySequentialFields_FieldOffset_FieldA()
{
var value = new ExplicitStrictWithEmptyAndNonEmptySequentialFields();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_ExplicitStrictWithEmptyAndNonEmptySequentialFields_FieldOffset_FieldB()
{
var value = new ExplicitStrictWithEmptyAndNonEmptySequentialFields();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructWithPack8_Size()
{
return UnsafeUtility.SizeOf<StructWithPack8>();
}
[TestCompiler]
public static unsafe int Test_StructWithPack8_FieldOffset_FieldA()
{
var value = new StructWithPack8();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructWithPack8_FieldOffset_FieldB()
{
var value = new StructWithPack8();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack2WithBytesAndInt_Size()
{
return UnsafeUtility.SizeOf<StructPack2WithBytesAndInt>();
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInt_FieldOffset_FieldA()
{
var value = new StructPack2WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInt_FieldOffset_FieldB()
{
var value = new StructPack2WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInt_FieldOffset_FieldC()
{
var value = new StructPack2WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldC;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack2WithBytesAndInts_Size()
{
return UnsafeUtility.SizeOf<StructPack2WithBytesAndInts>();
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInts_FieldOffset_FieldA()
{
var value = new StructPack2WithBytesAndInts();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInts_FieldOffset_FieldB()
{
var value = new StructPack2WithBytesAndInts();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInts_FieldOffset_FieldC()
{
var value = new StructPack2WithBytesAndInts();
var addressStart = &value;
var addressField = &value.FieldC;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack2WithBytesAndInts_FieldOffset_FieldD()
{
var value = new StructPack2WithBytesAndInts();
var addressStart = &value;
var addressField = &value.FieldD;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack1WithBytesAndInt_Size()
{
return UnsafeUtility.SizeOf<StructPack1WithBytesAndInt>();
}
[TestCompiler]
public static unsafe int Test_StructPack1WithBytesAndInt_FieldOffset_FieldA()
{
var value = new StructPack1WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack1WithBytesAndInt_FieldOffset_FieldB()
{
var value = new StructPack1WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack1WithBytesAndInt_FieldOffset_FieldC()
{
var value = new StructPack1WithBytesAndInt();
var addressStart = &value;
var addressField = &value.FieldC;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack1WithByteAndInt_Size()
{
return UnsafeUtility.SizeOf<StructPack1WithByteAndInt>();
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndInt_FieldOffset_FieldA()
{
var value = new StructPack1WithByteAndInt();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndInt_FieldOffset_FieldB()
{
var value = new StructPack1WithByteAndInt();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack1WithByteAndIntWrapper_Size()
{
return UnsafeUtility.SizeOf<StructPack1WithByteAndIntWrapper>();
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndIntWrapper_FieldOffset_FieldA()
{
var value = new StructPack1WithByteAndIntWrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndIntWrapper_FieldOffset_FieldB()
{
var value = new StructPack1WithByteAndIntWrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructPack1WithByteAndIntWrapper2_Size()
{
return UnsafeUtility.SizeOf<StructPack1WithByteAndIntWrapper2>();
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndIntWrapper2_FieldOffset_FieldA()
{
var value = new StructPack1WithByteAndIntWrapper2();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructPack1WithByteAndIntWrapper2_FieldOffset_FieldB()
{
var value = new StructPack1WithByteAndIntWrapper2();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructWithSizeAndPack_Size()
{
return UnsafeUtility.SizeOf<StructWithSizeAndPack>();
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack_FieldOffset_FieldA()
{
var value = new StructWithSizeAndPack();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack_FieldOffset_FieldB()
{
var value = new StructWithSizeAndPack();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructWithSizeAndPackWrapper_Size()
{
return UnsafeUtility.SizeOf<StructWithSizeAndPackWrapper>();
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPackWrapper_FieldOffset_FieldA()
{
var value = new StructWithSizeAndPackWrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPackWrapper_FieldOffset_FieldB()
{
var value = new StructWithSizeAndPackWrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructWithSizeAndPack4_Size()
{
return UnsafeUtility.SizeOf<StructWithSizeAndPack4>();
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack4_FieldOffset_FieldA()
{
var value = new StructWithSizeAndPack4();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack4_FieldOffset_FieldB()
{
var value = new StructWithSizeAndPack4();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler]
public static int Test_StructWithSizeAndPack4Wrapper_Size()
{
return UnsafeUtility.SizeOf<StructWithSizeAndPack4Wrapper>();
}
#endif
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack4Wrapper_FieldOffset_FieldA()
{
var value = new StructWithSizeAndPack4Wrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler]
public static unsafe int Test_StructWithSizeAndPack4Wrapper_FieldOffset_FieldB()
{
var value = new StructWithSizeAndPack4Wrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
#endif
[TestCompiler]
public static int Test_StructExplicitPack1WithByteAndInt_Size()
{
return UnsafeUtility.SizeOf<StructExplicitPack1WithByteAndInt>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndInt_FieldOffset_FieldA()
{
var value = new StructExplicitPack1WithByteAndInt();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndInt_FieldOffset_FieldB()
{
var value = new StructExplicitPack1WithByteAndInt();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructExplicitPack1WithByteAndIntWrapper_Size()
{
return UnsafeUtility.SizeOf<StructExplicitPack1WithByteAndIntWrapper>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndIntWrapper_FieldOffset_FieldA()
{
var value = new StructExplicitPack1WithByteAndIntWrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndIntWrapper_FieldOffset_FieldB()
{
var value = new StructExplicitPack1WithByteAndIntWrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructExplicitPack1WithByteAndIntWrapper2_Size()
{
return UnsafeUtility.SizeOf<StructExplicitPack1WithByteAndIntWrapper2>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndIntWrapper2_FieldOffset_FieldA()
{
var value = new StructExplicitPack1WithByteAndIntWrapper2();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitPack1WithByteAndIntWrapper2_FieldOffset_FieldB()
{
var value = new StructExplicitPack1WithByteAndIntWrapper2();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructExplicitWithSizeAndPack_Size()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack_FieldOffset_FieldA()
{
var value = new StructExplicitWithSizeAndPack();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack_FieldOffset_FieldB()
{
var value = new StructExplicitWithSizeAndPack();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructExplicitWithSizeAndPackWrapper_Size()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPackWrapper>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPackWrapper_FieldOffset_FieldA()
{
var value = new StructExplicitWithSizeAndPackWrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPackWrapper_FieldOffset_FieldB()
{
var value = new StructExplicitWithSizeAndPackWrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static int Test_StructExplicitWithSizeAndPack4_Size()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack4>();
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack4_FieldOffset_FieldA()
{
var value = new StructExplicitWithSizeAndPack4();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack4_FieldOffset_FieldB()
{
var value = new StructExplicitWithSizeAndPack4();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler]
public static int Test_StructExplicitWithSizeAndPack4Wrapper_Size()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack4Wrapper>();
}
#endif
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack4Wrapper_FieldOffset_FieldA()
{
var value = new StructExplicitWithSizeAndPack4Wrapper();
var addressStart = &value;
var addressField = &value.FieldA;
return (int)((byte*)addressField - (byte*)addressStart);
}
// Commented out until upstream IL2CPP bug is fixed
#if BURST_TESTS_ONLY
[TestCompiler]
public static unsafe int Test_StructExplicitWithSizeAndPack4Wrapper_FieldOffset_FieldB()
{
var value = new StructExplicitWithSizeAndPack4Wrapper();
var addressStart = &value;
var addressField = &value.FieldB;
return (int)((byte*)addressField - (byte*)addressStart);
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10c2b0100e723deab40f35dc43e40cd5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,248 @@
// NOTE: Please read this before adding or changing anything in this file.
//
// This file doesn't contain any actual tests. It only contains structs.
// Tests are automatically generated from all structs in this file,
// which test:
// - the size of the struct
// - the offsets of each field
//
// When a struct contains a pointer, the test needs to use
// OverrideOn32BitNative so that wasm tests can compare with the correct
// values when testing 32-bit wasm on a 64-bit host platform.
// While it would be possible to use Roslyn to calculate these
// values automatically, for simplicity we use a couple of
// generator-specific attributes to set these manually:
// - [TestGeneratorOverride32BitSize(20)] should be set on a struct
// - [TestGeneratorOverride32BitOffset(12)] should be set on a field
// See the file below for examples.
//
// The test generation code lives in Burst.Compiler.IL.Tests.CodeGen.
// After making changes to this file, please run that project.
//
// The generated tests are in 050-TestStructsLayout.Generated.cs.
using System;
using System.Runtime.InteropServices;
namespace Burst.Compiler.IL.Tests
{
partial class TestStructsLayout
{
[StructLayout(LayoutKind.Explicit, Size = 8)]
private unsafe struct CheckHoleInner
{
[FieldOffset(0)]
public byte* m_Ptr;
}
[TestGeneratorOverride32BitSize(20)]
private struct CheckHoleOuter
{
public CheckHoleInner a;
public int b;
[TestGeneratorOverride32BitOffset(12)]
public CheckHoleInner c;
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStructWithoutSize2
{
[FieldOffset(0)] public long a;
[FieldOffset(8)] public sbyte b;
[FieldOffset(9)] public int c;
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStructWithoutSize
{
[FieldOffset(0)] public int a;
[FieldOffset(4)] public sbyte b;
[FieldOffset(5)] public int c;
}
[StructLayout(LayoutKind.Sequential, Size = 12)]
private struct SequentialStructWithSize3
{
public int a;
public int b;
public sbyte c;
}
[StructLayout(LayoutKind.Sequential)]
private struct SequentialStructWithoutSize
{
public int a;
public int b;
public sbyte c;
}
private struct SequentialStructEmptyNoAttributes { }
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStructWithEmptySequentialFields
{
[FieldOffset(0)] public SequentialStructEmptyNoAttributes FieldA;
[FieldOffset(0)] public SequentialStructEmptyNoAttributes FieldB;
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStrictWithEmptyAndNonEmptySequentialFields
{
[FieldOffset(0)] public SequentialStructEmptyNoAttributes FieldA;
[FieldOffset(0)] public SequentialStructWithoutSize FieldB;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct StructWithPack8
{
public int FieldA;
public int FieldB;
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private struct StructPack2WithBytesAndInt
{
public byte FieldA;
public byte FieldB;
public int FieldC;
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private struct StructPack2WithBytesAndInts
{
public byte FieldA;
public byte FieldB;
public int FieldC;
public int FieldD;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct StructPack1WithBytesAndInt
{
public byte FieldA;
public byte FieldB;
public int FieldC;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct StructPack1WithByteAndInt
{
public byte FieldA;
public int FieldB;
}
private struct StructPack1WithByteAndIntWrapper
{
public StructPack1WithByteAndInt FieldA;
public StructPack1WithByteAndInt FieldB;
}
private struct StructPack1WithByteAndIntWrapper2
{
public StructPack1WithByteAndIntWrapper FieldA;
public StructPack1WithByteAndIntWrapper FieldB;
}
[StructLayout(LayoutKind.Sequential, Size = 12, Pack = 1)]
private struct StructWithSizeAndPack
{
public double FieldA;
public int FieldB;
}
private struct StructWithSizeAndPackWrapper
{
public byte FieldA;
public StructWithSizeAndPack FieldB;
}
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 4)]
private struct StructWithSizeAndPack4
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
private struct StructWithSizeAndPack4Wrapper
{
public byte FieldA;
public StructWithSizeAndPack4 FieldB;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
private struct StructExplicitPack1WithByteAndInt
{
[FieldOffset(0)]
public byte FieldA;
[FieldOffset(1)]
public int FieldB;
}
private struct StructExplicitPack1WithByteAndIntWrapper
{
public StructExplicitPack1WithByteAndInt FieldA;
public StructExplicitPack1WithByteAndInt FieldB;
}
private struct StructExplicitPack1WithByteAndIntWrapper2
{
public StructExplicitPack1WithByteAndIntWrapper FieldA;
public StructExplicitPack1WithByteAndIntWrapper FieldB;
}
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 1)]
private struct StructExplicitWithSizeAndPack
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
private struct StructExplicitWithSizeAndPackWrapper
{
public byte FieldA;
public StructExplicitWithSizeAndPack FieldB;
}
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 4)]
private struct StructExplicitWithSizeAndPack4
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
private struct StructExplicitWithSizeAndPack4Wrapper
{
public byte FieldA;
public StructExplicitWithSizeAndPack4 FieldB;
}
}
[AttributeUsage(AttributeTargets.Struct)]
internal sealed class TestGeneratorOverride32BitSizeAttribute : Attribute
{
public readonly int Size;
public TestGeneratorOverride32BitSizeAttribute(int size)
{
Size = size;
}
}
[AttributeUsage(AttributeTargets.Field)]
internal sealed class TestGeneratorOverride32BitOffsetAttribute : Attribute
{
public readonly int Offset;
public TestGeneratorOverride32BitOffsetAttribute(int offset)
{
Offset = offset;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 828266174e573a6da71055ba8f017778
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5eb3e1cf0e2a3614927c245069c69baf
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,183 @@
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
internal class TestFixed
{
public unsafe struct SomeStruct
{
public static readonly int[] Ints = new int[4] { 1, 2, 3, 4 };
public struct OtherStruct
{
public int x;
}
public static readonly OtherStruct[] Structs = new OtherStruct[2] { new OtherStruct { x = 42 }, new OtherStruct { x = 13 } };
public fixed ushort array[42];
public struct Provider : IArgumentProvider
{
public object Value
{
get
{
var s = new SomeStruct();
for (ushort i = 0; i < 42; i++)
{
s.array[i] = i;
}
return s;
}
}
}
}
[TestCompiler]
public static unsafe int ReadInts()
{
fixed (int* ptr = SomeStruct.Ints)
{
return ptr[2];
}
}
[TestCompiler]
public static unsafe int ReadIntsElement()
{
fixed (int* ptr = &SomeStruct.Ints[1])
{
return ptr[0];
}
}
[TestCompiler]
public static unsafe int ReadStructs()
{
fixed (SomeStruct.OtherStruct* ptr = SomeStruct.Structs)
{
return ptr[1].x;
}
}
[TestCompiler]
public static unsafe int ReadStructsElement()
{
fixed (SomeStruct.OtherStruct* ptr = &SomeStruct.Structs[1])
{
return ptr[0].x;
}
}
[TestCompiler(typeof(SomeStruct.Provider))]
public static unsafe ushort ReadFromFixedArray(ref SomeStruct s)
{
fixed (ushort* ptr = s.array)
{
ushort total = 0;
for (ushort i = 0; i < 42; i++)
{
total += ptr[i];
}
return total;
}
}
// The below tests are designed to verify the indexer is treated correctly for various fixed arrays (only the smallest case)
//(the bug was actually to do with pointer addition, so see 031-Pointer.cs for additional coverage)
//Its not perfect as if the indexer is treated as signed, then in burst we will read off the beginning of the array
//which might be into another array or off the beginning of the struct... and the value might accidently be correct.
public unsafe struct IndexerStructTestSByte
{
public fixed sbyte sbyteArray[256];
public struct Provider : IArgumentProvider
{
public object Value
{
get
{
var s = new IndexerStructTestSByte();
for (int a=0;a<256;a++)
{
s.sbyteArray[a] = sbyte.MinValue;
}
s.sbyteArray[127] = 127;
s.sbyteArray[128] = 63;
s.sbyteArray[255] = 23;
return s;
}
}
}
}
public unsafe struct IndexerStructTestByte
{
public fixed byte byteArray[256];
public struct Provider : IArgumentProvider
{
public object Value
{
get
{
var s = new IndexerStructTestByte();
for (int a=0;a<256;a++)
{
s.byteArray[a] = byte.MinValue;
}
s.byteArray[127] = 129;
s.byteArray[128] = 212;
s.byteArray[255] = 165;
return s;
}
}
}
}
// SByte array with different indexer types
[TestCompiler(typeof(IndexerStructTestSByte.Provider),(byte)0)]
[TestCompiler(typeof(IndexerStructTestSByte.Provider),(byte)128)]
[TestCompiler(typeof(IndexerStructTestSByte.Provider),(byte)255)]
public static unsafe sbyte IndexerReadFromSByteArrayWithByteOffset(ref IndexerStructTestSByte s, byte offset)
{
return s.sbyteArray[offset];
}
[TestCompiler(typeof(IndexerStructTestSByte.Provider),(sbyte)0)]
[TestCompiler(typeof(IndexerStructTestSByte.Provider),(sbyte)127)] // signed offset so limited
public static unsafe sbyte IndexerReadFromSByteArrayWithSByteOffset(ref IndexerStructTestSByte s, sbyte offset)
{
return s.sbyteArray[offset];
}
// Byte array with different indexer types
[TestCompiler(typeof(IndexerStructTestByte.Provider),(byte)0)]
[TestCompiler(typeof(IndexerStructTestByte.Provider),(byte)128)]
[TestCompiler(typeof(IndexerStructTestByte.Provider),(byte)255)]
public static unsafe byte IndexerReadFromByteArrayWithByteOffset(ref IndexerStructTestByte s, byte offset)
{
return s.byteArray[offset];
}
[TestCompiler(typeof(IndexerStructTestByte.Provider),(sbyte)0)]
[TestCompiler(typeof(IndexerStructTestByte.Provider),(sbyte)127)] // signed offset so limited
public static unsafe byte IndexerReadFromByteArrayWithSByteOffset(ref IndexerStructTestByte s, sbyte offset)
{
return s.byteArray[offset];
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36990957b5d73a9681ce7dd9f4d5a7b0
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,313 @@
using NUnit.Framework;
using Unity.Burst;
using Unity.Mathematics;
namespace Burst.Compiler.IL.Tests
{
internal class TestConstArrays
{
[TestCompiler]
public static int ReadFromIntArray()
{
return StructWithConstArray1.IntValues[1];
}
[TestCompiler]
public static unsafe int ReadViaFixed()
{
fixed (int* ptr = StructWithConstArray1.IntValues)
{
return ptr[2];
}
}
[TestCompiler]
public static int ReadFromColorArray()
{
var color = StructWithConstArrayWithStruct1.Colors[1];
return ((color.R * 255) + color.G) * 255 + color.B;
}
[TestCompiler]
public static int ReadFromColorArray2()
{
var color = StaticArrayStruct.Colors[1];
return ((color.R * 255) + color.G) * 255 + color.B;
}
struct StructWithConstArray1
{
public static readonly int[] IntValues = new int[4] { 1, 2, 3, 4 };
}
struct StructWithConstArrayWithStruct1
{
public static readonly Color[] Colors = { new Color(), new Color(1, 2, 3, 255) };
}
private struct Color
{
public Color(byte r, byte g, byte b, byte a)
{
R = r;
G = g;
B = b;
A = a;
}
public byte R, G, B, A;
}
private struct StaticArrayStruct
{
public static readonly double[] Doubles = { 3, 6, 9, 42, 43 };
public static readonly byte[] Bytes = { 1, 2, 3 };
public static readonly ushort[] UShorts = { 2, 6, 8, 2, 0 };
public static readonly int[] Ints = { -6, 6, 50 };
public static readonly int[] ZeroData = { 0, 0, 0, 0 };
public static readonly int[] ZeroLength = { };
public static readonly Color[] ZeroLengthStruct = { };
public static readonly Color[] Colors = { new Color(), new Color(1, 2, 3, 255) };
public static readonly int3[] Positions = { new int3(0, 0, 1), new int3(0, 1, 0), new int3(1, 0, 0) };
}
[TestCompiler]
public static int TestStaticReadonlyArrayLength()
{
return StaticArrayStruct.Doubles.Length + StaticArrayStruct.Bytes.Length +
StaticArrayStruct.UShorts.Length + StaticArrayStruct.Ints.Length +
StaticArrayStruct.ZeroData.Length + StaticArrayStruct.ZeroLength.Length +
StaticArrayStruct.ZeroLengthStruct.Length + StaticArrayStruct.Colors.Length +
StaticArrayStruct.Positions.Length;
}
private struct StructP
{
public static readonly int[] Value = new int[One()];
public static int One()
{
return 1;
}
}
[TestCompiler]
public static int TestStaticReadonlyArrayNonConstantLength()
{
return StructP.Value.Length;
}
private struct StructQ
{
public static readonly int[] Value = new int[10];
public static int One()
{
return 1;
}
static StructQ()
{
Value[One()] = 1;
}
}
[TestCompiler]
public static int TestStaticReadonlyArrayWithNonConstantStelemIndex()
{
return StructQ.Value[1];
}
private struct StructR
{
#pragma warning disable 0649
public static int[] Value;
#pragma warning restore 0649
static StructR()
{
Value[0] = 1;
}
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromManagedNonReadonlyStaticFieldNotSupported)]
public static int TestStaticReadonlyArrayExplicitConstructionOfUninitialized()
{
return StructR.Value.Length;
}
private struct StructS
{
public static readonly int[] Value = new int[10];
static StructS()
{
Value[0] = 1;
Value[1] = 2;
Value[2] = 8;
Value[3] = 2;
Value[4] = 0;
Value[5] = 2;
Value[6] = 1;
Value[7] = 2;
Value[8] = 2;
Value[9] = 3;
}
}
[TestCompiler]
public static int TestStaticReadonlyArrayExplicitConstruction()
{
int sum = 0;
for (int i = 0; i < 10; i++) sum += StructS.Value[i];
return sum;
}
[TestCompiler]
public static int TestStaticReadonlyArrayLdelem()
{
var doubles = StaticArrayStruct.Doubles[0];
for (int i = 1; i < StaticArrayStruct.Doubles.Length; i++) doubles += StaticArrayStruct.Doubles[i];
var bytes = StaticArrayStruct.Bytes[0];
for (int i = 1; i < StaticArrayStruct.Bytes.Length; i++) bytes += StaticArrayStruct.Bytes[i];
var ushorts = StaticArrayStruct.UShorts[0];
for (int i = 1; i < StaticArrayStruct.UShorts.Length; i++) ushorts += StaticArrayStruct.UShorts[i];
var ints = StaticArrayStruct.Ints[0];
for (int i = 1; i < StaticArrayStruct.Ints.Length; i++) ints += StaticArrayStruct.Ints[i];
ints += StaticArrayStruct.ZeroData[0];
for (int i = 1; i < StaticArrayStruct.ZeroData.Length; i++) ints += StaticArrayStruct.ZeroData[i];
for (int i = 0; i < StaticArrayStruct.ZeroLength.Length; i++) doubles += StaticArrayStruct.ZeroLength[i];
bytes = (byte)(StaticArrayStruct.Colors[0].R + StaticArrayStruct.Colors[0].G
+ StaticArrayStruct.Colors[0].B
+ StaticArrayStruct.Colors[0].A);
for (int i = 1; i < StaticArrayStruct.Colors.Length; i++)
bytes += (byte)(StaticArrayStruct.Colors[i].R + StaticArrayStruct.Colors[i].G
+ StaticArrayStruct.Colors[i].B
+ StaticArrayStruct.Colors[i].A);
for (int i = 1; i < StaticArrayStruct.Positions.Length; i++)
ints += math.dot(StaticArrayStruct.Positions[i - 1], StaticArrayStruct.Positions[i]);
return (int)doubles + bytes + ushorts + ints;
}
private static T TakesRef<T>(ref T x)
{
return x;
}
[TestCompiler]
public static int TestStaticReadonlyArrayWithElementRef()
{
return TakesRef(ref StaticArrayStruct.Ints[1]);
}
[TestCompiler]
public static int TestStaticReadonlyArrayWithElementVectorRef()
{
var x = TakesRef(ref StaticArrayStruct.Positions[1]);
return math.dot(x, x);
}
[TestCompiler(1)]
[TestCompiler(2)]
[TestCompiler(3)]
[TestCompiler(4)]
public static int TestStaticReadonlyArrayWithDynamicLdelem(int count)
{
int sum = 0;
for (int i = 0; i < count; i++)
{
sum += (int)StaticArrayStruct.Doubles[i];
}
return sum;
}
public struct ContainerStruct
{
public SmallStruct A;
public SmallStruct B;
public static readonly ContainerStruct[] CoolStructs =
{
new ContainerStruct
{
A = new SmallStruct { a = 3, b = 5 },
B = new SmallStruct { a = 9, b = 10 }
},
new ContainerStruct
{
A = new SmallStruct { a = 1, b = 5 },
B = new SmallStruct { a = 7, b = 8 }
}
};
}
[TestCompiler]
public static int TestStaticReadonlyArrayOfStructOfStructs()
{
return ContainerStruct.CoolStructs[0].A.a + ContainerStruct.CoolStructs[0].A.b +
ContainerStruct.CoolStructs[0].B.a + ContainerStruct.CoolStructs[0].B.b +
ContainerStruct.CoolStructs[1].A.a + ContainerStruct.CoolStructs[1].A.b +
ContainerStruct.CoolStructs[1].B.a + ContainerStruct.CoolStructs[1].B.b;
}
/* There's currently no way of settings the safety checks on from here
[TestCompiler(0xFFFFFFF, ExpectedException = typeof(IndexOutOfRangeException))]
public static int TestStaticReadonlyLdelemDynamicIndexOfBounds(int x)
{
return StaticArrayStruct.Ints[x];
}
*/
public struct SmallStruct
{
public int a;
public int b;
}
public struct NullArrayHolder
{
public static readonly int[] Array = null;
}
[TestCompiler()]
public static int TestStaticReadonlyNullArray()
{
if (NullArrayHolder.Array == null)
{
return 40;
}
return 3;
}
private static readonly int[] SomeArray = { 42, 13 };
[TestCompiler(42)]
public static int StoreNullIntoLocalArray(int x)
{
int[] someArray;
if (x == 0)
{
someArray = SomeArray;
}
else
{
someArray = null;
}
return someArray?.Length ?? 0;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b192164f6d13e9aaf88fbb6833566c4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,201 @@
using Burst.Compiler.IL.Tests.Helpers;
using Unity.Burst.CompilerServices;
using Unity.Mathematics;
namespace Burst.Compiler.IL.Tests
{
internal class Peephole
{
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtEqualFast(float f)
{
return math.sqrt(f) == 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtNotEqualFast(float f)
{
return math.sqrt(f) != 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100)]
public static int SqrtLessThan(float f)
{
return math.sqrt(f) < 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanFast(float f)
{
return math.sqrt(f) < 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanLargeConstant(float f)
{
return math.sqrt(f) < float.MaxValue ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanFastVector(ref float4 f)
{
return math.all(math.sqrt(f) < 2) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanLargeConstantVector(ref float4 f)
{
return math.all(math.sqrt(f) < new float4(1, 2, 3, float.MaxValue)) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100)]
public static int SqrtGreaterThan(float f)
{
return math.sqrt(f) > 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanFast(float f)
{
return math.sqrt(f) > 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanLargeConstant(float f)
{
return math.sqrt(f) > float.MaxValue ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanFastVector(ref float4 f)
{
return math.all(math.sqrt(f) > 2) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanLargeConstantVector(ref float4 f)
{
return math.all(math.sqrt(f) > new float4(1, 2, 3, float.MaxValue)) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100)]
public static int SqrtLessThanEqual(float f)
{
return math.sqrt(f) <= 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanEqualFast(float f)
{
return math.sqrt(f) <= 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanEqualLargeConstant(float f)
{
return math.sqrt(f) <= float.MaxValue ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanEqualFastVector(ref float4 f)
{
return math.all(math.sqrt(f) <= 2) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtLessThanEqualLargeConstantVector(ref float4 f)
{
return math.all(math.sqrt(f) <= new float4(1, 2, 3, float.MaxValue)) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100)]
public static int SqrtGreaterThanEqual(float f)
{
return math.sqrt(f) >= 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanEqualFast(float f)
{
return math.sqrt(f) >= 2 ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanEqualLargeConstant(float f)
{
return math.sqrt(f) >= float.MaxValue ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanEqualFastVector(ref float4 f)
{
return math.all(math.sqrt(f) >= 2) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtGreaterThanEqualLargeConstantVector(ref float4 f)
{
return math.all(math.sqrt(f) >= new float4(1, 2, 3, float.MaxValue)) ? 42 : 13;
}
[TestCompiler(DataRange.ZeroExclusiveTo100, DataRange.ZeroExclusiveTo100, FastMath = true)]
public static int SqrtAndSqrtFast(ref float4 a, ref float4 b)
{
return math.all(math.sqrt(a) >= math.sqrt(b)) ? 42 : 13;
}
[TestCompiler(0)]
public static float FloatExp2FromInt(int a)
{
return math.exp2(a);
}
[TestCompiler(0)]
public static double DoubleExp2FromInt(int a)
{
return math.exp2((double)a);
}
[TestCompiler((ushort)0)]
public static float FloatExp2FromUShort(ushort a)
{
return math.exp2(a);
}
[TestCompiler((ushort)0)]
public static double DoubleExp2FromUShort(ushort a)
{
return math.exp2((double)a);
}
[TestCompiler(0)]
public static float FloatPowFromInt(int a)
{
return math.pow(2.0f, a);
}
[TestCompiler(0)]
public static double DoublePowFromInt(int a)
{
return math.pow(2.0, a);
}
[TestCompiler(0u)]
public static float FloatPowFromUInt(uint a)
{
return math.pow(2.0f, a);
}
[TestCompiler(0u)]
public static double DoublePowFromUInt(uint a)
{
return math.pow(2.0, a);
}
[TestCompiler(int.MaxValue)]
public static int AShrToLShr([AssumeRange(0, int.MaxValue)] int a)
{
return a >> 4;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d15bef20cb43dc49f6156cde028d37a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,108 @@
namespace Burst.Compiler.IL.Tests.Shared
{
internal class TestStackalloc
{
[TestCompiler]
public static unsafe int Stackalloc1ByteWithInitializer()
{
var value = stackalloc byte[1] { 0xA4 };
return value[0];
}
[TestCompiler]
public static unsafe int Stackalloc16BytesWithInitializer()
{
// Roslyn generates quite different IL when the number of bytes is larger than 8.
var value = stackalloc byte[16] { 0xA4, 0xA1, 0x20, 0xA5, 0x80, 0x17, 0xF6, 0x4F, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16 };
return value[9];
}
[TestCompiler]
public static unsafe int Stackalloc16IntsWithInitializer()
{
var value = stackalloc int[16] { 0xA4, 0xA1, 0x20, 0xA5, 0x80, 0x17, 0xF6, 0x4F, 0xBD, 0x18, 0x16, 0x73, 0x43, 0xC5, 0xAF, 0x16 };
return value[9];
}
[TestCompiler(1)]
public static unsafe int StackallocInBranch(int takeBranch)
{
int* array = null;
if (takeBranch != 0)
{
int* elem = stackalloc int[1];
array = elem;
}
if (takeBranch != 0)
{
int* elem = stackalloc int[1];
if (array == elem)
{
return -1;
}
}
return 0;
}
[TestCompiler(4)]
public static unsafe int StackallocInLoop(int iterations)
{
int** array = stackalloc int*[iterations];
for (int i = 0; i < iterations; i++)
{
int* elem = stackalloc int[1];
array[i] = elem;
}
for (int i = 0; i < iterations; i++)
{
for (int k = i + 1; k < iterations; k++)
{
// Make sure all the stack allocations within the loop are unique addresses.
if (array[i] == array[k])
{
return -1;
}
}
}
return 0;
}
[TestCompiler]
public static unsafe int StackallocWithUnmanagedConstructedType()
{
var value = stackalloc[]
{
new Point<int> { X = 1, Y = 2 },
new Point<int> { X = 42, Y = 5 },
new Point<int> { X = 3, Y = -1 },
};
return value[1].X;
}
private struct Point<T>
{
public T X;
public T Y;
}
#if UNITY_2021_2_OR_NEWER || BURST_INTERNAL
[TestCompiler]
public static int StackallocInNestedExpression()
{
return StackallocInNestedExpressionHelper(stackalloc[] { 2, 4, 6, 8 });
}
private static int StackallocInNestedExpressionHelper(System.Span<int> span)
{
return span[2];
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 614354dff06f39c4b6ef0e21f5a45d23
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,151 @@
using System;
using NUnit.Framework;
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Test with enums.
/// </summary>
internal partial class TestEnums
{
[System.Flags]
public enum MyEnum
{
Hello = 5,
Something = 10
}
[TestCompiler]
public static int test_enum_cast_to_int()
{
MyEnum value = MyEnum.Hello;
return (int)value;
}
[TestCompiler(MyEnum.Hello)]
[TestCompiler(MyEnum.Something)]
public static int test_enum_compare(MyEnum value)
{
if (value == MyEnum.Hello)
return 0;
else
return 1;
}
//[TestCompiler(typeof(StructContainingEnumProvider))]
//public static int test_enum_in_struct(ref StructContainingEnum myStruct)
//{
// return myStruct.intValue + (int)myStruct.value;
//}
[TestCompiler(MyEnum.Hello)]
[TestCompiler(MyEnum.Something)]
[Ignore("Failure")]
public static int test_enum_has_flag(MyEnum value)
{
return value.HasFlag(MyEnum.Hello) ? 3 : 4;
}
[TestCompiler(MyEnum.Hello)]
[TestCompiler(MyEnum.Something)]
public static int test_enum_and_mask(MyEnum value)
{
return (value & MyEnum.Hello) != 0 ? 3 : 4;
}
[TestCompiler(1)]
[TestCompiler(2)]
public static int TestEnumSwitchCase(IntPtr value)
{
var enumValue = (SmallEnum) value.ToInt32();
// Need at least 3 cases to generate a proper switch
// otherwise Roslyn will generate an if/else
switch (enumValue)
{
case SmallEnum.One:
return 7;
case SmallEnum.Two:
return 8;
case SmallEnum.Three:
return 9;
default:
return 10;
}
}
private static int GetToInt32(int value)
{
return value;
}
[TestCompiler]
public static int test_enum_sizeof_small_enum()
{
return sizeof(SmallEnum);
}
[TestCompiler(SmallEnum.Three)]
public static int test_enum_sizeof_small_enum_in_struct_access(SmallEnum value)
{
var s = new MySmallEnumStruct
{
a = value,
b = value,
c = value
};
return (int)s.a + (int)s.b + (int)s.c;
}
public struct StructContainingEnum
{
public MyEnum value;
public int intValue;
}
public enum SmallEnum : byte
{
One,
Two,
Three
}
public struct MySmallEnumStruct
{
public SmallEnum a;
public SmallEnum b;
public SmallEnum c;
public SmallEnum d;
}
public enum SomeByteEnum : byte
{
First = 0,
Last = 255
}
public unsafe struct FixedByte4Struct
{
fixed byte bytes[4];
public SomeByteEnum this[SomeByteEnum index]
{
get { return (SomeByteEnum)bytes[(int)index]; }
}
public struct Provider : IArgumentProvider
{
public object Value => new FixedByte4Struct { };
}
}
[TestCompiler(typeof(FixedByte4Struct.Provider))]
public static SomeByteEnum test_enum_indexer(ref FixedByte4Struct bytes)
{
return bytes[0];
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7771e50c9f91376ca0db91c33bfccfda
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,173 @@
using System;
using System.Threading;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests of the <see cref="Interlocked"/> functions.
/// </summary>
internal class TestAtomics
{
[TestCompiler(1)]
[TestCompiler(-1)]
public static int test_atomic_increment_int(ref int value)
{
return Interlocked.Increment(ref value);
}
[TestCompiler(1L)]
[TestCompiler(-1L)]
public static long test_atomic_increment_long(ref long value)
{
return Interlocked.Increment(ref value);
}
[TestCompiler(1)]
[TestCompiler(-1)]
public static int test_atomic_add_int(ref int value)
{
return Interlocked.Add(ref value, 2);
}
[TestCompiler(1L)]
[TestCompiler(-1L)]
public static long test_atomic_add_long(ref long value)
{
return Interlocked.Add(ref value, 2);
}
[TestCompiler(1, 2, 1)]
[TestCompiler(1, 10, 1)]
[TestCompiler(1, 2, 2)]
[TestCompiler(7, 2, 1)]
[TestCompiler(7, 10, 1)]
[TestCompiler(7, 2, 2)]
public static int test_atomic_compare_and_exchange_int(ref int location, int value, int compareAnd)
{
return Interlocked.CompareExchange(ref location, value, compareAnd);
}
[TestCompiler(1L, 2L, 1L)]
[TestCompiler(1L, 10L, 1L)]
[TestCompiler(1L, 2L, 2L)]
[TestCompiler(7L, 2L, 1L)]
[TestCompiler(7L, 10L, 1L)]
[TestCompiler(7L, 2L, 2L)]
public static long test_atomic_compare_and_exchange_long(ref long location, long value, long compareAnd)
{
return Interlocked.CompareExchange(ref location, value, compareAnd);
}
[TestCompiler(1)]
[TestCompiler(-1)]
public static int test_atomic_decrement_int(ref int value)
{
return Interlocked.Decrement(ref value);
}
[TestCompiler(1L)]
[TestCompiler(-1L)]
public static long test_atomic_decrement_long(ref long value)
{
return Interlocked.Decrement(ref value);
}
[TestCompiler(1)]
public static int test_atomic_exchange_int(ref int value)
{
return Interlocked.Exchange(ref value, 5);
}
[TestCompiler(1L)]
public static long test_atomic_exchange_long(ref long value)
{
return Interlocked.Exchange(ref value, 5);
}
[TestCompiler(1)]
public static IntPtr ExchangeIntPtr(IntPtr value)
{
return Interlocked.Exchange(ref value, new IntPtr(5));
}
[TestCompiler(1, 2, 1)]
[TestCompiler(1, 10, 1)]
[TestCompiler(1, 2, 2)]
public static IntPtr CompareExchangeIntPtr(IntPtr location, IntPtr value, IntPtr compareAnd)
{
return Interlocked.CompareExchange(ref location, value, compareAnd);
}
[TestCompiler]
public static void test_atomic_memorybarrier()
{
Interlocked.MemoryBarrier();
}
[TestCompiler(0)]
public static int Case1111040(int val)
{
int test = val;
Interlocked.Increment(ref test);
Interlocked.Decrement(ref test);
return test;
}
[TestCompiler(42.0f)]
public static float ExchangeFloat(ref float f)
{
if (Interlocked.Exchange(ref f, 13) == 42)
{
return f;
}
return 0;
}
[TestCompiler(42.0)]
public static double ExchangeDouble(ref double d)
{
if (Interlocked.Exchange(ref d, 13) == 42)
{
return d;
}
return 0;
}
#if !BURST_APPLE_SILICON_TESTING // https://jira.unity3d.com/browse/UUM-9159
[TestCompiler(42.0f)]
public static float CompareExchangeFloat(ref float f)
{
if (Interlocked.CompareExchange(ref f, 13, 42) == 42)
{
return f;
}
return 0;
}
#endif
[TestCompiler(42.0)]
public static double CompareExchangeDouble(ref double d)
{
if (Interlocked.CompareExchange(ref d, 13, 42) == 42)
{
return d;
}
return 0;
}
[TestCompiler(42L)]
public static double Read(ref long l)
{
if (Interlocked.Read(ref l) == 42)
{
return l;
}
return 0;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1bc4bf3e38931c9b2014cc764d03dea
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,265 @@
using System;
using Burst.Compiler.IL.Tests.Helpers;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests of the <see cref="System.Math"/> functions.
/// </summary>
internal class TestSystemMath
{
[TestCompiler(DataRange.Standard)]
public static double TestCos(float value)
{
return Math.Cos(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestSin(float value)
{
return Math.Sin(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestTan(float value)
{
return (float) Math.Tan(value);
}
[TestCompiler(DataRange.Standard11)]
public static double TestAcos(float value)
{
return Math.Acos(value);
}
[TestCompiler(DataRange.Standard11)]
public static double TestAsin(float value)
{
return Math.Asin(value);
}
[TestCompiler(DataRange.Standard11)]
public static float TestAtan(float value)
{
return (float)Math.Atan(value);
}
[TestCompiler(DataRange.ZeroExclusiveToOneInclusive, DataRange.ZeroExclusiveToOneInclusive)]
public static float TestAtan2(float y, float x)
{
return (float)Math.Atan2(y, x);
}
[TestCompiler(DataRange.Standard)]
public static double TestCosh(float value)
{
return Math.Cosh(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestSinh(float value)
{
return Math.Sinh(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestTanh(float value)
{
return (float)Math.Tanh(value);
}
[TestCompiler(DataRange.StandardPositive)]
public static double TestSqrt(float value)
{
return Math.Sqrt(value);
}
[TestCompiler(DataRange.StandardPositive & ~DataRange.Zero)]
public static double TestLog(float value)
{
return Math.Log(value);
}
[TestCompiler(DataRange.StandardPositive & ~DataRange.Zero)]
public static double TestLog10(float value)
{
return Math.Log10(value);
}
[TestCompiler(DataRange.StandardPositive)]
public static double TestExp(float value)
{
return Math.Exp(value);
}
[TestCompiler(DataRange.Standard & ~(DataRange.Zero|DataRange.NaN), DataRange.Standard)]
[TestCompiler(DataRange.Standard & ~DataRange.Zero, DataRange.Standard & ~DataRange.Zero)]
public static double TestPow(float value, float power)
{
return Math.Pow(value, power);
}
[TestCompiler(DataRange.Standard)]
public static sbyte TestAbsSByte(sbyte value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard)]
public static short TestAbsShort(short value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard)]
public static int TestAbsInt(int value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard)]
public static long TestAbsLong(long value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestAbsFloat(float value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestAbsDouble(double value)
{
return Math.Abs(value);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static int TestMaxInt(int left, int right)
{
return Math.Max(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static int TestMinInt(int left, int right)
{
return Math.Min(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static double TestMaxDouble(double left, double right)
{
return Math.Max(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static double TestMinDouble(double left, double right)
{
return Math.Min(left, right);
}
[TestCompiler(DataRange.Standard)]
public static int TestSignInt(int value)
{
return Math.Sign(value);
}
[TestCompiler(DataRange.Standard & ~DataRange.NaN)]
public static int TestSignFloat(float value)
{
return Math.Sign(value);
}
[TestCompiler(float.NaN, ExpectedException = typeof(ArithmeticException))]
[MonoOnly(".NET CLR does not support burst.abort correctly")]
public static int TestSignException(float value)
{
return Math.Sign(value);
}
[TestCompiler(DataRange.Standard & ~DataRange.NaN)]
public static int TestSignDouble(double value)
{
return Math.Sign(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestCeilingDouble(double value)
{
return Math.Ceiling(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestFloorDouble(double value)
{
return Math.Floor(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestRoundDouble(double value)
{
return Math.Round(value);
}
[TestCompiler(DataRange.Standard)]
public static double TestTruncateDouble(double value)
{
return Math.Truncate(value);
}
[TestCompiler(DataRange.Standard, DataRange.Standard & ~DataRange.Zero)]
public static int TestDivRemInt(int a, int b)
{
int remResult;
var divResult = Math.DivRem(a, b, out remResult);
return divResult + remResult * 7;
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
[TestCompiler(int.MaxValue, DataRange.Standard)]
public static long TestBigMulInt(int a, int b)
{
return Math.BigMul(a, b);
}
[TestCompiler(DataRange.Standard & ~DataRange.Zero, DataRange.Standard & ~DataRange.Zero)]
public static double TestLogWithBaseDouble(double a, double newBase)
{
return Math.Log(a, newBase);
}
//[TestCompiler(1.0, 2.0)]
//[TestCompiler(10.0, 3.0)]
//[TestCompiler(15.0, 4.0)]
//[Ignore("Not yet supported")]
//public static double TestIEEERemainder(double a, double newBase)
//{
// return Math.IEEERemainder(a, newBase);
//}
[TestCompiler(DataRange.Standard)]
public static bool TestIsNanDouble(double a)
{
return double.IsNaN(a);
}
[TestCompiler(DataRange.Standard)]
public static bool TestIsNanFloat(float a)
{
return float.IsNaN(a);
}
[TestCompiler(DataRange.Standard)]
public static bool TestIsInfinityDouble(double a)
{
return double.IsInfinity(a);
}
[TestCompiler(DataRange.Standard)]
public static bool TestIsInfinityFloat(float a)
{
return float.IsInfinity(a);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47108b7d7a79385f98626b40f17ef46d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,192 @@
using System;
using System.Threading;
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests of the <see cref="System.Threading"/> functions.
/// </summary>
internal class TestSystemThreading
{
[TestCompiler]
public static void TestMemoryBarrier()
{
Thread.MemoryBarrier();
}
[TestCompiler]
public static int TestReadBool()
{
var data = false;
return (Volatile.Read(ref data) ? 1 : 0) + (Volatile.Read(ref data) ? 1 : 0);
}
[TestCompiler((byte)42)]
public static int TestReadByte(ref byte data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler((sbyte)42)]
public static int TestReadSByte(ref sbyte data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler((short)42)]
public static int TestReadShort(ref short data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler((ushort)42)]
public static int TestReadUShort(ref ushort data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler(42)]
public static int TestReadInt(ref int data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler(42u)]
public static uint TestReadUInt(ref uint data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler((long)42)]
public static long TestReadLong(ref long data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler((ulong)42)]
public static ulong TestReadULong(ref ulong data)
{
return Volatile.Read(ref data) + Volatile.Read(ref data);
}
[TestCompiler(42.0f)]
public static float TestReadFloat(ref float data)
{
return Volatile.Read(ref data);
}
[TestCompiler(42.0)]
public static double TestReadDouble(ref double data)
{
return Volatile.Read(ref data);
}
public struct UIntPtrProvider : IArgumentProvider
{
public object Value => UIntPtr.Zero;
}
[TestCompiler(typeof(UIntPtrProvider))]
public static UIntPtr TestReadUIntPtr(ref UIntPtr data)
{
return Volatile.Read(ref data);
}
[TestCompiler]
public static int TestWriteBool()
{
var data = false;
Volatile.Write(ref data, true);
return data ? 1 : 0;
}
[TestCompiler((byte)42)]
public static int TestWriteByte(ref byte data)
{
var result = data;
Volatile.Write(ref data, 1);
return result + data;
}
[TestCompiler((sbyte)42)]
public static int TestWriteSByte(ref sbyte data)
{
var result = data;
Volatile.Write(ref data, 2);
return result + data;
}
[TestCompiler((short)42)]
public static int TestWriteShort(ref short data)
{
var result = data;
Volatile.Write(ref data, 3);
return result + data;
}
[TestCompiler((ushort)42)]
public static int TestWriteUShort(ref ushort data)
{
var result = data;
Volatile.Write(ref data, 4);
return result + data;
}
[TestCompiler(42)]
public static int TestWriteInt(ref int data)
{
var result = data;
Volatile.Write(ref data, 5);
return result + data;
}
[TestCompiler(42u)]
public static uint TestWriteUInt(ref uint data)
{
var result = data;
Volatile.Write(ref data, 6);
return result + data;
}
[TestCompiler((long)42)]
public static long TestWriteLong(ref long data)
{
var result = data;
Volatile.Write(ref data, 7);
return result + data;
}
[TestCompiler((ulong)42)]
public static ulong TestWriteULong(ref ulong data)
{
var result = data;
Volatile.Write(ref data, 8);
return result + data;
}
[TestCompiler(42.0f)]
public static float TestWriteFloat(ref float data)
{
var result = data;
Volatile.Write(ref data, 9);
return result + data;
}
[TestCompiler(42.0)]
public static double TestWriteDouble(ref double data)
{
var result = data;
Volatile.Write(ref data, 10);
return result + data;
}
[TestCompiler(typeof(UIntPtrProvider))]
public static UIntPtr TestWriteUIntPtr(ref UIntPtr data)
{
var result = data;
Volatile.Write(ref data, new UIntPtr(11));
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 631a9c909a1e35999f140f79ef90911a
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,351 @@
using Burst.Compiler.IL.Tests.Helpers;
using System.Linq.Expressions;
using Unity.Mathematics;
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests a few single float functions for <see cref="Unity.Mathematics.math"/> functions.
/// </summary>
internal class TestUnityMath
{
[TestCompiler(DataRange.Standard)]
public static float TestCos(float value)
{
return math.cos(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestSin(float value)
{
return math.sin(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestTan(float value)
{
return (float) math.tan(value);
}
[TestCompiler(-1000000f)]
[TestCompiler(-1.2f)]
public static float TestTan2(float value)
{
return (float)math.tan(value);
}
[TestCompiler(DataRange.Standard11)]
public static float TestAcos(float value)
{
return math.acos(value);
}
[TestCompiler(DataRange.Standard11)]
public static float TestAsin(float value)
{
return math.asin(value);
}
[TestCompiler(DataRange.Standard11)]
public static float TestAtan(float value)
{
return (float)math.atan(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestCosh(float value)
{
return math.cosh(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestSinh(float value)
{
return math.sinh(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestTanh(float value)
{
return (float)math.tanh(value);
}
[TestCompiler(DataRange.StandardPositive)]
public static float TestSqrt(float value)
{
return math.sqrt(value);
}
[TestCompiler(DataRange.StandardPositive & ~DataRange.Zero)]
public static float TestLog(float value)
{
return math.log(value);
}
[TestCompiler(DataRange.StandardPositive & ~DataRange.Zero)]
public static float TestLog10(float value)
{
return math.log10(value);
}
[TestCompiler(DataRange.StandardPositive)]
public static float TestExp(float value)
{
return math.exp(value);
}
[TestCompiler(DataRange.Standard & ~(DataRange.Zero|DataRange.NaN), DataRange.Standard)]
[TestCompiler(DataRange.Standard & ~DataRange.Zero, DataRange.Standard & ~DataRange.Zero)]
public static float TestPow(float value, float power)
{
return math.pow(value, power);
}
[TestCompiler(DataRange.Standard)]
public static float TestAbsFloat(float value)
{
return math.abs(value);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static int TestMaxInt(int left, int right)
{
return math.max(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static int TestMinInt(int left, int right)
{
return math.min(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static float TestMaxfloat(float left, float right)
{
return math.max(left, right);
}
[TestCompiler(DataRange.Standard, DataRange.Standard)]
public static float TestMinfloat(float left, float right)
{
return math.min(left, right);
}
[TestCompiler(DataRange.Standard & ~DataRange.NaN)]
public static float TestSignFloat(float value)
{
return math.sign(value);
}
[TestCompiler(-123.45)]
[TestCompiler(-1E-20)]
[TestCompiler(0.0)]
[TestCompiler(1E-10)]
[TestCompiler(123.45)]
[TestCompiler(double.NegativeInfinity)]
[TestCompiler(double.NaN)]
[TestCompiler(double.PositiveInfinity)]
public static double TestSignDouble(double value)
{
return math.sign(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestCeilingfloat(float value)
{
return math.ceil(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestFloorfloat(float value)
{
return math.floor(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestRoundfloat(float value)
{
return math.round(value);
}
[TestCompiler(DataRange.Standard)]
public static float TestTruncatefloat(float value)
{
return math.trunc(value);
}
private readonly static float3 a = new float3(1, 2, 3);
[TestCompiler]
public static bool TestStaticLoad()
{
var cmp = a == new float3(1, 2, 3);
return cmp.x && cmp.y && cmp.z;
}
[TestCompiler(42L)]
public static long TestLongCountbits(long value)
{
return math.countbits(value) - 1;
}
[TestCompiler(42L)]
public static long TestLongLzcnt(long value)
{
return math.lzcnt(value) - 1;
}
[TestCompiler(42L)]
public static long TestLongTzcnt(long value)
{
return math.tzcnt(value) - 1;
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestUshortAddInt2(int2* o, ushort i)
{
*o = i + new int2(0, 0);
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestInt2AddUshort(int2* o, ushort i)
{
*o = new int2(0, 0) + i;
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestUshortSubInt2(int2* o, ushort i)
{
*o = i - new int2(0, 0);
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestInt2SubUshort(int2* o, ushort i)
{
*o = new int2(0, 0) - i;
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestUshortMulInt2(int2* o, ushort i)
{
*o = i * new int2(0, 0);
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestInt2MulUshort(int2* o, ushort i)
{
*o = new int2(0, 0) * i;
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestUshortDivInt2(int2* o, ushort i)
{
*o = i / new int2(1, 1);
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestInt2DivUshort(int2* o, ushort i)
{
*o = new int2(0, 0) / i;
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestUshortModInt2(int2* o, ushort i)
{
*o = i % new int2(1, 1);
}
[TestCompiler(typeof(ReturnBox), (ushort)42)]
public static unsafe void TestInt2ModUshort(int2* o, ushort i)
{
*o = new int2(0, 0) % i;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortEqInt2(ushort i)
{
return math.all(i == new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2EqUshort(ushort i)
{
return math.all(new int2(0, 0) == i) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortNeInt2(ushort i)
{
return math.all(i != new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2NeUshort(ushort i)
{
return math.all(new int2(0, 0) != i) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortGeInt2(ushort i)
{
return math.all(i >= new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2GeUshort(ushort i)
{
return math.all(new int2(0, 0) >= i) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortGtInt2(ushort i)
{
return math.all(i > new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2GtUshort(ushort i)
{
return math.all(new int2(0, 0) > i) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortLtInt2(ushort i)
{
return math.all(i < new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2LtUshort(ushort i)
{
return math.all(new int2(0, 0) < i) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestUshortLeInt2(ushort i)
{
return math.all(i <= new int2(0, 0)) ? 1 : 0;
}
[TestCompiler((ushort)42)]
public static unsafe int TestInt2LeUshort(ushort i)
{
return math.all(new int2(0, 0) <= i) ? 1 : 0;
}
[TestCompiler(42.0f)]
public static float TestSqrtAndAcosIsDefinedBehaviour(float a)
{
// This sqrt call will get folded away, but we need it here because it exhibits the bug.
if (math.sqrt(4) == 2)
{
return math.acos(a);
}
else
{
return 42;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d355504e32f39b89ef31f85cad154a5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,184 @@
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst;
using Unity.Burst.CompilerServices;
#if BURST_TESTS_ONLY
namespace Unity.Collections.LowLevel.Unsafe
{
internal class DisposeSentinel
{
}
}
#endif
namespace Burst.Compiler.IL.Tests
{
/// <summary>
/// Tests related to usage of partial managed objects (e.g loading null or storing null
/// reference to a struct, typically used by NativeArray DisposeSentinel)
/// </summary>
internal class PartialManaged
{
#if BURST_TESTS_ONLY || ENABLE_UNITY_COLLECTIONS_CHECKS
[TestCompiler]
public static int TestWriteNullReference()
{
var element = new Element();
WriteNullReference(out element.Reference);
return element.Value;
}
[BurstDiscard]
private static void WriteNullReference(out DisposeSentinel reference)
{
reference = null;
}
private struct Element
{
#pragma warning disable 0649
public int Value;
public DisposeSentinel Reference;
#pragma warning restore 0649
}
#endif
[TestCompiler]
public static void AssignNullToLocalVariableClass()
{
MyClass x = null;
#pragma warning disable 0219
MyClass value = x;
#pragma warning restore 0219
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_CallingManagedMethodNotSupported)]
public static int GetIndexOfCharFomString()
{
return "abc".IndexOf('b');
}
struct StructWithManaged
{
#pragma warning disable 0649
public MyClass myClassValue;
public string stringValue;
public object objectValue;
public float[] arrayValue;
public int value;
#pragma warning restore 0649
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotSupported)]
public static int AccessClassFromStruct()
{
var val = new StructWithManaged();
val.myClassValue.value = val.value;
return val.myClassValue.value;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotSupported)]
public static void AccessStringFromStruct()
{
var val = new StructWithManaged();
#pragma warning disable 0219
var p = val.stringValue = "abc";
#pragma warning restore 0219
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotSupported)]
public static void AccessObjectFromStruct()
{
var val = new StructWithManaged();
#pragma warning disable 0219
var p = val.objectValue;
p = new object();
#pragma warning restore 0219
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotSupported)]
public static void AccessArrayFromStruct()
{
var val = new StructWithManaged();
var p = val.arrayValue;
p[0] = val.value;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotSupported)]
public static int GetValueFromStructWithClassField()
{
var val = new StructWithManaged();
val.value = 5;
return val.value;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_InstructionNewobjWithManagedTypeNotSupported)]
public static void NewMyClass()
{
#pragma warning disable 0219
var value = new MyClass();
#pragma warning restore 0219
}
private class MyClass
{
public int value;
}
private class SomeClassWithMixedStatics
{
public static int SomeInt = 42;
public static readonly SharedStatic<int> SomeSharedStatic = SharedStatic<int>.GetOrCreate<int>();
[BurstDiscard]
private static void DoSomethingWithStaticInt(ref int x) => x = SomeInt;
[IgnoreWarning(1371)]
public static int DoSomething()
{
ref var data = ref SomeSharedStatic.Data;
DoSomethingWithStaticInt(ref data);
return SomeSharedStatic.Data;
}
}
[TestCompiler(OverrideManagedResult = 0)]
public static int DoSomethingThatUsesMixedStatics()
{
return SomeClassWithMixedStatics.DoSomething();
}
private class SomeClassWithMixedStaticsWithExplicitStaticConstructor
{
public static int SomeInt = 42;
public static readonly SharedStatic<int> SomeSharedStatic = SharedStatic<int>.GetOrCreate<int>();
static SomeClassWithMixedStaticsWithExplicitStaticConstructor()
{
SomeInt = 1;
}
[BurstDiscard]
private static void DoSomethingWithStaticInt(ref int x) => x = SomeInt;
[IgnoreWarning(1371)]
public static int DoSomething()
{
ref var data = ref SomeSharedStatic.Data;
DoSomethingWithStaticInt(ref data);
return SomeSharedStatic.Data;
}
}
[TestCompiler(OverrideManagedResult = 0)]
public static int DoSomethingThatUsesMixedStaticsWithExplicitStaticConstructor()
{
return SomeClassWithMixedStaticsWithExplicitStaticConstructor.DoSomething();
}
}
}

Some files were not shown because too many files have changed in this diff Show more