Library
parent
ae6ab1add8
commit
05812d7c42
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,840 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal enum DebugDataKind
|
||||
{
|
||||
LineOnly,
|
||||
Full
|
||||
}
|
||||
|
||||
internal enum AvailX86Targets
|
||||
{
|
||||
SSE2 = (int)BurstTargetCpu.X86_SSE2,
|
||||
SSE4 = (int)BurstTargetCpu.X86_SSE4,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum BitsetX86Targets
|
||||
{
|
||||
SSE2 = 1 << AvailX86Targets.SSE2,
|
||||
SSE4 = 1 << AvailX86Targets.SSE4,
|
||||
}
|
||||
|
||||
internal enum AvailX64Targets
|
||||
{
|
||||
SSE2 = (int)BurstTargetCpu.X64_SSE2,
|
||||
SSE4 = (int)BurstTargetCpu.X64_SSE4,
|
||||
AVX = (int)BurstTargetCpu.AVX,
|
||||
AVX2 = (int)BurstTargetCpu.AVX2,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum BitsetX64Targets
|
||||
{
|
||||
SSE2 = 1 << AvailX64Targets.SSE2,
|
||||
SSE4 = 1 << AvailX64Targets.SSE4,
|
||||
AVX = 1 << AvailX64Targets.AVX,
|
||||
AVX2 = 1 << AvailX64Targets.AVX2,
|
||||
}
|
||||
|
||||
internal enum AvailArm64Targets
|
||||
{
|
||||
ARMV8A = BurstTargetCpu.ARMV8A_AARCH64,
|
||||
ARMV8A_HALFFP = BurstTargetCpu.ARMV8A_AARCH64_HALFFP,
|
||||
ARMV9A = BurstTargetCpu.ARMV9A,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum BitsetArm64Targets
|
||||
{
|
||||
ARMV8A = 1 << AvailArm64Targets.ARMV8A,
|
||||
ARMV8A_HALFFP = 1 << AvailArm64Targets.ARMV8A_HALFFP,
|
||||
ARMV9A = 1 << AvailArm64Targets.ARMV9A,
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class BurstMetadataSettingAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class BurstCommonSettingAttribute : Attribute {}
|
||||
|
||||
class BurstPlatformLegacySettings : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
internal bool DisableOptimisations;
|
||||
[SerializeField]
|
||||
internal bool DisableSafetyChecks;
|
||||
[SerializeField]
|
||||
internal bool DisableBurstCompilation;
|
||||
|
||||
BurstPlatformLegacySettings(BuildTarget target)
|
||||
{
|
||||
DisableSafetyChecks = true;
|
||||
DisableBurstCompilation = false;
|
||||
DisableOptimisations = false;
|
||||
}
|
||||
}
|
||||
|
||||
// To add a setting,
|
||||
// Add a
|
||||
// [SerializeField] internal type settingname;
|
||||
// Add a
|
||||
// internal static readonly string settingname_DisplayName = "Name of option to be displayed in the editor (and searched for)";
|
||||
// Add a
|
||||
// internal static readonly string settingname_ToolTip = "tool tip information to display when hovering mouse
|
||||
// If the setting should be restricted to e.g. Standalone platform :
|
||||
//
|
||||
// Add a
|
||||
// internal static bool settingname_Display(BuildTarget selectedTarget, string architecture) {}
|
||||
//
|
||||
// Add a
|
||||
// internal static bool settingname_Serialise(BuildTarget selectedTarget) {}
|
||||
class BurstPlatformAotSettings : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
[BurstMetadataSetting] // We always need version to be in our saved settings!
|
||||
internal int Version;
|
||||
[SerializeField]
|
||||
internal bool EnableBurstCompilation;
|
||||
[SerializeField]
|
||||
internal bool EnableOptimisations;
|
||||
[SerializeField]
|
||||
internal bool EnableSafetyChecks;
|
||||
[SerializeField]
|
||||
internal bool EnableDebugInAllBuilds;
|
||||
[SerializeField]
|
||||
internal DebugDataKind DebugDataKind;
|
||||
[SerializeField]
|
||||
internal bool UsePlatformSDKLinker;
|
||||
[SerializeField]
|
||||
internal bool EnableArmv9SecurityFeatures;
|
||||
[SerializeField]
|
||||
internal AvailX86Targets CpuMinTargetX32;
|
||||
[SerializeField]
|
||||
internal AvailX86Targets CpuMaxTargetX32;
|
||||
[SerializeField]
|
||||
internal AvailX64Targets CpuMinTargetX64;
|
||||
[SerializeField]
|
||||
internal AvailX64Targets CpuMaxTargetX64;
|
||||
[SerializeField]
|
||||
internal BitsetX86Targets CpuTargetsX32;
|
||||
[SerializeField]
|
||||
internal BitsetX64Targets CpuTargetsX64;
|
||||
[SerializeField]
|
||||
internal BitsetArm64Targets CpuTargetsArm64;
|
||||
[SerializeField]
|
||||
internal OptimizeFor OptimizeFor;
|
||||
[SerializeField]
|
||||
[BurstCommonSetting]
|
||||
internal string DisabledWarnings;
|
||||
|
||||
internal static readonly string EnableDebugInAllBuilds_DisplayName = "Force Debug Information";
|
||||
internal static readonly string EnableDebugInAllBuilds_ToolTip = "Generates debug information for the Burst-compiled code, irrespective of if Development Mode is ticked. This can be used to generate symbols for release builds for platforms that need it.";
|
||||
|
||||
internal static readonly string DebugDataKind_DisplayName = "Debug Information Kind";
|
||||
internal static readonly string DebugDataKind_ToolTip = "Choose which kind of debug information you want present in builds with debug information enabled.";
|
||||
|
||||
internal static readonly string EnableOptimisations_DisplayName = "Enable Optimizations";
|
||||
internal static readonly string EnableOptimisations_ToolTip = "Enables all optimizations for the currently selected platform.";
|
||||
|
||||
internal static readonly string EnableBurstCompilation_DisplayName = "Enable Burst Compilation";
|
||||
internal static readonly string EnableBurstCompilation_ToolTip = "Enables burst compilation for the selected platform.";
|
||||
|
||||
internal static readonly string OptimizeFor_DisplayName = "Optimize For";
|
||||
internal static readonly string OptimizeFor_ToolTip = "Choose what optimization setting to compile Burst code for.";
|
||||
|
||||
internal static readonly string DisabledWarnings_DisplayName = "Disabled Warnings*";
|
||||
internal static readonly string DisabledWarnings_ToolTip = "Burst warnings to disable (separated by ;).";
|
||||
|
||||
internal static readonly string UsePlatformSDKLinker_DisplayName = "Use Platform SDK Linker";
|
||||
internal static readonly string UsePlatformSDKLinker_ToolTip = "Enabling this option will disable cross compilation support for desktops, and will require platform specific tools for Windows/Linux/Mac - use only if you encounter problems with the burst builtin solution.";
|
||||
|
||||
// We do not support this option anymore, so the easiest thing is to just not display it.
|
||||
internal static bool UsePlatformSDKLinker_Display(BuildTarget selectedTarget, string architecture) => false;
|
||||
internal static bool UsePlatformSDKLinker_Serialise(BuildTarget selectedTarget) => false;
|
||||
|
||||
internal static readonly string CpuTargetsX32_DisplayName = "Target 32Bit CPU Architectures";
|
||||
internal static readonly string CpuTargetsX32_ToolTip = "Use this to specify the set of target architectures to support for the currently selected platform.";
|
||||
internal static bool CpuTargetsX32_Display(BuildTarget selectedTarget, string architecture)
|
||||
{
|
||||
return (IsStandalone(selectedTarget) || selectedTarget == BuildTarget.WSAPlayer) && Has32BitSupport(selectedTarget);
|
||||
}
|
||||
internal static bool CpuTargetsX32_Serialise(BuildTarget selectedTarget)
|
||||
{
|
||||
return (IsStandalone(selectedTarget) || selectedTarget == BuildTarget.WSAPlayer) && Has32BitSupportForSerialise(selectedTarget);
|
||||
}
|
||||
|
||||
internal static readonly string CpuTargetsX64_DisplayName = "Target 64Bit CPU Architectures";
|
||||
internal static readonly string CpuTargetsX64_ToolTip = "Use this to specify the target architectures to support for the currently selected platform.";
|
||||
internal static bool CpuTargetsX64_Display(BuildTarget selectedTarget, string architecture)
|
||||
{
|
||||
return (IsStandalone(selectedTarget) || selectedTarget == BuildTarget.WSAPlayer)
|
||||
&& Has64BitSupport(selectedTarget)
|
||||
&& (selectedTarget != BuildTarget.StandaloneOSX || architecture != "arm64");
|
||||
}
|
||||
internal static bool CpuTargetsX64_Serialise(BuildTarget selectedTarget)
|
||||
{
|
||||
return IsStandalone(selectedTarget) || selectedTarget == BuildTarget.WSAPlayer;
|
||||
}
|
||||
|
||||
internal static readonly string CpuTargetsArm64_DisplayName = "Target Arm 64Bit CPU Architectures";
|
||||
internal static readonly string CpuTargetsArm64_ToolTip = "Use this to specify the target architectures to support for the currently selected platform.";
|
||||
internal static bool CpuTargetsArm64_Display(BuildTarget selectedTarget, string architecture)
|
||||
{
|
||||
return selectedTarget == BuildTarget.Android;
|
||||
}
|
||||
internal static bool CpuTargetsArm64_Serialise(BuildTarget selectedTarget)
|
||||
{
|
||||
return selectedTarget == BuildTarget.Android;
|
||||
}
|
||||
|
||||
internal static bool IsStandalone(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.StandaloneLinux64:
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
case BuildTarget.StandaloneOSX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BurstPlatformAotSettings(BuildTarget target)
|
||||
{
|
||||
InitialiseDefaults();
|
||||
}
|
||||
|
||||
private const int DefaultVersion = 4;
|
||||
|
||||
internal void InitialiseDefaults()
|
||||
{
|
||||
Version = DefaultVersion;
|
||||
EnableSafetyChecks = false;
|
||||
EnableBurstCompilation = true;
|
||||
EnableOptimisations = true;
|
||||
EnableDebugInAllBuilds = false;
|
||||
DebugDataKind = DebugDataKind.Full;
|
||||
UsePlatformSDKLinker = false; // Only applicable for desktop targets (Windows/Mac/Linux)
|
||||
CpuMinTargetX32 = 0;
|
||||
CpuMaxTargetX32 = 0;
|
||||
CpuMinTargetX64 = 0;
|
||||
CpuMaxTargetX64 = 0;
|
||||
CpuTargetsX32 = BitsetX86Targets.SSE2 | BitsetX86Targets.SSE4;
|
||||
CpuTargetsX64 = BitsetX64Targets.SSE2 | BitsetX64Targets.AVX2;
|
||||
CpuTargetsArm64 = BitsetArm64Targets.ARMV8A;
|
||||
DisabledWarnings = "";
|
||||
OptimizeFor = OptimizeFor.Default;
|
||||
}
|
||||
|
||||
internal static string GetPath(BuildTarget? target)
|
||||
{
|
||||
if (target.HasValue)
|
||||
{
|
||||
return "ProjectSettings/BurstAotSettings_" + target.ToString() + ".json";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "ProjectSettings/CommonBurstAotSettings.json";
|
||||
}
|
||||
}
|
||||
|
||||
internal static BuildTarget? ResolveTarget(BuildTarget? target)
|
||||
{
|
||||
if (!target.HasValue)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
// Treat the 32/64 platforms the same from the point of view of burst settings
|
||||
// since there is no real way to distinguish from the platforms selector
|
||||
if (target == BuildTarget.StandaloneWindows64 || target == BuildTarget.StandaloneWindows)
|
||||
return BuildTarget.StandaloneWindows;
|
||||
|
||||
// 32 bit linux support was deprecated
|
||||
if (target == BuildTarget.StandaloneLinux64)
|
||||
return BuildTarget.StandaloneLinux64;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
internal static readonly string BurstMiscPathPostFix = "_BurstDebugInformation_DoNotShip";
|
||||
internal static string FetchOutputPath(BuildSummary summary)
|
||||
{
|
||||
var finalOutputPath = summary.outputPath;
|
||||
var directoryName = Path.GetDirectoryName(finalOutputPath);
|
||||
var appName = Path.GetFileNameWithoutExtension(finalOutputPath);
|
||||
|
||||
return $"{Path.Combine(directoryName, appName)}{BurstMiscPathPostFix}";
|
||||
}
|
||||
|
||||
internal static BurstPlatformAotSettings GetOrCreateSettings(BuildTarget? target)
|
||||
{
|
||||
target = ResolveTarget(target);
|
||||
var settings = CreateInstance<BurstPlatformAotSettings>();
|
||||
settings.InitialiseDefaults();
|
||||
string path = GetPath(target);
|
||||
|
||||
var fileExists = File.Exists(path);
|
||||
var upgraded = false;
|
||||
if (fileExists)
|
||||
{
|
||||
var json = File.ReadAllText(path);
|
||||
settings = SerialiseIn(target, json, out upgraded);
|
||||
}
|
||||
|
||||
if (!fileExists || upgraded)
|
||||
{
|
||||
// If the settings file didn't previously exist,
|
||||
// or it did exist but we've just upgraded it to a new version,
|
||||
// save it to disk now.
|
||||
settings.Save(target);
|
||||
}
|
||||
|
||||
// Overwrite the settings with any that are common and shared between all settings.
|
||||
if (target.HasValue)
|
||||
{
|
||||
var commonSettings = GetOrCreateSettings(null);
|
||||
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
foreach (var field in platformFields)
|
||||
{
|
||||
if (null != field.GetCustomAttribute<BurstCommonSettingAttribute>())
|
||||
{
|
||||
field.SetValue(settings, field.GetValue(commonSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
delegate bool SerialiseItem(BuildTarget selectedPlatform);
|
||||
|
||||
private static BurstPlatformAotSettings SerialiseIn(BuildTarget? target, string json, out bool upgraded)
|
||||
{
|
||||
var versioned = ScriptableObject.CreateInstance<BurstPlatformAotSettings>();
|
||||
EditorJsonUtility.FromJsonOverwrite(json, versioned);
|
||||
|
||||
upgraded = false;
|
||||
|
||||
if (versioned.Version == 0)
|
||||
{
|
||||
// Deal with pre versioned format
|
||||
var legacy = ScriptableObject.CreateInstance<BurstPlatformLegacySettings>();
|
||||
EditorJsonUtility.FromJsonOverwrite(json, legacy);
|
||||
|
||||
// Legacy file, upgrade it
|
||||
versioned.InitialiseDefaults();
|
||||
versioned.EnableOptimisations = !legacy.DisableOptimisations;
|
||||
versioned.EnableBurstCompilation = !legacy.DisableBurstCompilation;
|
||||
versioned.EnableSafetyChecks = !legacy.DisableSafetyChecks;
|
||||
|
||||
// Destroy the legacy object so Unity doesn't try to backup / restore it later during domain reload.
|
||||
ScriptableObject.DestroyImmediate(legacy);
|
||||
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
if (versioned.Version < 3)
|
||||
{
|
||||
// Upgrade the version first
|
||||
versioned.Version = 3;
|
||||
|
||||
// Upgrade from min..max targets to bitset
|
||||
versioned.CpuTargetsX32 |= (BitsetX86Targets)(1 << (int)versioned.CpuMinTargetX32);
|
||||
versioned.CpuTargetsX32 |= (BitsetX86Targets)(1 << (int)versioned.CpuMaxTargetX32);
|
||||
|
||||
versioned.CpuTargetsX64 |= (BitsetX64Targets)(1 << (int)versioned.CpuMinTargetX64);
|
||||
versioned.CpuTargetsX64 |= (BitsetX64Targets)(1 << (int)versioned.CpuMaxTargetX64);
|
||||
|
||||
// Extra checks to add targets in the min..max range for 64-bit targets.
|
||||
switch (versioned.CpuMinTargetX64)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case AvailX64Targets.SSE2:
|
||||
switch (versioned.CpuMaxTargetX64)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case AvailX64Targets.AVX2:
|
||||
versioned.CpuTargetsX64 |= (BitsetX64Targets)(1 << (int)AvailX64Targets.AVX);
|
||||
goto case AvailX64Targets.AVX;
|
||||
case AvailX64Targets.AVX:
|
||||
versioned.CpuTargetsX64 |= (BitsetX64Targets)(1 << (int)AvailX64Targets.SSE4);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AvailX64Targets.SSE4:
|
||||
switch (versioned.CpuMaxTargetX64)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case AvailX64Targets.AVX2:
|
||||
versioned.CpuTargetsX64 |= (BitsetX64Targets)(1 << (int)AvailX64Targets.AVX);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Wipe the old min/max targets
|
||||
versioned.CpuMinTargetX32 = 0;
|
||||
versioned.CpuMaxTargetX32 = 0;
|
||||
versioned.CpuMinTargetX64 = 0;
|
||||
versioned.CpuMaxTargetX64 = 0;
|
||||
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
if (versioned.Version < 4)
|
||||
{
|
||||
// Upgrade the version first.
|
||||
versioned.Version = 4;
|
||||
|
||||
// When we upgrade we'll set the optimization level to default (which is, as expected, the default).
|
||||
versioned.OptimizeFor = OptimizeFor.Default;
|
||||
|
||||
// This option has been removed as user-setting options, so switch them to false here.
|
||||
versioned.EnableSafetyChecks = false;
|
||||
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
// Otherwise should be a modern file with a valid version (we can use that to upgrade when the time comes)
|
||||
return versioned;
|
||||
}
|
||||
|
||||
private static bool ShouldSerialiseOut(BuildTarget? target, FieldInfo field)
|
||||
{
|
||||
var method = typeof(BurstPlatformAotSettings).GetMethod(field.Name + "_Serialise", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
var shouldSerialise = (SerialiseItem)Delegate.CreateDelegate(typeof(SerialiseItem), method);
|
||||
if (!target.HasValue || !shouldSerialise(target.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we always need to write out the attribute, return now.
|
||||
if (null != field.GetCustomAttribute<BurstMetadataSettingAttribute>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var isCommon = !target.HasValue;
|
||||
var hasCommonAttribute = null != field.GetCustomAttribute<BurstCommonSettingAttribute>();
|
||||
|
||||
if ((isCommon && hasCommonAttribute) || (!isCommon && !hasCommonAttribute))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal string SerialiseOut(BuildTarget? target)
|
||||
{
|
||||
// Version 2 and onwards serialise a custom object in order to avoid serialising all the settings.
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.Append("{\n");
|
||||
s.Append(" \"MonoBehaviour\": {\n");
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
int total = 0;
|
||||
for (int i = 0; i < platformFields.Length; i++)
|
||||
{
|
||||
if (ShouldSerialiseOut(target, platformFields[i]))
|
||||
{
|
||||
total++;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < platformFields.Length; i++)
|
||||
{
|
||||
if (ShouldSerialiseOut(target, platformFields[i]))
|
||||
{
|
||||
s.Append($" \"{platformFields[i].Name}\": ");
|
||||
if (platformFields[i].FieldType.IsEnum)
|
||||
s.Append((int)platformFields[i].GetValue(this));
|
||||
else if (platformFields[i].FieldType == typeof(string))
|
||||
s.Append($"\"{platformFields[i].GetValue(this)}\"");
|
||||
else if (platformFields[i].FieldType == typeof(bool))
|
||||
s.Append(((bool)platformFields[i].GetValue(this)) ? "true" : "false");
|
||||
else
|
||||
s.Append((int)platformFields[i].GetValue(this));
|
||||
|
||||
total--;
|
||||
if (total != 0)
|
||||
s.Append(",");
|
||||
s.Append("\n");
|
||||
}
|
||||
}
|
||||
s.Append(" }\n");
|
||||
s.Append("}\n");
|
||||
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
internal void Save(BuildTarget? target)
|
||||
{
|
||||
if (target.HasValue)
|
||||
{
|
||||
target = ResolveTarget(target);
|
||||
}
|
||||
|
||||
var path = GetPath(target);
|
||||
|
||||
if (!AssetDatabase.IsOpenForEdit(path))
|
||||
{
|
||||
if (!AssetDatabase.MakeEditable(path))
|
||||
{
|
||||
Debug.LogWarning($"Burst could not save AOT settings file {path}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(path, SerialiseOut(target));
|
||||
}
|
||||
|
||||
internal static SerializedObject GetCommonSerializedSettings()
|
||||
{
|
||||
return new SerializedObject(GetOrCreateSettings(null));
|
||||
}
|
||||
|
||||
internal static SerializedObject GetSerializedSettings(BuildTarget target)
|
||||
{
|
||||
return new SerializedObject(GetOrCreateSettings(target));
|
||||
}
|
||||
|
||||
internal static bool Has32BitSupport(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.WSAPlayer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool Has32BitSupportForSerialise(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
case BuildTarget.WSAPlayer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool Has64BitSupport(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
case BuildTarget.WSAPlayer:
|
||||
case BuildTarget.StandaloneOSX:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static BurstTargetCpu GetCpu(int v)
|
||||
{
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
|
||||
var r = ((v > 0xFFFF) ? 1 : 0) << 4; v >>= r;
|
||||
var shift = ((v > 0xFF) ? 1 : 0) << 3; v >>= shift; r |= shift;
|
||||
shift = ((v > 0xF) ? 1 : 0) << 2; v >>= shift; r |= shift;
|
||||
shift = ((v > 0x3) ? 1 : 0) << 1; v >>= shift; r |= shift;
|
||||
r |= (v >> 1);
|
||||
return (BurstTargetCpu)r;
|
||||
}
|
||||
|
||||
private static IEnumerable<Enum> GetFlags(Enum input)
|
||||
{
|
||||
foreach (Enum value in Enum.GetValues(input.GetType()))
|
||||
{
|
||||
if (input.HasFlag(value))
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal TargetCpus GetDesktopCpu32Bit()
|
||||
{
|
||||
var cpus = new TargetCpus();
|
||||
|
||||
foreach (var target in GetFlags(CpuTargetsX32))
|
||||
{
|
||||
cpus.Cpus.Add(GetCpu((int)(BitsetX86Targets)target));
|
||||
}
|
||||
|
||||
// If no targets were specified just default to the oldest CPU supported.
|
||||
if (cpus.Cpus.Count == 0)
|
||||
{
|
||||
cpus.Cpus.Add(BurstTargetCpu.X86_SSE2);
|
||||
}
|
||||
|
||||
return cpus;
|
||||
}
|
||||
|
||||
internal TargetCpus GetDesktopCpu64Bit()
|
||||
{
|
||||
var cpus = new TargetCpus();
|
||||
|
||||
foreach (var target in GetFlags(CpuTargetsX64))
|
||||
{
|
||||
cpus.Cpus.Add(GetCpu((int)(BitsetX64Targets)target));
|
||||
}
|
||||
|
||||
// If no targets were specified just default to the oldest CPU supported.
|
||||
if (cpus.Cpus.Count == 0)
|
||||
{
|
||||
cpus.Cpus.Add(BurstTargetCpu.X64_SSE2);
|
||||
}
|
||||
|
||||
return cpus;
|
||||
}
|
||||
|
||||
internal TargetCpus GetAndroidCpuArm64()
|
||||
{
|
||||
var cpus = new TargetCpus();
|
||||
|
||||
foreach (var target in GetFlags(CpuTargetsArm64))
|
||||
{
|
||||
cpus.Cpus.Add(GetCpu((int)(BitsetArm64Targets)target));
|
||||
}
|
||||
|
||||
// If no targets were specified just default to the oldest CPU supported.
|
||||
if (cpus.Cpus.Count == 0)
|
||||
{
|
||||
cpus.Cpus.Add(BurstTargetCpu.ARMV8A_AARCH64);
|
||||
}
|
||||
|
||||
return cpus;
|
||||
}
|
||||
}
|
||||
|
||||
static class BurstAotSettingsIMGUIRegister
|
||||
{
|
||||
class BurstAotSettingsProvider : SettingsProvider
|
||||
{
|
||||
SerializedObject[] m_PlatformSettings;
|
||||
SerializedProperty[][] m_PlatformProperties;
|
||||
DisplayItem[][] m_PlatformVisibility;
|
||||
GUIContent[][] m_PlatformToolTips;
|
||||
BuildPlatform[] m_ValidPlatforms;
|
||||
SerializedObject m_CommonPlatformSettings;
|
||||
|
||||
delegate bool DisplayItem(BuildTarget selectedTarget, string architecture);
|
||||
|
||||
static bool DefaultShow(BuildTarget selectedTarget, string architecture)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
static bool DefaultHide(BuildTarget selectedTarget, string architecture)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public BurstAotSettingsProvider()
|
||||
: base("Project/Burst AOT Settings", SettingsScope.Project, null)
|
||||
{
|
||||
int a;
|
||||
|
||||
m_ValidPlatforms = BuildPlatforms.instance.GetValidPlatforms(true).ToArray();
|
||||
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
int numPlatformFields = platformFields.Length;
|
||||
int numKeywords = numPlatformFields;
|
||||
var tempKeywords = new string[numKeywords];
|
||||
|
||||
for (a = 0; a < numPlatformFields; a++)
|
||||
{
|
||||
tempKeywords[a] = typeof(BurstPlatformAotSettings).GetField(platformFields[a].Name + "_ToolTip", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
|
||||
}
|
||||
|
||||
keywords = new HashSet<string>(tempKeywords);
|
||||
|
||||
m_PlatformSettings = new SerializedObject[m_ValidPlatforms.Length];
|
||||
m_PlatformProperties = new SerializedProperty[m_ValidPlatforms.Length][];
|
||||
m_PlatformVisibility = new DisplayItem[m_ValidPlatforms.Length][];
|
||||
m_PlatformToolTips = new GUIContent[m_ValidPlatforms.Length][];
|
||||
|
||||
m_CommonPlatformSettings = null;
|
||||
}
|
||||
|
||||
public override void OnActivate(string searchContext, VisualElement rootElement)
|
||||
{
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
for (int p = 0; p < m_ValidPlatforms.Length; p++)
|
||||
{
|
||||
InitialiseSettingsForCommon(platformFields);
|
||||
InitialiseSettingsForPlatform(p, platformFields);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitialiseSettingsForCommon(FieldInfo[] commonFields)
|
||||
{
|
||||
m_CommonPlatformSettings = BurstPlatformAotSettings.GetCommonSerializedSettings();
|
||||
}
|
||||
|
||||
private void InitialiseSettingsForPlatform(int platform, FieldInfo[] platformFields)
|
||||
{
|
||||
if (m_ValidPlatforms[platform].targetGroup == BuildTargetGroup.Standalone)
|
||||
m_PlatformSettings[platform] = BurstPlatformAotSettings.GetSerializedSettings(EditorUserBuildSettings.selectedStandaloneTarget);
|
||||
else
|
||||
m_PlatformSettings[platform] = BurstPlatformAotSettings.GetSerializedSettings(m_ValidPlatforms[platform].defaultTarget);
|
||||
|
||||
m_PlatformProperties[platform] = new SerializedProperty[platformFields.Length];
|
||||
m_PlatformToolTips[platform] = new GUIContent[platformFields.Length];
|
||||
m_PlatformVisibility[platform] = new DisplayItem[platformFields.Length];
|
||||
for (int i = 0; i < platformFields.Length; i++)
|
||||
{
|
||||
m_PlatformProperties[platform][i] = m_PlatformSettings[platform].FindProperty(platformFields[i].Name);
|
||||
var displayName = typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_DisplayName", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
|
||||
var toolTip = typeof(BurstPlatformAotSettings).GetField(platformFields[i].Name + "_ToolTip", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as string;
|
||||
m_PlatformToolTips[platform][i] = EditorGUIUtility.TrTextContent(displayName, toolTip);
|
||||
|
||||
var method = typeof(BurstPlatformAotSettings).GetMethod(platformFields[i].Name + "_Display", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (method == null)
|
||||
{
|
||||
if (displayName == null)
|
||||
{
|
||||
m_PlatformVisibility[platform][i] = DefaultHide;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlatformVisibility[platform][i] = DefaultShow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlatformVisibility[platform][i] = (DisplayItem)Delegate.CreateDelegate(typeof(DisplayItem), method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string FetchStandaloneTargetName()
|
||||
{
|
||||
switch (EditorUserBuildSettings.selectedStandaloneTarget)
|
||||
{
|
||||
case BuildTarget.StandaloneOSX:
|
||||
return "Mac OS X"; // Matches the Build Settings Dialog names
|
||||
case BuildTarget.StandaloneWindows:
|
||||
case BuildTarget.StandaloneWindows64:
|
||||
return "Windows";
|
||||
default:
|
||||
return "Linux";
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(string searchContext)
|
||||
{
|
||||
var rect = EditorGUILayout.BeginVertical();
|
||||
|
||||
EditorGUIUtility.labelWidth = rect.width / 2;
|
||||
|
||||
int selectedPlatform = EditorGUILayout.BeginPlatformGrouping(m_ValidPlatforms, null);
|
||||
|
||||
// During a build and other cases, the settings object can become invalid, if it does, we re-build it for the current platform
|
||||
// this fixes the settings failing to save if modified after a build has finished, and the settings were still open
|
||||
if (!m_PlatformSettings[selectedPlatform].isValid)
|
||||
{
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
InitialiseSettingsForCommon(platformFields);
|
||||
|
||||
// If the selected platform is invalid, it means all of them will be. So we do a pass to reinitialize all now.
|
||||
for (var platform = 0; platform < m_PlatformSettings.Length; platform++)
|
||||
{
|
||||
InitialiseSettingsForPlatform(platform, platformFields);
|
||||
}
|
||||
}
|
||||
|
||||
var selectedTarget = m_ValidPlatforms[selectedPlatform].defaultTarget;
|
||||
if (m_ValidPlatforms[selectedPlatform].targetGroup == BuildTargetGroup.Standalone)
|
||||
selectedTarget = EditorUserBuildSettings.selectedStandaloneTarget;
|
||||
|
||||
var buildTargetName = BuildPipeline.GetBuildTargetName(selectedTarget);
|
||||
var architecture = EditorUserBuildSettings.GetPlatformSettings(buildTargetName, "Architecture").ToLowerInvariant();
|
||||
|
||||
if (m_ValidPlatforms[selectedPlatform].targetGroup == BuildTargetGroup.Standalone)
|
||||
{
|
||||
// Note burst treats Windows and Windows32 as the same target from a settings point of view (same for linux)
|
||||
// So we only display the standalone platform
|
||||
EditorGUILayout.LabelField(EditorGUIUtility.TrTextContent("Target Platform", "Shows the currently selected standalone build target, can be switched in the Build Settings dialog"), EditorGUIUtility.TrTextContent(FetchStandaloneTargetName()));
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_PlatformProperties[selectedPlatform].Length; i++)
|
||||
{
|
||||
if (m_PlatformVisibility[selectedPlatform][i](selectedTarget, architecture))
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_PlatformProperties[selectedPlatform][i], m_PlatformToolTips[selectedPlatform][i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ValidPlatforms[selectedPlatform].targetGroup == BuildTargetGroup.Android)
|
||||
EditorGUILayout.HelpBox("Armv9A (SVE2) target CPU architecture is experimental", MessageType.Warning);
|
||||
|
||||
EditorGUILayout.EndPlatformGrouping();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.LabelField("* Shared setting common across all platforms");
|
||||
|
||||
if (m_PlatformSettings[selectedPlatform].hasModifiedProperties)
|
||||
{
|
||||
m_PlatformSettings[selectedPlatform].ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
var commonAotSettings = ((BurstPlatformAotSettings)m_CommonPlatformSettings.targetObject);
|
||||
var platformAotSettings = ((BurstPlatformAotSettings)m_PlatformSettings[selectedPlatform].targetObject);
|
||||
|
||||
var platformFields = typeof(BurstPlatformAotSettings).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
foreach (var field in platformFields)
|
||||
{
|
||||
if (null != field.GetCustomAttribute<BurstCommonSettingAttribute>())
|
||||
{
|
||||
field.SetValue(commonAotSettings, field.GetValue(platformAotSettings));
|
||||
|
||||
foreach (var platformSetting in m_PlatformSettings)
|
||||
{
|
||||
field.SetValue(platformSetting.targetObject, field.GetValue(commonAotSettings));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commonAotSettings.Save(null);
|
||||
platformAotSettings.Save(selectedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateBurstAotSettingsProvider()
|
||||
{
|
||||
return new BurstAotSettingsProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,168 @@
|
|||
#if UNITY_EDITOR || BURST_INTERNAL
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal partial class BurstDisassembler
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="AsmTokenKind"/> provider for LLVM IR - intrinsics are not covered at this time
|
||||
/// </summary>
|
||||
private class LLVMIRAsmTokenKindProvider : AsmTokenKindProvider
|
||||
{
|
||||
|
||||
private static readonly string[] Qualifiers = new[]
|
||||
{
|
||||
"to",
|
||||
"new",
|
||||
|
||||
"float",
|
||||
"double",
|
||||
"i1",
|
||||
"i32",
|
||||
"i16",
|
||||
"i64",
|
||||
|
||||
"eq",
|
||||
"ne",
|
||||
"ugt",
|
||||
"uge",
|
||||
"ult",
|
||||
"ule",
|
||||
"sgt",
|
||||
"sge",
|
||||
"slt",
|
||||
"sle",
|
||||
|
||||
"false",
|
||||
"true",
|
||||
|
||||
"oeq",
|
||||
"ogt",
|
||||
"oge",
|
||||
"olt",
|
||||
"ole",
|
||||
"one",
|
||||
"ord",
|
||||
"ueq",
|
||||
"une",
|
||||
"uno",
|
||||
};
|
||||
|
||||
private static readonly string[] Instructions = new[]
|
||||
{
|
||||
"ret",
|
||||
"br",
|
||||
"switch",
|
||||
"indirectbr",
|
||||
"invoke",
|
||||
"callbr",
|
||||
"resume",
|
||||
"catchswitch",
|
||||
"catchret",
|
||||
"cleanupret",
|
||||
"unreachable",
|
||||
|
||||
"add",
|
||||
"sub",
|
||||
"mul",
|
||||
"udiv",
|
||||
"sdiv",
|
||||
"urem",
|
||||
"srem",
|
||||
|
||||
"shl",
|
||||
"lshr",
|
||||
"ashr",
|
||||
"and",
|
||||
"or",
|
||||
"xor",
|
||||
|
||||
"extractvalue",
|
||||
"insertvalue",
|
||||
|
||||
"alloca",
|
||||
"load",
|
||||
"store",
|
||||
"fence",
|
||||
"cmpxchg",
|
||||
"atomicrmw",
|
||||
"getelementptr",
|
||||
|
||||
"trunc",
|
||||
"zext",
|
||||
"sext",
|
||||
"ptrtoint",
|
||||
"inttoptr",
|
||||
"bitcast",
|
||||
"addrspacecast",
|
||||
|
||||
"icmp",
|
||||
"phi",
|
||||
"select",
|
||||
"freeze",
|
||||
"call",
|
||||
"va_arg",
|
||||
"landingpad",
|
||||
"catchpad",
|
||||
"cleanuppad",
|
||||
};
|
||||
|
||||
private static readonly string[] FpuInstructions = new[]
|
||||
{
|
||||
"fneg",
|
||||
|
||||
"fadd",
|
||||
"fsub",
|
||||
"fmul",
|
||||
"fdiv",
|
||||
"frem",
|
||||
|
||||
"fptrunc",
|
||||
"fpext",
|
||||
"fptoui",
|
||||
"fptosi",
|
||||
"uitofp",
|
||||
"sitofp",
|
||||
|
||||
"fcmp",
|
||||
};
|
||||
|
||||
private static readonly string[] SimdInstructions = new[]
|
||||
{
|
||||
"extractelement",
|
||||
"insertelement",
|
||||
"shufflevector",
|
||||
};
|
||||
|
||||
private LLVMIRAsmTokenKindProvider() : base(Qualifiers.Length + Instructions.Length + FpuInstructions.Length + SimdInstructions.Length)
|
||||
{
|
||||
foreach (var instruction in Qualifiers)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.Qualifier);
|
||||
}
|
||||
|
||||
foreach (var instruction in Instructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.Instruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in FpuInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.Instruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in SimdInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.InstructionSIMD);
|
||||
}
|
||||
}
|
||||
|
||||
public override SIMDkind SimdKind(StringSlice instruction)
|
||||
{
|
||||
throw new System.NotImplementedException("Syntax Highlighting is not implemented for LLVM IR.");
|
||||
}
|
||||
|
||||
public static readonly LLVMIRAsmTokenKindProvider Instance = new LLVMIRAsmTokenKindProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal partial class BurstDisassembler
|
||||
{
|
||||
internal class LLVMIRInstructionInfo
|
||||
{
|
||||
internal static bool GetLLVMIRInfo(string instructionName, out string instructionInfo)
|
||||
{
|
||||
var returnValue = true;
|
||||
|
||||
switch (instructionName)
|
||||
{
|
||||
default:
|
||||
instructionInfo = string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
using System;
|
||||
|
||||
#if UNITY_EDITOR || BURST_INTERNAL
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal partial class BurstDisassembler
|
||||
{
|
||||
internal class WasmAsmTokenKindProvider : AsmTokenKindProvider
|
||||
{
|
||||
private static readonly string[] Registers = new[]
|
||||
{
|
||||
"memory.", // add . to avoid parsing instruction portion as directive
|
||||
"local.",
|
||||
"global.",
|
||||
"i32.",
|
||||
"i64.",
|
||||
"f32.",
|
||||
"f64."
|
||||
};
|
||||
|
||||
private static readonly string[] Qualifiers = new[]
|
||||
{
|
||||
"offset",
|
||||
"align",
|
||||
|
||||
"eqz",
|
||||
"eq",
|
||||
"ne",
|
||||
"lt_s",
|
||||
"lt_u",
|
||||
"gt_s",
|
||||
"gt_u",
|
||||
"le_s",
|
||||
"le_u",
|
||||
"ge_s",
|
||||
"ge_u",
|
||||
"lt",
|
||||
"gt",
|
||||
"le",
|
||||
"ge",
|
||||
};
|
||||
|
||||
private static readonly string[] Instructions = new[]
|
||||
{
|
||||
"if",
|
||||
"end",
|
||||
"block",
|
||||
"end_block",
|
||||
"end_loop",
|
||||
"end_function",
|
||||
"loop",
|
||||
"unreachable",
|
||||
"nop",
|
||||
"call",
|
||||
"call_indirect",
|
||||
|
||||
"drop",
|
||||
"select",
|
||||
"get",
|
||||
"set",
|
||||
"tee",
|
||||
|
||||
"load",
|
||||
"load8_s",
|
||||
"load8_u",
|
||||
"load16_s",
|
||||
"load16_u",
|
||||
"load32_s",
|
||||
"load32_u",
|
||||
"store",
|
||||
"store8",
|
||||
"store16",
|
||||
"store32",
|
||||
"size",
|
||||
"grow",
|
||||
|
||||
"const",
|
||||
"clz",
|
||||
"ctz",
|
||||
"popcnt",
|
||||
"add",
|
||||
"sub",
|
||||
"mul",
|
||||
"div_s",
|
||||
"div_u",
|
||||
"rem_s",
|
||||
"rem_u",
|
||||
"and",
|
||||
"or",
|
||||
"xor",
|
||||
"shl",
|
||||
"shr_s",
|
||||
"shr_u",
|
||||
"rotl",
|
||||
"rotr",
|
||||
"abs",
|
||||
"neg",
|
||||
"ceil",
|
||||
"floor",
|
||||
"trunc",
|
||||
"sqrt",
|
||||
"div",
|
||||
"min",
|
||||
"max",
|
||||
"copysign",
|
||||
|
||||
"wrap_i64",
|
||||
"trunc_f32_s",
|
||||
"trunc_f32_u",
|
||||
"trunc_f64_s",
|
||||
"trunc_f64_u",
|
||||
"extend_i32_s",
|
||||
"extend_i32_u",
|
||||
"convert_i32_s",
|
||||
"convert_i32_u",
|
||||
"convert_i64_s",
|
||||
"convert_i64_u",
|
||||
"demote_f64",
|
||||
"promote_f32",
|
||||
"reinterpret_f32",
|
||||
"reinterpret_f64",
|
||||
"reinterpret_i32",
|
||||
"reinterpret_i64",
|
||||
};
|
||||
|
||||
private static readonly string[] BranchInstructions = new string[]
|
||||
{
|
||||
"br_if",
|
||||
};
|
||||
|
||||
private static readonly string[] JumpInstructions = new string[]
|
||||
{
|
||||
"br",
|
||||
"br_table"
|
||||
};
|
||||
|
||||
private static readonly string[] ReturnInstructions = new string[]
|
||||
{
|
||||
"return",
|
||||
};
|
||||
|
||||
private static readonly string[] SimdInstructions = new string[]
|
||||
{
|
||||
};
|
||||
|
||||
private WasmAsmTokenKindProvider() : base(
|
||||
Registers.Length +
|
||||
Qualifiers.Length +
|
||||
Instructions.Length +
|
||||
BranchInstructions.Length +
|
||||
JumpInstructions.Length +
|
||||
ReturnInstructions.Length +
|
||||
SimdInstructions.Length)
|
||||
{
|
||||
foreach (var register in Registers)
|
||||
{
|
||||
AddTokenKind(register, AsmTokenKind.Register);
|
||||
}
|
||||
|
||||
foreach (var instruction in Qualifiers)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.Qualifier);
|
||||
}
|
||||
|
||||
foreach (var instruction in Instructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.Instruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in BranchInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.BranchInstruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in JumpInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.JumpInstruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in ReturnInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.ReturnInstruction);
|
||||
}
|
||||
|
||||
foreach (var instruction in SimdInstructions)
|
||||
{
|
||||
AddTokenKind(instruction, AsmTokenKind.InstructionSIMD);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool AcceptsCharAsIdentifierOrRegisterEnd(char c)
|
||||
{
|
||||
return c == '.';
|
||||
}
|
||||
|
||||
public override bool IsInstructionOrRegisterOrIdentifier(char c)
|
||||
{
|
||||
// Wasm should not take '.' with it as this will take register.instruction combo as one.
|
||||
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' ||
|
||||
c == '@';
|
||||
}
|
||||
|
||||
public override SIMDkind SimdKind(StringSlice instruction)
|
||||
{
|
||||
throw new NotImplementedException("WASM does not contain any SIMD instruction.");
|
||||
}
|
||||
|
||||
public static readonly WasmAsmTokenKindProvider Instance = new WasmAsmTokenKindProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,263 @@
|
|||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal partial class BurstDisassembler
|
||||
{
|
||||
internal class WasmInstructionInfo
|
||||
{
|
||||
internal static bool GetWasmInfo(string instructionName, out string instructionInfo)
|
||||
{
|
||||
var returnValue = true;
|
||||
|
||||
switch (instructionName)
|
||||
{
|
||||
case "if":
|
||||
instructionInfo = "Executes a statement if the last item on the stack is true.";
|
||||
break;
|
||||
case "end":
|
||||
instructionInfo = "Can be used to end a block, loop, if or else.";
|
||||
break;
|
||||
case "end_function":
|
||||
instructionInfo = "Ends function.";
|
||||
break;
|
||||
case "block":
|
||||
instructionInfo = "Creates a label that can later be branched out of with a br.";
|
||||
break;
|
||||
case "end_block":
|
||||
instructionInfo = "Ends the previous opened block.";
|
||||
break;
|
||||
case "loop":
|
||||
instructionInfo = "Creates a label that can later be branched to with a br.";
|
||||
break;
|
||||
case "end_loop":
|
||||
instructionInfo = "Ends the previous opened loop label.";
|
||||
break;
|
||||
case "unreachable":
|
||||
instructionInfo = "Denotes a point in code that should not be reachable.";
|
||||
break;
|
||||
case "nop":
|
||||
instructionInfo = "Does nothing.";
|
||||
break;
|
||||
case "call":
|
||||
instructionInfo = "Calls a function.";
|
||||
break;
|
||||
case "call_indirect":
|
||||
instructionInfo = "Calls a function in a table.";
|
||||
break;
|
||||
case "drop":
|
||||
instructionInfo = "Pops a value from the stack, and discards it.";
|
||||
break;
|
||||
case "select":
|
||||
instructionInfo = "Selects one of its first two operands based on a boolean condition.";
|
||||
break;
|
||||
case "get":
|
||||
instructionInfo = "Load the value of a variable onto the stack.";
|
||||
break;
|
||||
case "set":
|
||||
instructionInfo = "Set the value of a variable.";
|
||||
break;
|
||||
case "tee":
|
||||
instructionInfo = "Set the value of a variable and keep the value on the stack.";
|
||||
break;
|
||||
case "load":
|
||||
instructionInfo = "Load a number from memory.";
|
||||
break;
|
||||
case "load8_s":
|
||||
instructionInfo = "Load a signed 8-bit value from memory.";
|
||||
break;
|
||||
case "load8_u":
|
||||
instructionInfo = "Load an unsigned 8-bit value from memory.";
|
||||
break;
|
||||
case "load16_s":
|
||||
instructionInfo = "Load a signed 16-bit value from memory.";
|
||||
break;
|
||||
case "load16_u":
|
||||
instructionInfo = "Load an unsigned 16-bit value from memory.";
|
||||
break;
|
||||
case "load32_s":
|
||||
instructionInfo = "Load a signed 32-bit value from memory.";
|
||||
break;
|
||||
case "load32_u":
|
||||
instructionInfo = "Load an unsigned 32-bit value from memory.";
|
||||
break;
|
||||
case "store":
|
||||
instructionInfo = "Store a number in memory.";
|
||||
break;
|
||||
case "store8":
|
||||
instructionInfo = "Store a 8-bit number in memory.";
|
||||
break;
|
||||
case "store16":
|
||||
instructionInfo = "Store a 16-bit number in memory.";
|
||||
break;
|
||||
case "store32":
|
||||
instructionInfo = "Store a 32-bit number in memory.";
|
||||
break;
|
||||
case "size":
|
||||
instructionInfo = "Get the size of the memory instance.";
|
||||
break;
|
||||
case "grow":
|
||||
instructionInfo = "Increase the size of the memory instance.";
|
||||
break;
|
||||
case "const":
|
||||
instructionInfo = "Declare a constant number.";
|
||||
break;
|
||||
case "clz":
|
||||
instructionInfo = "Count leading zeros in a numbers binary representation.";
|
||||
break;
|
||||
case "ctz":
|
||||
instructionInfo = "Count trailing zeros in a numbers binary representation.";
|
||||
break;
|
||||
case "popcnt":
|
||||
instructionInfo = "Count the number of '1' in a numbers binary representation.";
|
||||
break;
|
||||
case "add":
|
||||
instructionInfo = "Add up two numbers.";
|
||||
break;
|
||||
case "sub":
|
||||
instructionInfo = "Subtract one number from another number.";
|
||||
break;
|
||||
case "mul":
|
||||
instructionInfo = "Multiply one number by another number.";
|
||||
break;
|
||||
case "div_s":
|
||||
instructionInfo = "Divide two signed numbers.";
|
||||
break;
|
||||
case "div_u":
|
||||
instructionInfo = "Divide two unsigned numbers.";
|
||||
break;
|
||||
case "rem_s":
|
||||
instructionInfo = "Calculate the remainder left over when two signed integers are divided.";
|
||||
break;
|
||||
case "rem_u":
|
||||
instructionInfo = "Calculate the remainder left over when two unsigned integers are divided.";
|
||||
break;
|
||||
case "and":
|
||||
instructionInfo = "Bitwise and operation.";
|
||||
break;
|
||||
case "or":
|
||||
instructionInfo = "Bitwise or operation.";
|
||||
break;
|
||||
case "xor":
|
||||
instructionInfo = "Bitwise exclusive or operation.";
|
||||
break;
|
||||
case "shl":
|
||||
instructionInfo = "Bitwise shift left operation.";
|
||||
break;
|
||||
case "shr_s":
|
||||
instructionInfo = "Bitwise signed shift right operation.";
|
||||
break;
|
||||
case "shr_u":
|
||||
instructionInfo = "Bitwise unsigned shift right operation.";
|
||||
break;
|
||||
case "rotl":
|
||||
instructionInfo = "Bitwise rotate left operation.";
|
||||
break;
|
||||
case "rotr":
|
||||
instructionInfo = "Bitwise rotate right operation.";
|
||||
break;
|
||||
case "abs":
|
||||
instructionInfo = "Get the absolute value of a number.";
|
||||
break;
|
||||
case "neg":
|
||||
instructionInfo = "Negate a number.";
|
||||
break;
|
||||
case "ceil":
|
||||
instructionInfo = "Round up a number.";
|
||||
break;
|
||||
case "floor":
|
||||
instructionInfo = "Round down a number.";
|
||||
break;
|
||||
case "trunc":
|
||||
instructionInfo = "Discard the fractional part of a number.";
|
||||
break;
|
||||
case "sqrt":
|
||||
instructionInfo = "Get the square root of a number.";
|
||||
break;
|
||||
case "div":
|
||||
instructionInfo = "Divide two numbers.";
|
||||
break;
|
||||
case "min":
|
||||
instructionInfo = "Get the lower of two numbers.";
|
||||
break;
|
||||
case "max":
|
||||
instructionInfo = "Get the highest of two numbers.";
|
||||
break;
|
||||
case "copysign":
|
||||
instructionInfo = "Copy just the sign bit from one number to another.";
|
||||
break;
|
||||
case "wrap_i64":
|
||||
instructionInfo = "Convert (wrap) i64 number to i32 number.";
|
||||
break;
|
||||
case "trunc_f32_s":
|
||||
instructionInfo = "Truncate fractional part away from a signed 32-bit floating number, giving a " +
|
||||
"signed 32-bit integer.";
|
||||
break;
|
||||
case "trunc_f32_u":
|
||||
instructionInfo = "Truncate fractional part away from a unsigned 32-bit floating number, giving a " +
|
||||
"unsigned 32-bit integer.";
|
||||
break;
|
||||
case "trunc_f64_s":
|
||||
instructionInfo = "Truncate fractional part away from a signed 64-bit floating number, giving a " +
|
||||
"signed 64-bit integer.";
|
||||
break;
|
||||
case "trunc_f64_u":
|
||||
instructionInfo = "Truncate fractional part away from a unsigned 64-bit floating number, giving a " +
|
||||
"unsigned 64-bit integer.";
|
||||
break;
|
||||
case "extend_i32_s":
|
||||
instructionInfo = "Convert (extend) signed 32-bit integer to signed 64-bit integer number.";
|
||||
break;
|
||||
case "extend_i32_u":
|
||||
instructionInfo = "Convert (extend) unsigned 32-bit integer to unsigned 64-bit integer number.";
|
||||
break;
|
||||
case "convert_i32_s":
|
||||
instructionInfo = "Convert signed 32-bit integer to signed 32-bit floating number.";
|
||||
break;
|
||||
case "convert_i32_u":
|
||||
instructionInfo = "Convert unsigned 32-bit integer to unsigned 32-bit floating number.";
|
||||
break;
|
||||
case "convert_i64_s":
|
||||
instructionInfo = "Convert signed 64-bit integer to signed 64-bit floating number.";
|
||||
break;
|
||||
case "convert_i64_u":
|
||||
instructionInfo = "Convert unsigned 64-bit integer to unsigned 64-bit floating number.";
|
||||
break;
|
||||
case "demote_f64":
|
||||
instructionInfo = "Convert (demote) 64-bit floating number to 32-bit floating number.";
|
||||
break;
|
||||
case "promote_f32":
|
||||
instructionInfo = "Convert (promote) 32-bit floating number to 64-bit floating number.";
|
||||
break;
|
||||
case "reinterpret_f32":
|
||||
instructionInfo = "Reinterpret the bytes of 32-bit floating number as 32-bit integer number.";
|
||||
break;
|
||||
case "reinterpret_f64":
|
||||
instructionInfo = "Reinterpret the bytes of 64-bit floating number as 64-bit integer number.";
|
||||
break;
|
||||
case "reinterpret_i32":
|
||||
instructionInfo = "Reinterpret the bytes of 32-bit integer number as 32-bit floating number.";
|
||||
break;
|
||||
case "reinterpret_i64":
|
||||
instructionInfo = "Reinterpret the bytes of 64-bit integer number as 64-bit floating number.";
|
||||
break;
|
||||
case "br_if":
|
||||
instructionInfo = "Branch to a loop or block if condition is true.";
|
||||
break;
|
||||
case "br":
|
||||
instructionInfo = "Branch to a loop or block.";
|
||||
break;
|
||||
case "br_table":
|
||||
instructionInfo = "Branch to a loop or block if condition based on argument.";
|
||||
break;
|
||||
case "return":
|
||||
instructionInfo = "Returns from a function.";
|
||||
break;
|
||||
default:
|
||||
instructionInfo = string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,575 @@
|
|||
#if UNITY_EDITOR || BURST_INTERNAL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal partial class BurstDisassembler
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for providing extended information of an identifier
|
||||
/// </summary>
|
||||
internal abstract class AsmTokenKindProvider
|
||||
{
|
||||
// Internally using string slice instead of string
|
||||
// to support faster lookup from AsmToken
|
||||
private readonly Dictionary<StringSlice, AsmTokenKind> _tokenKinds;
|
||||
private int _maximumLength;
|
||||
|
||||
protected AsmTokenKindProvider(int capacity)
|
||||
{
|
||||
_tokenKinds = new Dictionary<StringSlice, AsmTokenKind>(capacity);
|
||||
}
|
||||
|
||||
protected void AddTokenKind(string text, AsmTokenKind kind)
|
||||
{
|
||||
_tokenKinds.Add(new StringSlice(text), kind);
|
||||
if (text.Length > _maximumLength) _maximumLength = text.Length;
|
||||
}
|
||||
|
||||
public virtual AsmTokenKind FindTokenKind(StringSlice slice)
|
||||
{
|
||||
return slice.Length <= _maximumLength && _tokenKinds.TryGetValue(slice, out var tokenKind)
|
||||
? tokenKind
|
||||
: AsmTokenKind.Identifier;
|
||||
}
|
||||
|
||||
public virtual bool AcceptsCharAsIdentifierOrRegisterEnd(char c)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool IsInstructionOrRegisterOrIdentifier(char c)
|
||||
{
|
||||
// we include . because we have instructions like `b.le` or `f32.const`
|
||||
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_' ||
|
||||
c == '@' || c == '.';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether regA == regB. This function assumes the given strings are proper registers.
|
||||
/// </summary>
|
||||
public virtual bool RegisterEqual(string regA, string regB) => regA == regB;
|
||||
|
||||
public abstract SIMDkind SimdKind(StringSlice instruction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ASM tokenizer
|
||||
/// </summary>
|
||||
private struct AsmTokenizer
|
||||
{
|
||||
private readonly string _text;
|
||||
private readonly AsmKind _asmKind;
|
||||
private readonly AsmTokenKindProvider _tokenKindProvider;
|
||||
private int _position;
|
||||
private int _nextPosition;
|
||||
private int _alignedPosition;
|
||||
private int _nextAlignedPosition;
|
||||
private char _c;
|
||||
private readonly char _commentStartChar;
|
||||
private bool _doPad;
|
||||
private int _padding;
|
||||
|
||||
public AsmTokenizer(string text, AsmKind asmKind, AsmTokenKindProvider tokenKindProvider, char commentStart)
|
||||
{
|
||||
_text = text;
|
||||
_asmKind = asmKind;
|
||||
_tokenKindProvider = tokenKindProvider;
|
||||
_position = 0;
|
||||
_nextPosition = 0;
|
||||
_alignedPosition = 0;
|
||||
_nextAlignedPosition = 0;
|
||||
_commentStartChar = commentStart;
|
||||
_doPad = false;
|
||||
_padding = 0;
|
||||
_c = (char)0;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
public bool TryGetNextToken(out AsmToken token)
|
||||
{
|
||||
token = new AsmToken();
|
||||
while (true)
|
||||
{
|
||||
var startPosition = _position;
|
||||
var startAlignedPosition = _alignedPosition;
|
||||
|
||||
if (_c == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_c == '.')
|
||||
{
|
||||
token = ParseDirective(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Like everywhere else in this file, we are inlining the matching characters instead
|
||||
// of using helper functions, as Mono might not be enough good at inlining by itself
|
||||
if (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c == '_' || _c == '@')
|
||||
{
|
||||
token = ParseInstructionOrIdentifierOrRegister(startPosition, startAlignedPosition);
|
||||
PrepareAlignment(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_c >= '0' && _c <= '9' || _c == '-')
|
||||
{
|
||||
token = ParseNumber(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_c == '"')
|
||||
{
|
||||
token = ParseString(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_c == _commentStartChar)
|
||||
{
|
||||
token = ParseComment(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_c == '\r')
|
||||
{
|
||||
if (PreviewChar() == '\n')
|
||||
{
|
||||
NextChar(); // skip \r
|
||||
}
|
||||
token = ParseNewLine(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_c == '\n')
|
||||
{
|
||||
token = ParseNewLine(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_doPad)
|
||||
{
|
||||
_nextAlignedPosition += _padding;
|
||||
_doPad = false;
|
||||
}
|
||||
token = ParseMisc(startPosition, startAlignedPosition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareAlignment(AsmToken token)
|
||||
{
|
||||
var kind = token.Kind;
|
||||
_padding = InstructionAlignment - token.Length;
|
||||
_doPad = _asmKind == AsmKind.Intel
|
||||
&& (kind == AsmTokenKind.Instruction
|
||||
|| kind == AsmTokenKind.BranchInstruction
|
||||
|| kind == AsmTokenKind.CallInstruction
|
||||
|| kind == AsmTokenKind.JumpInstruction
|
||||
|| kind == AsmTokenKind.ReturnInstruction
|
||||
|| kind == AsmTokenKind.InstructionSIMD)
|
||||
&& _c != '\n' && _c != '\r' // If there is no registers behind instruction don't align.
|
||||
&& _padding > 0;
|
||||
}
|
||||
|
||||
private AsmToken ParseNewLine(int startPosition, int startAlignedPosition)
|
||||
{
|
||||
var endPosition = _position;
|
||||
NextChar(); // Skip newline
|
||||
return new AsmToken(AsmTokenKind.NewLine, startPosition, startAlignedPosition, endPosition - startPosition + 1);
|
||||
}
|
||||
|
||||
private AsmToken ParseMisc(int startPosition, int startAlignedPosition)
|
||||
{
|
||||
var endPosition = _position;
|
||||
// Parse anything that is not a directive, instruction, number, string or comment
|
||||
while (!((_c == (char)0) || (_c == '\r') || (_c == '\n') || (_c == '.') || (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c == '_' || _c == '@') || (_c >= '0' && _c <= '9' || _c == '-') || (_c == '"') || (_c == _commentStartChar)))
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
return new AsmToken(AsmTokenKind.Misc, startPosition, startAlignedPosition, endPosition - startPosition + 1);
|
||||
}
|
||||
|
||||
private static readonly string[] DataDirectiveStrings = new[]
|
||||
{
|
||||
AssertDataDirectiveLength(".long"),
|
||||
AssertDataDirectiveLength(".byte"),
|
||||
AssertDataDirectiveLength(".short"),
|
||||
AssertDataDirectiveLength(".ascii"),
|
||||
AssertDataDirectiveLength(".asciz"),
|
||||
};
|
||||
|
||||
private static string AssertDataDirectiveLength(string text)
|
||||
{
|
||||
var length = text.Length;
|
||||
Debug.Assert(length == 5 || length == 6, $"Invalid length {length} for string {text}. Expecting 5 or 6");
|
||||
return text;
|
||||
}
|
||||
|
||||
private AsmToken ParseDirective(int startPosition, int startAlignedPosition)
|
||||
{
|
||||
var endPosition = _position;
|
||||
NextChar(); // skip .
|
||||
bool isLabel = _c == 'L'; // A label starts with a capital `L` like .Lthis_is_a_jump_label
|
||||
while (_c >= 'a' && _c <= 'z' || _c >= 'A' && _c <= 'Z' || _c >= '0' && _c <= '9' || _c == '.' || _c == '_' || _c == '@')
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
// Refine the kind of directive:
|
||||
//
|
||||
// .Lfunc_begin => FunctionBegin
|
||||
// .Lfunc_end => FunctionEnd
|
||||
// .L????????? => Label
|
||||
// data directive (.byte, .long, .short...) => DataDirective
|
||||
// anything else => Directive
|
||||
const string MatchFunc = ".Lfunc_";
|
||||
const int MatchFuncLength = 7;
|
||||
Debug.Assert(MatchFunc.Length == MatchFuncLength);
|
||||
var kind = isLabel ? AsmTokenKind.Label : AsmTokenKind.Directive;
|
||||
// Fast early check
|
||||
if (isLabel && string.CompareOrdinal(_text, startPosition, MatchFunc, 0, MatchFuncLength) == 0)
|
||||
{
|
||||
if (string.CompareOrdinal(_text, startPosition, ".Lfunc_begin", 0, ".Lfunc_begin".Length) == 0)
|
||||
{
|
||||
kind = AsmTokenKind.FunctionBegin;
|
||||
}
|
||||
else if (string.CompareOrdinal(_text, startPosition, ".Lfunc_end", 0, ".Lfunc_end".Length) == 0)
|
||||
{
|
||||
kind = AsmTokenKind.FunctionEnd;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust directive to mark data directives, source location directives...etc.
|
||||
int length = endPosition - startPosition + 1;
|
||||
|
||||
// Use length to early exit
|
||||
if (!isLabel && length >= 4 && length <= 8)
|
||||
{
|
||||
if ((length == 5 || length == 6))
|
||||
{
|
||||
foreach (var dataDirectiveStr in DataDirectiveStrings)
|
||||
{
|
||||
if (string.CompareOrdinal(_text, startPosition, dataDirectiveStr, 0, dataDirectiveStr.Length) == 0)
|
||||
{
|
||||
kind = AsmTokenKind.DataDirective;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// .file => SourceFile
|
||||
if (kind == AsmTokenKind.Directive && string.CompareOrdinal(_text, startPosition, ".file", 0, 5) == 0)
|
||||
{
|
||||
kind = AsmTokenKind.SourceFile;
|
||||
}
|
||||
}
|
||||
// .loc => SourceLocation
|
||||
// .cv_loc => SourceLocation
|
||||
else if ((length == 4 && string.CompareOrdinal(_text, startPosition, ".loc", 0, 4) == 0) ||
|
||||
(length == 7 && string.CompareOrdinal(_text, startPosition, ".cv_loc", 0, 7) == 0))
|
||||
{
|
||||
kind = AsmTokenKind.SourceLocation;
|
||||
}
|
||||
// .file .cv_file => SourceFile
|
||||
else if (length == 8 && string.CompareOrdinal(_text, startPosition, ".cv_file", 0, 8) == 0)
|
||||
{
|
||||
kind = AsmTokenKind.SourceFile;
|
||||
}
|
||||
}
|
||||
|
||||
return new AsmToken(kind, startPosition, startAlignedPosition, length);
|
||||
}
|
||||
|
||||
private AsmToken ParseInstructionOrIdentifierOrRegister(int startPosition, int startAlignedPosition)
|
||||
{
|
||||
var endPosition = _position;
|
||||
while (_tokenKindProvider.IsInstructionOrRegisterOrIdentifier(_c))
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
if (_tokenKindProvider.AcceptsCharAsIdentifierOrRegisterEnd(_c))
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
// Resolve token kind for identifier
|
||||
int length = endPosition - startPosition + 1;
|
||||
var tokenKind = _tokenKindProvider.FindTokenKind(new StringSlice(_text, startPosition, length));
|
||||
|
||||
if (tokenKind == AsmTokenKind.Identifier)
|
||||
{
|
||||
// If we have `:` right after an identifier, change from identifier to label declaration to help the semantic pass later
|
||||
if (_c == ':')
|
||||
{
|
||||
tokenKind = AsmTokenKind.Label;
|
||||
}
|
||||
}
|
||||
|
||||
return new AsmToken(tokenKind, startPosition, startAlignedPosition, endPosition - startPosition + 1);
|
||||
}
|
||||
|
||||
private AsmToken ParseNumber(int startPosition, int startAlignedPostion)
|
||||
{
|
||||
var endPosition = _position;
|
||||
if (_c == '-')
|
||||
{
|
||||
NextChar();
|
||||
}
|
||||
while (_c >= '0' && _c <= '9' || _c >= 'a' && _c <= 'f' || _c >= 'A' && _c <= 'F' || _c == 'x' || _c == '.')
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
// If we have `:` right after a number, change from number to label declaration to help the semantic pass later
|
||||
var numberKind = _c == ':' ? AsmTokenKind.Label : AsmTokenKind.Number;
|
||||
return new AsmToken(numberKind, startPosition, startAlignedPostion, endPosition - startPosition + 1);
|
||||
}
|
||||
private AsmToken ParseString(int startPosition, int startAlignedPostion)
|
||||
{
|
||||
var endPosition = _position;
|
||||
// Skip first "
|
||||
NextChar();
|
||||
while (_c != (char)0 && _c != '"')
|
||||
{
|
||||
// Skip escape \"
|
||||
if (_c == '\\' && PreviewChar() == '"')
|
||||
{
|
||||
NextChar();
|
||||
}
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
endPosition = _position;
|
||||
NextChar(); // Skip trailing 0
|
||||
|
||||
// If we have `:` right after a string, change from string to label declaration to help the semantic pass later
|
||||
var stringKind = _c == ':' ? AsmTokenKind.Label : AsmTokenKind.String;
|
||||
return new AsmToken(stringKind, startPosition, startAlignedPostion, endPosition - startPosition + 1);
|
||||
}
|
||||
|
||||
private AsmToken ParseComment(int startPosition, int startAlignedPosition)
|
||||
{
|
||||
var endPosition = _position;
|
||||
while (_c != (char)0 && (_c != '\n' && _c != '\r'))
|
||||
{
|
||||
endPosition = _position;
|
||||
NextChar();
|
||||
}
|
||||
|
||||
return new AsmToken(AsmTokenKind.Comment, startPosition, startAlignedPosition, endPosition - startPosition + 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void NextChar()
|
||||
{
|
||||
if (_nextPosition < _text.Length)
|
||||
{
|
||||
_position = _nextPosition;
|
||||
_c = _text[_position];
|
||||
_nextPosition = _position + 1;
|
||||
|
||||
_alignedPosition = _nextAlignedPosition;
|
||||
_nextAlignedPosition = _alignedPosition + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_c = (char)0;
|
||||
}
|
||||
}
|
||||
|
||||
private char PreviewChar()
|
||||
{
|
||||
return _nextPosition < _text.Length ? _text[_nextPosition] : (char)0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum SIMDkind
|
||||
{
|
||||
Packed,
|
||||
Scalar,
|
||||
Infrastructure,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An ASM token. The token doesn't contain the string of the token, but provides method <see cref="Slice"/> and <see cref="ToString"/> to extract it.
|
||||
/// </summary>
|
||||
internal readonly struct AsmToken
|
||||
{
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CAUTION: It is important to not put *any managed objects*
|
||||
// into this struct for GC efficiency
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public AsmToken(AsmTokenKind kind, int position, int alignedPosition, int length)
|
||||
{
|
||||
Kind = kind;
|
||||
Position = position;
|
||||
AlignedPosition = alignedPosition;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public readonly AsmTokenKind Kind;
|
||||
|
||||
public readonly int Position;
|
||||
|
||||
public readonly int AlignedPosition;
|
||||
|
||||
public readonly int Length;
|
||||
|
||||
public StringSlice Slice(string text) => new StringSlice(text, Position, Length);
|
||||
|
||||
public string ToString(string text) => text.Substring(Position, Length);
|
||||
|
||||
public string ToFriendlyText(string text)
|
||||
{
|
||||
return $"{text.Substring(Position, Length)} : {Kind}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kind of an ASM token.
|
||||
/// </summary>
|
||||
internal enum AsmTokenKind
|
||||
{
|
||||
Eof,
|
||||
Directive,
|
||||
DataDirective,
|
||||
SourceFile,
|
||||
SourceLocation,
|
||||
Label,
|
||||
FunctionBegin,
|
||||
FunctionEnd,
|
||||
Identifier,
|
||||
Qualifier,
|
||||
Instruction,
|
||||
CallInstruction,
|
||||
BranchInstruction,
|
||||
JumpInstruction,
|
||||
ReturnInstruction,
|
||||
InstructionSIMD,
|
||||
Register,
|
||||
Number,
|
||||
String,
|
||||
Comment,
|
||||
NewLine,
|
||||
Misc
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// A slice of a string from an original string.
|
||||
/// </summary>
|
||||
internal readonly struct StringSlice : IEquatable<StringSlice>
|
||||
{
|
||||
private readonly string _text;
|
||||
|
||||
public readonly int Position;
|
||||
|
||||
public readonly int Length;
|
||||
|
||||
public StringSlice(string text)
|
||||
{
|
||||
_text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Position = 0;
|
||||
Length = text.Length;
|
||||
}
|
||||
|
||||
public StringSlice(string text, int position, int length)
|
||||
{
|
||||
_text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Position = position;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public char this[int index] => _text[Position + index];
|
||||
|
||||
public bool Equals(StringSlice other)
|
||||
{
|
||||
if (Length != other.Length) return false;
|
||||
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
if (this[i] != other[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is StringSlice other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = Length;
|
||||
for (int i = 0; i < Length; i++)
|
||||
{
|
||||
hashCode = (hashCode * 397) ^ this[i];
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(StringSlice left, StringSlice right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(StringSlice left, StringSlice right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _text.Substring(Position, Length);
|
||||
}
|
||||
|
||||
public bool StartsWith(string text)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (Length < text.Length) return false;
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
if (_text[Position + i] != c) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Contains(char c) => _text.Skip(Position).Take(Length).Any(elm => elm == c);
|
||||
|
||||
public int IndexOf(char c)
|
||||
{
|
||||
for (var i = 0; i < Length; i++)
|
||||
{
|
||||
if (_text[Position + i] == c)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Some code in Unity.Entities.TypeManager
|
||||
// (https://github.cds.internal.unity3d.com/unity/dots/blob/d82f136abd45af8760235b885b63ecb50dcaf5f8/Packages/com.unity.entities/Unity.Entities/Types/TypeManager.cs#L426)
|
||||
// uses reflection to call a static Unity.Burst.Editor.BurstLoader.IsDebugging property,
|
||||
// to ensure that BurstLoader has been initialized.
|
||||
// It specifically looks in the Unity.Burst.Editor.dll assembly.
|
||||
// So we use type-forwarding to let it find the "real" BurstLoader.
|
||||
[assembly: TypeForwardedToAttribute(typeof(Unity.Burst.Editor.BurstLoader))]
|
|
@ -0,0 +1,94 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal static class BurstMath
|
||||
{
|
||||
private const float HitBoxAdjust = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Rotates <see cref="point"/> around a pivot point according to angle given in degrees.
|
||||
/// </summary>
|
||||
/// <param name="angle">Angle in degrees.</param>
|
||||
/// <param name="point">Point to rotate.</param>
|
||||
/// <param name="pivotPoint">Pivot point to rotate around.</param>
|
||||
/// <returns>The rotated point.</returns>
|
||||
internal static Vector2 AnglePoint(float angle, Vector2 point, Vector2 pivotPoint)
|
||||
{
|
||||
// https://matthew-brett.github.io/teaching/rotation_2d.html
|
||||
// Problem Angle is calculates as angle clockwise, and here we use it as it was counterclockwise!
|
||||
var s = Mathf.Sin(angle);
|
||||
var c = Mathf.Cos(angle);
|
||||
point -= pivotPoint;
|
||||
|
||||
return new Vector2(c * point.x - s * point.y, s * point.x + c * point.y) + pivotPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculated angle in degrees between two points.
|
||||
/// </summary>
|
||||
/// <param name="start">Starting point.</param>
|
||||
/// <param name="end">End point.</param>
|
||||
/// <returns>Angle in degrees.</returns>
|
||||
internal static float CalculateAngle(Vector2 start, Vector2 end)
|
||||
{
|
||||
var distance = end - start;
|
||||
var angle = Mathf.Rad2Deg * Mathf.Atan(distance.y / distance.x);
|
||||
if (distance.x < 0)
|
||||
{
|
||||
angle += 180;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <see cref="point"/> is within <see cref="rect"/> enlarged by <see cref="HitBoxAdjust"/>.
|
||||
/// </summary>
|
||||
/// <param name="rect">Give rect to enlarge and check with.</param>
|
||||
/// <param name="point">Given point to match in enlarged <see cref="rect"/>.</param>
|
||||
/// <returns>Whether <see cref="point"/> is within enlarged <see cref="rect"/>.</returns>
|
||||
internal static bool AdjustedContains(Rect rect, Vector2 point)
|
||||
{
|
||||
return rect.yMax + HitBoxAdjust >= point.y && rect.yMin - HitBoxAdjust <= point.y
|
||||
&& rect.xMax + HitBoxAdjust >= point.x && rect.xMin - HitBoxAdjust <= point.x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if <see cref="num"/> is within the closed interval defined by endPoint1 and endPoint2.
|
||||
/// </summary>
|
||||
/// <param name="endPoint1">One side of range.</param>
|
||||
/// <param name="endPoint2">Other side of range.</param>
|
||||
/// <param name="num">Number to check if it is in between <see cref="endPoint1"/> and <see cref="endPoint2"/>.</param>
|
||||
/// <returns>Whether <see cref="num"/> is within given range.</returns>
|
||||
internal static bool WithinRange(float endPoint1, float endPoint2, float num)
|
||||
{
|
||||
float start, end;
|
||||
if (endPoint1 < endPoint2)
|
||||
{
|
||||
start = endPoint1;
|
||||
end = endPoint2;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = endPoint2;
|
||||
end = endPoint1;
|
||||
}
|
||||
return start <= num && num <= end;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds down to nearest amount specified by <see cref="to"/>.
|
||||
/// </summary>
|
||||
/// <param name="number">Number to round down.</param>
|
||||
/// <param name="to">Specifies what amount to round down to.</param>
|
||||
/// <returns><see cref="number"/> rounded down to amount <see cref="to"/>.</returns>
|
||||
internal static float RoundDownToNearest(float number, float to)
|
||||
{
|
||||
//https://www.programmingnotes.org/7601/cs-how-to-round-a-number-to-the-nearest-x-using-cs/
|
||||
float inverse = 1 / to;
|
||||
float dividend = Mathf.Floor(number * inverse);
|
||||
return dividend / inverse;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
using Unity.Burst.LowLevel;
|
||||
using UnityEditor;
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Register all menu entries for burst to the Editor
|
||||
/// </summary>
|
||||
internal static class BurstMenu
|
||||
{
|
||||
private const string EnableBurstCompilationText = "Jobs/Burst/Enable Compilation";
|
||||
private const string EnableSafetyChecksTextOff = "Jobs/Burst/Safety Checks/Off";
|
||||
private const string EnableSafetyChecksTextOn = "Jobs/Burst/Safety Checks/On";
|
||||
private const string EnableSafetyChecksTextForceOn = "Jobs/Burst/Safety Checks/Force On";
|
||||
private const string ForceSynchronousCompilesText = "Jobs/Burst/Synchronous Compilation";
|
||||
private const string EnableDebugCompilationText = "Jobs/Burst/Native Debug Mode Compilation";
|
||||
private const string ShowBurstTimingsText = "Jobs/Burst/Show Timings";
|
||||
private const string BurstInspectorText = "Jobs/Burst/Open Inspector...";
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #1 Enable Compilation
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(EnableBurstCompilationText, false, 1)]
|
||||
private static void EnableBurstCompilation()
|
||||
{
|
||||
ChangeOptionSafely(() => BurstEditorOptions.EnableBurstCompilation = !BurstEditorOptions.EnableBurstCompilation);
|
||||
}
|
||||
|
||||
[MenuItem(EnableBurstCompilationText, true, 1)]
|
||||
private static bool EnableBurstCompilationValidate()
|
||||
{
|
||||
Menu.SetChecked(EnableBurstCompilationText, BurstEditorOptions.EnableBurstCompilation);
|
||||
return BurstCompilerService.IsInitialized;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #2 Safety Checks
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(EnableSafetyChecksTextOff, false, 1)]
|
||||
private static void EnableBurstSafetyChecksOff()
|
||||
{
|
||||
ChangeOptionSafely(() =>
|
||||
{
|
||||
BurstEditorOptions.EnableBurstSafetyChecks = false;
|
||||
BurstEditorOptions.ForceEnableBurstSafetyChecks = false;
|
||||
});
|
||||
Menu.SetChecked(EnableSafetyChecksTextOff, true);
|
||||
Menu.SetChecked(EnableSafetyChecksTextOn, false);
|
||||
Menu.SetChecked(EnableSafetyChecksTextForceOn, false);
|
||||
}
|
||||
|
||||
[MenuItem(EnableSafetyChecksTextOff, true, 1)]
|
||||
private static bool EnableBurstSafetyChecksOffValidate()
|
||||
{
|
||||
Menu.SetChecked(EnableSafetyChecksTextOff, !BurstEditorOptions.EnableBurstSafetyChecks && !BurstEditorOptions.ForceEnableBurstSafetyChecks);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
[MenuItem(EnableSafetyChecksTextOn, false, 2)]
|
||||
private static void EnableBurstSafetyChecksOn()
|
||||
{
|
||||
ChangeOptionSafely(() =>
|
||||
{
|
||||
BurstEditorOptions.EnableBurstSafetyChecks = true;
|
||||
BurstEditorOptions.ForceEnableBurstSafetyChecks = false;
|
||||
});
|
||||
Menu.SetChecked(EnableSafetyChecksTextOff, false);
|
||||
Menu.SetChecked(EnableSafetyChecksTextOn, true);
|
||||
Menu.SetChecked(EnableSafetyChecksTextForceOn, false);
|
||||
}
|
||||
|
||||
[MenuItem(EnableSafetyChecksTextOn, true, 2)]
|
||||
private static bool EnableBurstSafetyChecksOnValidate()
|
||||
{
|
||||
Menu.SetChecked(EnableSafetyChecksTextOn, BurstEditorOptions.EnableBurstSafetyChecks && !BurstEditorOptions.ForceEnableBurstSafetyChecks);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
[MenuItem(EnableSafetyChecksTextForceOn, false, 3)]
|
||||
private static void EnableBurstSafetyChecksForceOn()
|
||||
{
|
||||
ChangeOptionSafely(() =>
|
||||
{
|
||||
BurstEditorOptions.EnableBurstSafetyChecks = true;
|
||||
BurstEditorOptions.ForceEnableBurstSafetyChecks = true;
|
||||
});
|
||||
Menu.SetChecked(EnableSafetyChecksTextOff, false);
|
||||
Menu.SetChecked(EnableSafetyChecksTextOn, false);
|
||||
Menu.SetChecked(EnableSafetyChecksTextForceOn, true);
|
||||
}
|
||||
|
||||
[MenuItem(EnableSafetyChecksTextForceOn, true, 3)]
|
||||
private static bool EnableBurstSafetyChecksForceOnValidate()
|
||||
{
|
||||
Menu.SetChecked(EnableSafetyChecksTextForceOn, BurstEditorOptions.ForceEnableBurstSafetyChecks);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #3 Synchronous Compilation
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(ForceSynchronousCompilesText, false, 5)]
|
||||
private static void ForceSynchronousCompiles()
|
||||
{
|
||||
BurstEditorOptions.EnableBurstCompileSynchronously = !BurstEditorOptions.EnableBurstCompileSynchronously;
|
||||
}
|
||||
|
||||
[MenuItem(ForceSynchronousCompilesText, true, 5)]
|
||||
private static bool ForceSynchronousCompilesValidate()
|
||||
{
|
||||
Menu.SetChecked(ForceSynchronousCompilesText, BurstEditorOptions.EnableBurstCompileSynchronously);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #4 Synchronous Compilation
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(EnableDebugCompilationText, false, 6)]
|
||||
private static void EnableDebugMode()
|
||||
{
|
||||
ChangeOptionSafely(() =>
|
||||
{
|
||||
BurstEditorOptions.EnableBurstDebug = !BurstEditorOptions.EnableBurstDebug;
|
||||
});
|
||||
}
|
||||
|
||||
[MenuItem(EnableDebugCompilationText, true, 6)]
|
||||
private static bool EnableDebugModeValidate()
|
||||
{
|
||||
Menu.SetChecked(EnableDebugCompilationText, BurstEditorOptions.EnableBurstDebug);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #5 Show Timings
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(ShowBurstTimingsText, false, 7)]
|
||||
private static void ShowBurstTimings()
|
||||
{
|
||||
BurstEditorOptions.EnableBurstTimings = !BurstEditorOptions.EnableBurstTimings;
|
||||
}
|
||||
|
||||
[MenuItem(ShowBurstTimingsText, true, 7)]
|
||||
private static bool ShowBurstTimingsValidate()
|
||||
{
|
||||
Menu.SetChecked(ShowBurstTimingsText, BurstEditorOptions.EnableBurstTimings);
|
||||
return BurstCompilerService.IsInitialized && BurstEditorOptions.EnableBurstCompilation;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// #6 Open Inspector...
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
[MenuItem(BurstInspectorText, false, 8)]
|
||||
private static void BurstInspector()
|
||||
{
|
||||
// Get existing open window or if none, make a new one:
|
||||
BurstInspectorGUI window = EditorWindow.GetWindow<BurstInspectorGUI>("Burst Inspector");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
[MenuItem(BurstInspectorText, true, 8)]
|
||||
private static bool BurstInspectorValidate()
|
||||
{
|
||||
return BurstCompilerService.IsInitialized;
|
||||
}
|
||||
|
||||
private static void ChangeOptionSafely(Action callback)
|
||||
{
|
||||
try
|
||||
{
|
||||
RequiresRestartUtility.CalledFromUI = true;
|
||||
|
||||
callback();
|
||||
|
||||
if (RequiresRestartUtility.RequiresRestart)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
"Editor Restart Required",
|
||||
"This setting will not be applied until the Editor has been restarted. Please restart the Editor to continue.",
|
||||
"OK");
|
||||
BurstCompiler.Shutdown();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
RequiresRestartUtility.RequiresRestart = false;
|
||||
RequiresRestartUtility.CalledFromUI = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal struct SearchCriteria
|
||||
{
|
||||
internal string filter;
|
||||
internal bool isCaseSensitive;
|
||||
internal bool isWholeWords;
|
||||
internal bool isRegex;
|
||||
|
||||
internal SearchCriteria(string keyword, bool caseSensitive, bool wholeWord, bool regex)
|
||||
{
|
||||
filter = keyword;
|
||||
isCaseSensitive = caseSensitive;
|
||||
isWholeWords = wholeWord;
|
||||
isRegex = regex;
|
||||
}
|
||||
|
||||
internal bool Equals(SearchCriteria obj) =>
|
||||
filter == obj.filter && isCaseSensitive == obj.isCaseSensitive && isWholeWords == obj.isWholeWords && isRegex == obj.isRegex;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is SearchCriteria other && Equals(other);
|
||||
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
}
|
||||
|
||||
internal static class BurstStringSearch
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets index of line end in given string, both absolute and relative to start of line.
|
||||
/// </summary>
|
||||
/// <param name="str">String to search in.</param>
|
||||
/// <param name="line">Line to get end index of.</param>
|
||||
/// <returns>(absolute line end index of string, line end index relative to line start).</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Argument must be greater than 0 and less than or equal to number of lines in
|
||||
/// <paramref name="str" />.
|
||||
/// </exception>
|
||||
internal static (int total, int relative) GetEndIndexOfPlainLine (string str, int line)
|
||||
{
|
||||
var lastIdx = -1;
|
||||
var newIdx = -1;
|
||||
|
||||
for (var i = 0; i <= line; i++)
|
||||
{
|
||||
lastIdx = newIdx;
|
||||
newIdx = str.IndexOf('\n', lastIdx + 1);
|
||||
|
||||
if (newIdx == -1 && i < line)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(line),
|
||||
"Argument must be greater than 0 and less than or equal to number of lines in str.");
|
||||
}
|
||||
}
|
||||
lastIdx++;
|
||||
return newIdx != -1 ? (newIdx, newIdx - lastIdx) : (str.Length - 1, str.Length - 1 - lastIdx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of line end in given string, both absolute and relative to start of line.
|
||||
/// Adjusts the index so color tags are not included in relative index.
|
||||
/// </summary>
|
||||
/// <param name="str">String to search in.</param>
|
||||
/// <param name="line">Line to find end of in string.</param>
|
||||
/// <returns>(absolute line end index of string, line end index relative to line start adjusted for color tags).</returns>
|
||||
internal static (int total, int relative) GetEndIndexOfColoredLine(string str, int line)
|
||||
{
|
||||
var (total, relative) = GetEndIndexOfPlainLine(str, line);
|
||||
return RemoveColorTagFromIdx(str, total, relative);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts index of color tags on line.
|
||||
/// </summary>
|
||||
/// <remarks>Assumes that <see cref="tidx"/> is index of something not a color tag.</remarks>
|
||||
/// <param name="str">String containing the indexes.</param>
|
||||
/// <param name="tidx">Total index of line end.</param>
|
||||
/// <param name="ridx">Relative index of line end.</param>
|
||||
/// <returns>(<see cref="tidx"/>, <see cref="ridx"/>) adjusted for color tags on line.</returns>
|
||||
private static (int total, int relative) RemoveColorTagFromIdx(string str, int tidx, int ridx)
|
||||
{
|
||||
var lineStartIdx = tidx - ridx;
|
||||
var colorTagFiller = 0;
|
||||
|
||||
var tmp = str.LastIndexOf("</color", tidx);
|
||||
var lastWasStart = true;
|
||||
var colorTagStart = str.LastIndexOf("<color=", tidx);
|
||||
|
||||
if (tmp > colorTagStart)
|
||||
{
|
||||
// color tag end was closest
|
||||
lastWasStart = false;
|
||||
colorTagStart = tmp;
|
||||
}
|
||||
|
||||
while (colorTagStart != -1 && colorTagStart >= lineStartIdx)
|
||||
{
|
||||
var colorTagEnd = str.IndexOf('>', colorTagStart);
|
||||
// +1 as the index is zero based.
|
||||
colorTagFiller += colorTagEnd - colorTagStart + 1;
|
||||
|
||||
if (lastWasStart)
|
||||
{
|
||||
colorTagStart = str.LastIndexOf("</color", colorTagStart);
|
||||
lastWasStart = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
colorTagStart = str.LastIndexOf("<color=", colorTagStart);
|
||||
lastWasStart = true;
|
||||
}
|
||||
}
|
||||
return (tidx - colorTagFiller, ridx - colorTagFiller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the zero indexed line number of given <see cref="matchIdx"/>.
|
||||
/// </summary>
|
||||
/// <param name="str">String to search in.</param>
|
||||
/// <param name="matchIdx">Index to find line number of.</param>
|
||||
/// <returns>Line number of given index in string.</returns>
|
||||
internal static int FindLineNr(string str, int matchIdx)
|
||||
{
|
||||
var lineNr = 0;
|
||||
var idxn = str.IndexOf('\n');
|
||||
|
||||
while (idxn != -1 && idxn < matchIdx)
|
||||
{
|
||||
lineNr++;
|
||||
idxn = str.IndexOf('\n', idxn + 1);
|
||||
}
|
||||
|
||||
return lineNr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds first match of <see cref="criteria"/> in given string.
|
||||
/// </summary>
|
||||
/// <param name="str">String to search in.</param>
|
||||
/// <param name="criteria">Search options.</param>
|
||||
/// <param name="regx">Used when <see cref="criteria"/> specifies regex search.</param>
|
||||
/// <param name="startIdx">Index to start the search at.</param>
|
||||
/// <returns>(start index of match, length of match)</returns>
|
||||
internal static (int idx, int length) FindMatch(string str, SearchCriteria criteria, Regex regx, int startIdx = 0)
|
||||
{
|
||||
var idx = -1;
|
||||
var len = 0;
|
||||
|
||||
if (criteria.isRegex)
|
||||
{
|
||||
// regex will have the appropriate options in it if isCaseSensitive or/and isWholeWords is true.
|
||||
var res = regx.Match(str, startIdx);
|
||||
|
||||
if (res.Success) (idx, len) = (res.Index, res.Length);
|
||||
}
|
||||
else if (criteria.isWholeWords)
|
||||
{
|
||||
(idx, len) = (IndexOfWholeWord(str, startIdx, criteria.filter, criteria.isCaseSensitive
|
||||
? StringComparison.InvariantCulture
|
||||
: StringComparison.InvariantCultureIgnoreCase), criteria.filter.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
(idx, len) = (str.IndexOf(criteria.filter, startIdx, criteria.isCaseSensitive
|
||||
? StringComparison.InvariantCulture
|
||||
: StringComparison.InvariantCultureIgnoreCase), criteria.filter.Length);
|
||||
}
|
||||
|
||||
return (idx, len);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds index of <see cref="filter"/> matching for whole words.
|
||||
/// </summary>
|
||||
/// <param name="str">String to search in.</param>
|
||||
/// <param name="startIdx">Index to start search from.</param>
|
||||
/// <param name="filter">Key to search for.</param>
|
||||
/// <param name="opt">Options for string comparison.</param>
|
||||
/// <returns>Index of match or -1.</returns>
|
||||
private static int IndexOfWholeWord(string str, int startIdx, string filter, StringComparison opt)
|
||||
{
|
||||
const string wholeWordMatch = @"\w";
|
||||
|
||||
var j = startIdx;
|
||||
var filterLen = filter.Length;
|
||||
var strLen = str.Length;
|
||||
while (j < strLen && (j = str.IndexOf(filter, j, opt)) >= 0)
|
||||
{
|
||||
var noPrior = true;
|
||||
if (j != 0)
|
||||
{
|
||||
var frontBorder = str[j - 1];
|
||||
noPrior = !Regex.IsMatch(frontBorder.ToString(), wholeWordMatch);
|
||||
}
|
||||
|
||||
var noAfter = true;
|
||||
if (j + filterLen != strLen)
|
||||
{
|
||||
var endBorder = str[j + filterLen];
|
||||
noAfter = !Regex.IsMatch(endBorder.ToString(), wholeWordMatch);
|
||||
}
|
||||
|
||||
if (noPrior && noAfter) return j;
|
||||
|
||||
j++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
internal static class LabeledPopup
|
||||
{
|
||||
// Because the function given to dropdown menu needs takes its parameter
|
||||
// in the form of an object, we need someway to wrap the integer into one.
|
||||
private struct IntegerWrapper
|
||||
{
|
||||
public int Value { get; }
|
||||
|
||||
public IntegerWrapper(int v)
|
||||
{
|
||||
Value = v;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables having several popup menus functioning independently at the same time.
|
||||
/// </summary>
|
||||
private class PopperCallBack
|
||||
{
|
||||
public static PopperCallBack Instance = null;
|
||||
|
||||
/// <summary>
|
||||
/// Name of the event send when an index have been chosen.
|
||||
/// </summary>
|
||||
private const string IndexChangeEventName = "PopperChangingIndex";
|
||||
|
||||
private readonly int _controlID;
|
||||
private int _selectedIdx;
|
||||
private readonly GUIView _view;
|
||||
|
||||
public PopperCallBack(int controlID)
|
||||
{
|
||||
_controlID = controlID;
|
||||
_selectedIdx = -1;
|
||||
_view = GUIView.current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get selection chosen by dropdown menu <see cref="controlID"/>.
|
||||
/// </summary>
|
||||
/// <param name="controlId">ID of popup menu.</param>
|
||||
/// <param name="selectedIdx">Current selected index.</param>
|
||||
/// <returns>
|
||||
/// Either the selected target, or the <see cref="selectedIdx"/>
|
||||
/// if none were chosen yet.
|
||||
/// </returns>
|
||||
internal static int GetSelectionValue(int controlId, int selectedIdx)
|
||||
{
|
||||
var selected = selectedIdx;
|
||||
|
||||
// A command event with message IndexChangeEvent will be sent whenever a choice on
|
||||
// the dropdown menu has been made. So if this is not the case return whatever index was given
|
||||
var evt = Event.current;
|
||||
if (evt.type != EventType.ExecuteCommand || evt.commandName != IndexChangeEventName) return selected;
|
||||
|
||||
// If this is the popup opened right now: Set the selection idx appropriately
|
||||
if (Instance != null && controlId == Instance._controlID)
|
||||
{
|
||||
selected = Instance._selectedIdx;
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets selection on the opened dropdown, and sends an event
|
||||
/// to the view the popup is within.
|
||||
/// </summary>
|
||||
/// <param name="index">Index selected.</param>
|
||||
internal void SetSelection(object index)
|
||||
{
|
||||
_selectedIdx = ((IntegerWrapper)index).Value;
|
||||
_view.SendEvent(EditorGUIUtility.CommandEvent(IndexChangeEventName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name used for getting a controlID for popup menus.
|
||||
/// </summary>
|
||||
private const string LabelControlName = "LabeledPopup";
|
||||
|
||||
/// <summary>
|
||||
/// Create a immediate automatically positioned popup menu.
|
||||
/// </summary>
|
||||
/// <param name="index">Current active selection index.</param>
|
||||
/// <param name="display">Name to display as the button.</param>
|
||||
/// <param name="options">Display name for the dropdown menu.</param>
|
||||
/// <returns>The possibly new active selection index.</returns>
|
||||
public static int Popup(int index, GUIContent display, string[] options)
|
||||
{
|
||||
// GetControlRect so space is reserved for the button, and we get a
|
||||
// position to place the drop down context menu at.
|
||||
var pos = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight,
|
||||
EditorStyles.popup);
|
||||
|
||||
var controlID = GUIUtility.GetControlID(LabelControlName.GetHashCode(), FocusType.Keyboard, pos);
|
||||
|
||||
var selected = PopperCallBack.GetSelectionValue(controlID, index);
|
||||
|
||||
if (GUI.Button(pos, display, EditorStyles.popup))
|
||||
{
|
||||
PopperCallBack.Instance = new PopperCallBack(controlID);
|
||||
|
||||
var menu = new GenericMenu();
|
||||
for (var i = 0; i < options.Length; i++)
|
||||
{
|
||||
var size = options[i];
|
||||
|
||||
menu.AddItem(EditorGUIUtility.TrTextContent(size), i == index, PopperCallBack.Instance.SetSelection, new IntegerWrapper(i));
|
||||
}
|
||||
menu.Popup(pos, index);
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Unity.Burst.Editor",
|
||||
"references": [ "Unity.Burst" ],
|
||||
"includePlatforms": [ "Editor" ],
|
||||
"allowUnsafeCode": true
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Make internals visible for BurstGlobalCompilerOptions
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.CodeGen")]
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Editor")]
|
||||
// Make internals visible to Unity burst tests
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Tests.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Benchmarks")]
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// How the code should be optimized.
|
||||
/// </summary>
|
||||
public enum OptimizeFor
|
||||
{
|
||||
/// <summary>
|
||||
/// The default optimization mode - uses <see cref="OptimizeFor.Balanced"/>.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Optimize for performance - the compiler should make the most optimal binary possible.
|
||||
/// </summary>
|
||||
Performance = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Optimize for size - the compiler should make the smallest binary possible.
|
||||
/// </summary>
|
||||
Size = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Optimize for fast compilation - the compiler should perform some optimization, but take as little time as possible to do it.
|
||||
/// </summary>
|
||||
FastCompilation = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Optimize for balanced compilation - ensuring that good performance is obtained while keeping compile time as low as possible.
|
||||
/// </summary>
|
||||
Balanced = 4,
|
||||
}
|
||||
|
||||
#if !BURST_COMPILER_SHARED
|
||||
// FloatMode and FloatPrecision must be kept in sync with burst.h / Burst.Backend
|
||||
|
||||
/// <summary>
|
||||
/// Represents the floating point optimization mode for compilation.
|
||||
/// </summary>
|
||||
public enum FloatMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the default target floating point mode - <see cref="FloatMode.Strict"/>.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// No floating point optimizations are performed.
|
||||
/// </summary>
|
||||
Strict = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved for future.
|
||||
/// </summary>
|
||||
Deterministic = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Allows algebraically equivalent optimizations (which can alter the results of calculations), it implies :
|
||||
/// <para/> optimizations can assume results and arguments contain no NaNs or +/- Infinity and treat sign of zero as insignificant.
|
||||
/// <para/> optimizations can use reciprocals - 1/x * y , instead of y/x.
|
||||
/// <para/> optimizations can use fused instructions, e.g. madd.
|
||||
/// </summary>
|
||||
Fast = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the floating point precision used for certain builtin operations e.g. sin/cos.
|
||||
/// </summary>
|
||||
public enum FloatPrecision
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the default target floating point precision - <see cref="FloatPrecision.Medium"/>.
|
||||
/// </summary>
|
||||
Standard = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Compute with an accuracy of 1 ULP - highly accurate, but increased runtime as a result, should not be required for most purposes.
|
||||
/// </summary>
|
||||
High = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Compute with an accuracy of 3.5 ULP - considered acceptable accuracy for most tasks.
|
||||
/// </summary>
|
||||
Medium = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Compute with an accuracy lower than or equal to <see cref="FloatPrecision.Medium"/>, with some range restrictions (defined per function).
|
||||
/// </summary>
|
||||
Low = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This attribute is used to tag jobs or function-pointers as being Burst compiled, and optionally set compilation parameters.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Assembly)]
|
||||
public class BurstCompileAttribute : System.Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the float mode of operation for this Burst compilation.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <see cref="FloatMode.Default"/>.
|
||||
/// </value>
|
||||
public FloatMode FloatMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the floating point precision to use for this Burst compilation.
|
||||
/// Allows you to trade accuracy for speed of computation, useful when you don't require much precision.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <see cref="FloatPrecision.Standard"/>.
|
||||
/// </value>
|
||||
public FloatPrecision FloatPrecision { get; set; }
|
||||
|
||||
internal bool? _compileSynchronously;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not to Burst compile the code immediately on first use, or in the background over time.
|
||||
/// </summary>
|
||||
/// <value>The default is <c>false</c>, <c>true</c> will force this code to be compiled synchronously on first invocation.</value>
|
||||
public bool CompileSynchronously
|
||||
{
|
||||
get => _compileSynchronously.HasValue ? _compileSynchronously.Value : false;
|
||||
set => _compileSynchronously = value;
|
||||
}
|
||||
|
||||
internal bool? _debug;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to compile the code in a way that allows it to be debugged.
|
||||
/// If this is set to <c>true</c>, the current implementation disables optimisations on this method
|
||||
/// allowing it to be debugged using a Native debugger.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <c>false</c>.
|
||||
/// </value>
|
||||
public bool Debug
|
||||
{
|
||||
get => _debug.HasValue ? _debug.Value : false;
|
||||
set => _debug = value;
|
||||
}
|
||||
|
||||
internal bool? _disableSafetyChecks;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to disable safety checks for the current job or function pointer.
|
||||
/// If this is set to <c>true</c>, the current job or function pointer will be compiled
|
||||
/// with safety checks disabled unless the global 'Safety Checks/Force On' option is active.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <c>false</c>.
|
||||
/// </value>
|
||||
public bool DisableSafetyChecks
|
||||
{
|
||||
get => _disableSafetyChecks.HasValue ? _disableSafetyChecks.Value : false;
|
||||
set => _disableSafetyChecks = value;
|
||||
}
|
||||
|
||||
internal bool? _disableDirectCall;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean to disable the translation of a static method call as direct call to
|
||||
/// the generated native method. By default, when compiling static methods with Burst and calling
|
||||
/// them from C#, they will be translated to a direct call to the Burst generated method.
|
||||
/// code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <c>false</c>.
|
||||
/// </value>
|
||||
public bool DisableDirectCall
|
||||
{
|
||||
get => _disableDirectCall.HasValue ? _disableDirectCall.Value : false;
|
||||
set => _disableDirectCall = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How should this entry-point be optimized.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The default is <see cref="OptimizeFor.Default"/>.
|
||||
/// </value>
|
||||
public OptimizeFor OptimizeFor { get; set; }
|
||||
|
||||
internal string[] Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tags a struct/method/class as being Burst compiled, with the default <see cref="FloatPrecision"/>, <see cref="FloatMode"/> and <see cref="CompileSynchronously"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [BurstCompile]
|
||||
/// struct MyMethodsAreCompiledByBurstUsingTheDefaultSettings
|
||||
/// {
|
||||
/// //....
|
||||
/// }
|
||||
///</code>
|
||||
/// </example>
|
||||
public BurstCompileAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tags a struct/method/class as being Burst compiled, with the specified <see cref="FloatPrecision"/> and <see cref="FloatMode"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [BurstCompile(FloatPrecision.Low, FloatMode.Fast)]
|
||||
/// struct MyMethodsAreCompiledByBurstWithLowPrecisionAndFastFloatingPointMode
|
||||
/// {
|
||||
/// //....
|
||||
/// }
|
||||
///</code>
|
||||
///</example>
|
||||
/// <param name="floatPrecision">Specify the required floating point precision.</param>
|
||||
/// <param name="floatMode">Specify the required floating point mode.</param>
|
||||
public BurstCompileAttribute(FloatPrecision floatPrecision, FloatMode floatMode)
|
||||
{
|
||||
FloatMode = floatMode;
|
||||
FloatPrecision = floatPrecision;
|
||||
}
|
||||
|
||||
internal BurstCompileAttribute(string[] options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,878 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Scripting;
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// The burst compiler runtime frontend.
|
||||
/// </summary>
|
||||
///
|
||||
public static class BurstCompiler
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if the LoadAdditionalLibrary API is supported by the current version of Unity
|
||||
/// </summary>
|
||||
/// <returns>True if the LoadAdditionalLibrary API can be used by the current version of Unity</returns>
|
||||
public static bool IsLoadAdditionalLibrarySupported()
|
||||
{
|
||||
return IsApiAvailable("LoadBurstLibrary");
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
#if UNITY_EDITOR
|
||||
static unsafe BurstCompiler()
|
||||
{
|
||||
// Store pointers to Log and Compile callback methods.
|
||||
// For more info about why we need to do this, see comments in CallbackStubManager.
|
||||
string GetFunctionPointer<TDelegate>(TDelegate callback)
|
||||
{
|
||||
GCHandle.Alloc(callback); // Ensure delegate is never garbage-collected.
|
||||
var callbackFunctionPointer = Marshal.GetFunctionPointerForDelegate(callback);
|
||||
return "0x" + callbackFunctionPointer.ToInt64().ToString("X16");
|
||||
}
|
||||
|
||||
EagerCompileLogCallbackFunctionPointer = GetFunctionPointer<LogCallbackDelegate>(EagerCompileLogCallback);
|
||||
ManagedResolverFunctionPointer = GetFunctionPointer<ManagedFnPtrResolverDelegate>(ManagedResolverFunction);
|
||||
ProgressCallbackFunctionPointer = GetFunctionPointer<ProgressCallbackDelegate>(ProgressCallback);
|
||||
ProfileBeginCallbackFunctionPointer = GetFunctionPointer<ProfileBeginCallbackDelegate>(ProfileBeginCallback);
|
||||
ProfileEndCallbackFunctionPointer = GetFunctionPointer<ProfileEndCallbackDelegate>(ProfileEndCallback);
|
||||
}
|
||||
#endif
|
||||
|
||||
private class CommandBuilder
|
||||
{
|
||||
private StringBuilder _builder;
|
||||
private bool _hasArgs;
|
||||
|
||||
public CommandBuilder()
|
||||
{
|
||||
_builder = new StringBuilder();
|
||||
_hasArgs = false;
|
||||
}
|
||||
|
||||
public CommandBuilder Begin(string cmd)
|
||||
{
|
||||
_builder.Clear();
|
||||
_hasArgs = false;
|
||||
_builder.Append(cmd);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder With(string arg)
|
||||
{
|
||||
if (!_hasArgs) _builder.Append(' ');
|
||||
_hasArgs = true;
|
||||
_builder.Append(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder With(IntPtr arg)
|
||||
{
|
||||
if (!_hasArgs) _builder.Append(' ');
|
||||
_hasArgs = true;
|
||||
_builder.AppendFormat("0x{0:X16}", arg.ToInt64());
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder And(char sep = '|')
|
||||
{
|
||||
_builder.Append(sep);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string SendToCompiler()
|
||||
{
|
||||
return SendRawCommandToCompiler(_builder.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static CommandBuilder _cmdBuilder;
|
||||
|
||||
private static CommandBuilder BeginCompilerCommand(string cmd)
|
||||
{
|
||||
if (_cmdBuilder == null)
|
||||
{
|
||||
_cmdBuilder = new CommandBuilder();
|
||||
}
|
||||
|
||||
return _cmdBuilder.Begin(cmd);
|
||||
}
|
||||
|
||||
#if BURST_INTERNAL
|
||||
[ThreadStatic]
|
||||
public static Func<object, IntPtr> InternalCompiler;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Internal variable setup by BurstCompilerOptions.
|
||||
/// </summary>
|
||||
#if BURST_INTERNAL
|
||||
|
||||
[ThreadStatic] // As we are changing this boolean via BurstCompilerOptions in btests and we are running multithread tests
|
||||
// we would change a global and it would generate random errors, so specifically for btests, we are using a TLS.
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
static bool _IsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether Burst is enabled.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR || BURST_INTERNAL
|
||||
public static bool IsEnabled => _IsEnabled;
|
||||
#else
|
||||
public static bool IsEnabled => _IsEnabled && BurstCompilerHelper.IsBurstGenerated;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets the global options for the burst compiler.
|
||||
/// </summary>
|
||||
public static readonly BurstCompilerOptions Options = new BurstCompilerOptions(true);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the execution mode for all jobs spawned from now on.
|
||||
/// </summary>
|
||||
/// <param name="mode">Specifiy the required execution mode</param>
|
||||
public static void SetExecutionMode(BurstExecutionEnvironment mode)
|
||||
{
|
||||
Burst.LowLevel.BurstCompilerService.SetCurrentExecutionMode((uint)mode);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieve the current execution mode that is configured.
|
||||
/// </summary>
|
||||
/// <returns>Currently configured execution mode</returns>
|
||||
public static BurstExecutionEnvironment GetExecutionMode()
|
||||
{
|
||||
return (BurstExecutionEnvironment)Burst.LowLevel.BurstCompilerService.GetCurrentExecutionMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile the following delegate with burst and return a new delegate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="delegateMethod"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>NOT AVAILABLE, unsafe to use</remarks>
|
||||
internal static unsafe T CompileDelegate<T>(T delegateMethod) where T : class
|
||||
{
|
||||
// We have added support for runtime CompileDelegate in 2018.2+
|
||||
void* function = Compile(delegateMethod, false);
|
||||
object res = System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer((IntPtr)function, delegateMethod.GetType());
|
||||
return (T)res;
|
||||
}
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private static void VerifyDelegateIsNotMulticast<T>(T delegateMethod) where T : class
|
||||
{
|
||||
var delegateKind = delegateMethod as Delegate;
|
||||
if (delegateKind.GetInvocationList().Length > 1)
|
||||
{
|
||||
throw new InvalidOperationException($"Burst does not support multicast delegates, please use a regular delegate for `{delegateMethod}'");
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private static void VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(T delegateMethod) where T : class
|
||||
{
|
||||
var attrib = delegateMethod.GetType().GetCustomAttribute<System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute>();
|
||||
if (attrib == null || attrib.CallingConvention != CallingConvention.Cdecl)
|
||||
{
|
||||
#if !BURST_INTERNAL
|
||||
UnityEngine.Debug.LogWarning($"The delegate type {delegateMethod.GetType().FullName} should be decorated with [UnmanagedFunctionPointer(CallingConvention.Cdecl)] to ensure runtime interoperabilty between managed code and Burst-compiled code.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT USE - deprecated.
|
||||
/// </summary>
|
||||
/// <param name="burstMethodHandle">The Burst method to compile.</param>
|
||||
/// <param name="managedMethodHandle">The fallback managed method to use.</param>
|
||||
/// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param>
|
||||
/// <returns>Nothing</returns>
|
||||
[Obsolete("This method will be removed in a future version of Burst")]
|
||||
public static unsafe IntPtr CompileILPPMethod(RuntimeMethodHandle burstMethodHandle, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile an IL Post-Processed method.
|
||||
/// </summary>
|
||||
/// <param name="burstMethodHandle">The Burst method to compile.</param>
|
||||
/// <returns>A token that must be passed to <see cref="GetILPPMethodFunctionPointer2"/> to get an actual executable function pointer.</returns>
|
||||
public static unsafe IntPtr CompileILPPMethod2(RuntimeMethodHandle burstMethodHandle)
|
||||
{
|
||||
if (burstMethodHandle.Value == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(burstMethodHandle));
|
||||
}
|
||||
|
||||
OnCompileILPPMethod2?.Invoke();
|
||||
|
||||
var burstMethod = (MethodInfo)MethodBase.GetMethodFromHandle(burstMethodHandle);
|
||||
|
||||
return (IntPtr)Compile(new FakeDelegate(burstMethod), burstMethod, isFunctionPointer: true, isILPostProcessing: true);
|
||||
}
|
||||
|
||||
internal static Action OnCompileILPPMethod2;
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT USE - deprecated.
|
||||
/// </summary>
|
||||
/// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param>
|
||||
/// <returns>Nothing.</returns>
|
||||
[Obsolete("This method will be removed in a future version of Burst")]
|
||||
public static unsafe void* GetILPPMethodFunctionPointer(IntPtr ilppMethod)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For a previous call to <see cref="CompileILPPMethod2"/>, get the actual executable function pointer.
|
||||
/// </summary>
|
||||
/// <param name="ilppMethod">The result of a previous call to <see cref="CompileILPPMethod"/>.</param>
|
||||
/// <param name="managedMethodHandle">The fallback managed method to use.</param>
|
||||
/// <param name="delegateTypeHandle">The type of the delegate used to execute these methods.</param>
|
||||
/// <returns>A pointer into an executable region, for running the function pointer.</returns>
|
||||
public static unsafe void* GetILPPMethodFunctionPointer2(IntPtr ilppMethod, RuntimeMethodHandle managedMethodHandle, RuntimeTypeHandle delegateTypeHandle)
|
||||
{
|
||||
if (ilppMethod == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ilppMethod));
|
||||
}
|
||||
|
||||
if (managedMethodHandle.Value == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(managedMethodHandle));
|
||||
}
|
||||
|
||||
if (delegateTypeHandle.Value == IntPtr.Zero)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(delegateTypeHandle));
|
||||
}
|
||||
|
||||
// If we are in the editor, we need to route a command to the compiler to start compiling the deferred ILPP compilation.
|
||||
// Otherwise if we're in Burst's internal testing, or in a player build, we already actually have the actual executable
|
||||
// pointer address, and we just return that.
|
||||
#if UNITY_EDITOR
|
||||
var managedMethod = (MethodInfo)MethodBase.GetMethodFromHandle(managedMethodHandle);
|
||||
var delegateType = Type.GetTypeFromHandle(delegateTypeHandle);
|
||||
var managedFallbackDelegate = Delegate.CreateDelegate(delegateType, managedMethod);
|
||||
|
||||
var handle = GCHandle.Alloc(managedFallbackDelegate);
|
||||
|
||||
var result =
|
||||
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandILPPCompilation)
|
||||
.With(ilppMethod).And()
|
||||
.With(ManagedResolverFunctionPointer).And()
|
||||
.With(GCHandle.ToIntPtr(handle))
|
||||
.SendToCompiler();
|
||||
|
||||
return new IntPtr(Convert.ToInt64(result, 16)).ToPointer();
|
||||
#else
|
||||
return ilppMethod.ToPointer();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT USE - deprecated.
|
||||
/// </summary>
|
||||
/// <param name="handle">A runtime method handle.</param>
|
||||
/// <returns>Nothing.</returns>
|
||||
[Obsolete("This method will be removed in a future version of Burst")]
|
||||
public static unsafe void* CompileUnsafeStaticMethod(RuntimeMethodHandle handle)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile the following delegate into a function pointer with burst, invokable from a Burst Job or from regular C#.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the delegate of the function pointer</typeparam>
|
||||
/// <param name="delegateMethod">The delegate to compile</param>
|
||||
/// <returns>A function pointer invokable from a Burst Job or from regular C#</returns>
|
||||
public static unsafe FunctionPointer<T> CompileFunctionPointer<T>(T delegateMethod) where T : class
|
||||
{
|
||||
VerifyDelegateIsNotMulticast<T>(delegateMethod);
|
||||
VerifyDelegateHasCorrectUnmanagedFunctionPointerAttribute<T>(delegateMethod);
|
||||
// We have added support for runtime CompileDelegate in 2018.2+
|
||||
void* function = Compile(delegateMethod, true);
|
||||
return new FunctionPointer<T>(new IntPtr(function));
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
internal class StaticTypeReinitAttribute : Attribute
|
||||
{
|
||||
public readonly Type reinitType;
|
||||
|
||||
public StaticTypeReinitAttribute(Type toReinit)
|
||||
{
|
||||
reinitType = toReinit;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void* Compile(object delegateObj, bool isFunctionPointer)
|
||||
{
|
||||
if (!(delegateObj is Delegate)) throw new ArgumentException("object instance must be a System.Delegate", nameof(delegateObj));
|
||||
var delegateMethod = (Delegate)delegateObj;
|
||||
return Compile(delegateMethod, delegateMethod.Method, isFunctionPointer, false);
|
||||
}
|
||||
|
||||
private static unsafe void* Compile(object delegateObj, MethodInfo methodInfo, bool isFunctionPointer, bool isILPostProcessing)
|
||||
{
|
||||
if (delegateObj == null) throw new ArgumentNullException(nameof(delegateObj));
|
||||
|
||||
if (delegateObj.GetType().IsGenericType)
|
||||
{
|
||||
throw new InvalidOperationException($"The delegate type `{delegateObj.GetType()}` must be a non-generic type");
|
||||
}
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
throw new InvalidOperationException($"The method `{methodInfo}` must be static. Instance methods are not supported");
|
||||
}
|
||||
if (methodInfo.IsGenericMethod)
|
||||
{
|
||||
throw new InvalidOperationException($"The method `{methodInfo}` must be a non-generic method");
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
if (isFunctionPointer && !isILPostProcessing &&
|
||||
methodInfo.GetCustomAttributes().All(s => s.GetType().Name != "MonoPInvokeCallbackAttribute"))
|
||||
{
|
||||
UnityEngine.Debug.Log($"The method `{methodInfo}` must have `MonoPInvokeCallback` attribute to be compatible with IL2CPP!");
|
||||
}
|
||||
#endif
|
||||
|
||||
void* function;
|
||||
|
||||
#if BURST_INTERNAL
|
||||
// Internally in Burst tests, we callback the C# method instead
|
||||
function = (void*)InternalCompiler(delegateObj);
|
||||
#else
|
||||
|
||||
Delegate managedFallbackDelegateMethod = null;
|
||||
|
||||
if (!isILPostProcessing)
|
||||
{
|
||||
managedFallbackDelegateMethod = delegateObj as Delegate;
|
||||
}
|
||||
|
||||
var delegateMethod = delegateObj as Delegate;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string defaultOptions;
|
||||
|
||||
// In case Burst is disabled entirely from the command line
|
||||
if (BurstCompilerOptions.ForceDisableBurstCompilation)
|
||||
{
|
||||
if (isILPostProcessing)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
GCHandle.Alloc(managedFallbackDelegateMethod);
|
||||
function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod);
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
if (isILPostProcessing)
|
||||
{
|
||||
defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n";
|
||||
}
|
||||
else if (isFunctionPointer)
|
||||
{
|
||||
defaultOptions = "--" + BurstCompilerOptions.OptionJitIsForFunctionPointer + "\n";
|
||||
// Make sure that the delegate will never be collected
|
||||
var delHandle = GCHandle.Alloc(managedFallbackDelegateMethod);
|
||||
defaultOptions += "--" + BurstCompilerOptions.OptionJitManagedDelegateHandle + "0x" + ManagedResolverFunctionPointer + "|" + "0x" + GCHandle.ToIntPtr(delHandle).ToInt64().ToString("X16");
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultOptions = "--" + BurstCompilerOptions.OptionJitEnableSynchronousCompilation;
|
||||
}
|
||||
|
||||
string extraOptions;
|
||||
// The attribute is directly on the method, so we recover the underlying method here
|
||||
if (Options.TryGetOptions(methodInfo, true, out extraOptions, isForILPostProcessing: isILPostProcessing))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(extraOptions))
|
||||
{
|
||||
defaultOptions += "\n" + extraOptions;
|
||||
}
|
||||
|
||||
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, defaultOptions);
|
||||
function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId);
|
||||
}
|
||||
#else
|
||||
// The attribute is directly on the method, so we recover the underlying method here
|
||||
if (BurstCompilerOptions.HasBurstCompileAttribute(methodInfo))
|
||||
{
|
||||
if (Options.EnableBurstCompilation && BurstCompilerHelper.IsBurstGenerated)
|
||||
{
|
||||
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(delegateObj, string.Empty);
|
||||
function = Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is for direct-call, and we're in a player, with Burst disabled, then we should return null,
|
||||
// since we don't actually have a managedFallbackDelegateMethod at this point.
|
||||
if (isILPostProcessing)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure that the delegate will never be collected
|
||||
GCHandle.Alloc(managedFallbackDelegateMethod);
|
||||
// If we are in a standalone player, and burst is disabled and we are actually
|
||||
// trying to load a function pointer, in that case we need to support it
|
||||
// so we are then going to use the managed function directly
|
||||
// NOTE: When running under IL2CPP, this could lead to a `System.NotSupportedException : To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition.`
|
||||
// so in that case, the method needs to have `MonoPInvokeCallback`
|
||||
// but that's a requirement for IL2CPP, not an issue with burst
|
||||
function = (void*)Marshal.GetFunctionPointerForDelegate(managedFallbackDelegateMethod);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Burst cannot compile the function pointer `{methodInfo}` because the `[BurstCompile]` attribute is missing");
|
||||
}
|
||||
#endif
|
||||
// Should not happen but in that case, we are still trying to generated an error
|
||||
// It can be null if we are trying to compile a function in a standalone player
|
||||
// and the function was not compiled. In that case, we need to output an error
|
||||
if (function == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Burst failed to compile the function pointer `{methodInfo}`");
|
||||
}
|
||||
|
||||
// When burst compilation is disabled, we are still returning a valid stub function pointer (the a pointer to the managed function)
|
||||
// so that CompileFunctionPointer actually returns a delegate in all cases
|
||||
return function;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lets the compiler service know we are shutting down, called by the event on OnDomainUnload, if EditorApplication.quitting was called
|
||||
/// </summary>
|
||||
internal static void Shutdown()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandShutdown);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal static void SetDefaultOptions()
|
||||
{
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetDefaultOptions, Options.GetOptions(true, isForCompilerClient: true));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// We need this to be queried each domain reload in a static constructor so that it is called on the main thread only!
|
||||
internal static readonly bool IsScriptDebugInfoEnabled = UnityEditor.Compilation.CompilationPipeline.IsScriptDebugInfoEnabled();
|
||||
|
||||
private sealed class DomainReloadStateSingleton : UnityEditor.ScriptableSingleton<DomainReloadStateSingleton>
|
||||
{
|
||||
public bool AlreadyLoaded = false;
|
||||
public bool IsScriptDebugInfoEnabled = false;
|
||||
}
|
||||
|
||||
internal static bool WasScriptDebugInfoEnabledAtDomainReload => DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled;
|
||||
|
||||
internal static void DomainReload()
|
||||
{
|
||||
const string parameterSeparator = "***";
|
||||
const string assemblySeparator = "```";
|
||||
|
||||
var isScriptDebugInfoEnabled = IsScriptDebugInfoEnabled;
|
||||
|
||||
var cmdBuilder =
|
||||
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandDomainReload)
|
||||
.With(ProgressCallbackFunctionPointer)
|
||||
.With(parameterSeparator)
|
||||
.With(EagerCompileLogCallbackFunctionPointer)
|
||||
.With(parameterSeparator)
|
||||
.With(isScriptDebugInfoEnabled ? "Debug" : "Release")
|
||||
.With(parameterSeparator);
|
||||
|
||||
// We need to send the list of assemblies if
|
||||
// (a) we have never done that before in this Editor instance, or
|
||||
// (b) we have done it before, but now the scripting code optimization mode has changed
|
||||
// from Debug to Release or vice-versa.
|
||||
// This is because these are the two cases in which CompilerClient will be
|
||||
// destroyed and recreated.
|
||||
if (!DomainReloadStateSingleton.instance.AlreadyLoaded ||
|
||||
DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled != isScriptDebugInfoEnabled)
|
||||
{
|
||||
// Gather list of assemblies to compile (only actually used at Editor startup)
|
||||
var assemblyNames = UnityEditor.Compilation.CompilationPipeline
|
||||
.GetAssemblies(UnityEditor.Compilation.AssembliesType.Editor)
|
||||
.Where(x => File.Exists(x.outputPath)) // If C# compilation fails, it won't exist on disk
|
||||
.Select(x => x.name);
|
||||
|
||||
foreach (var assemblyName in assemblyNames)
|
||||
{
|
||||
cmdBuilder.With(assemblyName)
|
||||
.With(assemblySeparator);
|
||||
}
|
||||
|
||||
DomainReloadStateSingleton.instance.AlreadyLoaded = true;
|
||||
DomainReloadStateSingleton.instance.IsScriptDebugInfoEnabled = IsScriptDebugInfoEnabled;
|
||||
}
|
||||
|
||||
cmdBuilder.SendToCompiler();
|
||||
}
|
||||
|
||||
internal static string VersionNotify(string version)
|
||||
{
|
||||
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandVersionNotification, version);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cancel any compilation being processed by the JIT Compiler in the background.
|
||||
/// </summary>
|
||||
internal static void Cancel()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandCancel);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if there is any job pending related to the last compilation ID.
|
||||
/// </summary>
|
||||
internal static bool IsCurrentCompilationDone()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsCurrentCompilationDone) == "True";
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void Enable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandEnableCompiler);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void Disable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandDisableCompiler);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool IsHostEditorArm()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsArmTestEnv)=="true";
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void TriggerUnsafeStaticMethodRecompilation()
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var reinitAttributes = asm.GetCustomAttributes().Where(
|
||||
x => x.GetType().FullName == "Unity.Burst.BurstCompiler+StaticTypeReinitAttribute"
|
||||
);
|
||||
foreach (var attribute in reinitAttributes)
|
||||
{
|
||||
var ourAttribute = attribute as StaticTypeReinitAttribute;
|
||||
var type = ourAttribute.reinitType;
|
||||
var method = type.GetMethod("Constructor",BindingFlags.Static|BindingFlags.Public);
|
||||
method.Invoke(null, new object[] { });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void TriggerRecompilation()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SetDefaultOptions();
|
||||
|
||||
// This is done separately from CompilerCommandTriggerRecompilation below,
|
||||
// because CompilerCommandTriggerRecompilation will cause all jobs to re-request
|
||||
// their function pointers from Burst, and we need to have actually triggered
|
||||
// compilation by that point.
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerSetupRecompilation);
|
||||
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandTriggerRecompilation, Options.GetOptions(true));
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void UnloadAdditionalLibraries()
|
||||
{
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandUnloadBurstNatives);
|
||||
}
|
||||
|
||||
internal static void InitialiseDebuggerHooks()
|
||||
{
|
||||
if (IsApiAvailable("BurstManagedDebuggerPluginV1"))
|
||||
{
|
||||
SendCommandToCompiler(SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestInitialiseDebuggerCommmand));
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsApiAvailable(string apiName)
|
||||
{
|
||||
return SendCommandToCompiler(BurstCompilerOptions.CompilerCommandIsNativeApiAvailable, apiName) == "True";
|
||||
}
|
||||
|
||||
internal static int RequestSetProtocolVersion(int version)
|
||||
{
|
||||
// Ask editor for the maximum version of the protocol we support, then inform the rest of the systems the negotiated version
|
||||
var editorVersion = SendCommandToCompiler(BurstCompilerOptions.CompilerCommandRequestSetProtocolVersionEditor, $"{version}");
|
||||
if (string.IsNullOrEmpty(editorVersion) || !int.TryParse(editorVersion, out var result))
|
||||
{
|
||||
result=0;
|
||||
}
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandSetProtocolVersionBurst, $"{result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private unsafe delegate void LogCallbackDelegate(void* userData, int logType, byte* message, byte* fileName, int lineNumber);
|
||||
|
||||
private static unsafe void EagerCompileLogCallback(void* userData, int logType, byte* message, byte* fileName, int lineNumber)
|
||||
{
|
||||
if (EagerCompilationLoggingEnabled)
|
||||
{
|
||||
BurstRuntime.Log(message, logType, fileName, lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate IntPtr ManagedFnPtrResolverDelegate(IntPtr handleVal);
|
||||
|
||||
private static IntPtr ManagedResolverFunction(IntPtr handleVal)
|
||||
{
|
||||
var delegateObj = GCHandle.FromIntPtr(handleVal).Target;
|
||||
var fnptr = Marshal.GetFunctionPointerForDelegate(delegateObj);
|
||||
return fnptr;
|
||||
}
|
||||
|
||||
internal static bool EagerCompilationLoggingEnabled = false;
|
||||
|
||||
private static readonly string EagerCompileLogCallbackFunctionPointer;
|
||||
private static readonly string ManagedResolverFunctionPointer;
|
||||
#endif
|
||||
|
||||
internal static void Initialize(string[] assemblyFolders)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var assemblyFoldersSerialized = SafeStringArrayHelper.SerialiseStringArraySafe(assemblyFolders);
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandInitialize, assemblyFoldersSerialized);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void NotifyCompilationStarted(string[] assemblyFolders)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var assemblyFoldersSerialized = SafeStringArrayHelper.SerialiseStringArraySafe(assemblyFolders);
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationStarted, assemblyFoldersSerialized);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void NotifyAssemblyCompilationNotRequired(string assemblyName)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationNotRequired, assemblyName);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void NotifyAssemblyCompilationFinished(string assemblyName)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyAssemblyCompilationFinished, assemblyName);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static void NotifyCompilationFinished()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
SendCommandToCompiler(BurstCompilerOptions.CompilerCommandNotifyCompilationFinished);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static string AotCompilation(string[] assemblyFolders, string[] assemblyRoots, string options)
|
||||
{
|
||||
var result = "failed";
|
||||
#if UNITY_EDITOR
|
||||
result = SendCommandToCompiler(
|
||||
BurstCompilerOptions.CompilerCommandAotCompilation,
|
||||
BurstCompilerOptions.SerialiseCompilationOptionsSafe(assemblyRoots, assemblyFolders, options));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static readonly string ProgressCallbackFunctionPointer;
|
||||
|
||||
private delegate void ProgressCallbackDelegate(int current, int total);
|
||||
|
||||
private static void ProgressCallback(int current, int total)
|
||||
{
|
||||
OnProgress?.Invoke(current, total);
|
||||
}
|
||||
|
||||
internal static event Action<int, int> OnProgress;
|
||||
#endif
|
||||
|
||||
internal static void SetProfilerCallbacks()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
BeginCompilerCommand(BurstCompilerOptions.CompilerCommandSetProfileCallbacks)
|
||||
.With(ProfileBeginCallbackFunctionPointer).And(';')
|
||||
.With(ProfileEndCallbackFunctionPointer)
|
||||
.SendToCompiler();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal delegate void ProfileBeginCallbackDelegate(string markerName, string metadataName, string metadataValue);
|
||||
internal delegate void ProfileEndCallbackDelegate(string markerName);
|
||||
|
||||
private static readonly string ProfileBeginCallbackFunctionPointer;
|
||||
private static readonly string ProfileEndCallbackFunctionPointer;
|
||||
|
||||
private static void ProfileBeginCallback(string markerName, string metadataName, string metadataValue) => OnProfileBegin?.Invoke(markerName, metadataName, metadataValue);
|
||||
private static void ProfileEndCallback(string markerName) => OnProfileEnd?.Invoke(markerName);
|
||||
|
||||
internal static event ProfileBeginCallbackDelegate OnProfileBegin;
|
||||
internal static event ProfileEndCallbackDelegate OnProfileEnd;
|
||||
#endif
|
||||
|
||||
|
||||
private static string SendRawCommandToCompiler(string command)
|
||||
{
|
||||
var results = Unity.Burst.LowLevel.BurstCompilerService.GetDisassembly(DummyMethodInfo, command);
|
||||
if (!string.IsNullOrEmpty(results))
|
||||
return results.TrimStart('\n');
|
||||
return "";
|
||||
}
|
||||
|
||||
private static string SendCommandToCompiler(string commandName, string commandArgs = null)
|
||||
{
|
||||
if (commandName == null) throw new ArgumentNullException(nameof(commandName));
|
||||
|
||||
if (commandArgs == null)
|
||||
{
|
||||
// If there are no arguments then there's no reason to go through the builder
|
||||
return SendRawCommandToCompiler(commandName);
|
||||
}
|
||||
|
||||
// Otherwise use the builder for building the final command
|
||||
return BeginCompilerCommand(commandName)
|
||||
.With(commandArgs)
|
||||
.SendToCompiler();
|
||||
}
|
||||
|
||||
private static readonly MethodInfo DummyMethodInfo = typeof(BurstCompiler).GetMethod(nameof(DummyMethod), BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
/// <summary>
|
||||
/// Dummy empty method for being able to send a command to the compiler
|
||||
/// </summary>
|
||||
private static void DummyMethod() { }
|
||||
|
||||
#if !UNITY_EDITOR && !BURST_INTERNAL
|
||||
/// <summary>
|
||||
/// Internal class to detect at standalone player time if AOT settings were enabling burst.
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
internal static class BurstCompilerHelper
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate bool IsBurstEnabledDelegate();
|
||||
private static readonly IsBurstEnabledDelegate IsBurstEnabledImpl = new IsBurstEnabledDelegate(IsBurstEnabled);
|
||||
|
||||
[BurstCompile]
|
||||
[AOT.MonoPInvokeCallback(typeof(IsBurstEnabledDelegate))]
|
||||
private static bool IsBurstEnabled()
|
||||
{
|
||||
bool result = true;
|
||||
DiscardedMethod(ref result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[BurstDiscard]
|
||||
private static void DiscardedMethod(ref bool value)
|
||||
{
|
||||
value = false;
|
||||
}
|
||||
|
||||
private static unsafe bool IsCompiledByBurst(Delegate del)
|
||||
{
|
||||
var delegateMethodId = Unity.Burst.LowLevel.BurstCompilerService.CompileAsyncDelegateMethod(del, string.Empty);
|
||||
// We don't try to run the method, having a pointer is already enough to tell us that burst was active for AOT settings
|
||||
return Unity.Burst.LowLevel.BurstCompilerService.GetAsyncCompiledAsyncDelegateMethod(delegateMethodId) != (void*)0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether burst was enabled for standalone player, used only at runtime.
|
||||
/// </summary>
|
||||
public static readonly bool IsBurstGenerated = IsCompiledByBurst(IsBurstEnabledImpl);
|
||||
}
|
||||
#endif // !UNITY_EDITOR && !BURST_INTERNAL
|
||||
|
||||
/// <summary>
|
||||
/// Fake delegate class to make BurstCompilerService.CompileAsyncDelegateMethod happy
|
||||
/// so that it can access the underlying static method via the property get_Method.
|
||||
/// So this class is not a delegate.
|
||||
/// </summary>
|
||||
private class FakeDelegate
|
||||
{
|
||||
public FakeDelegate(MethodInfo method)
|
||||
{
|
||||
Method = method;
|
||||
}
|
||||
|
||||
[Preserve]
|
||||
public MethodInfo Method { get; }
|
||||
}
|
||||
|
||||
#else // UNITY_DOTSPLAYER || NET_DOTS
|
||||
|
||||
/// <summary>
|
||||
/// Compile the following delegate into a function pointer with burst, invokable from a Burst Job or from regular C#.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the delegate of the function pointer</typeparam>
|
||||
/// <param name="delegateMethod">The delegate to compile</param>
|
||||
/// <returns>A function pointer invokable from a Burst Job or from regular C#</returns>
|
||||
public static unsafe FunctionPointer<T> CompileFunctionPointer<T>(T delegateMethod) where T : System.Delegate
|
||||
{
|
||||
// Make sure that the delegate will never be collected
|
||||
GCHandle.Alloc(delegateMethod);
|
||||
return new FunctionPointer<T>(Marshal.GetFunctionPointerForDelegate(delegateMethod));
|
||||
}
|
||||
|
||||
internal static bool IsApiAvailable(string apiName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,925 @@
|
|||
// BurstCompiler.Compile is not supported on Tiny/ZeroPlayer
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
#if BURST_COMPILER_SHARED
|
||||
using Burst.Compiler.IL.Helpers;
|
||||
#else
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using Unity.Burst;
|
||||
#endif
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// NOTE: This file is shared via a csproj cs link in Burst.Compiler.IL
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
#endif //!UNITY_DOTSPLAYER && !NET_DOTS
|
||||
namespace Unity.Burst
|
||||
{
|
||||
internal enum GlobalSafetyChecksSettingKind
|
||||
{
|
||||
Off = 0,
|
||||
On = 1,
|
||||
ForceOn = 2,
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
/// <summary>
|
||||
/// Options available at Editor time and partially at runtime to control the behavior of the compilation and to enable/disable burst jobs.
|
||||
/// </summary>
|
||||
#if BURST_COMPILER_SHARED
|
||||
internal sealed partial class BurstCompilerOptionsInternal
|
||||
#else
|
||||
public sealed partial class BurstCompilerOptions
|
||||
#endif
|
||||
{
|
||||
private const string DisableCompilationArg = "--burst-disable-compilation";
|
||||
|
||||
private const string ForceSynchronousCompilationArg = "--burst-force-sync-compilation";
|
||||
|
||||
internal const string DefaultLibraryName = "lib_burst_generated";
|
||||
|
||||
internal const string BurstInitializeName = "burst.initialize";
|
||||
internal const string BurstInitializeExternalsName = "burst.initialize.externals";
|
||||
internal const string BurstInitializeStaticsName = "burst.initialize.statics";
|
||||
|
||||
#if BURST_COMPILER_SHARED || UNITY_EDITOR
|
||||
internal static readonly string DefaultCacheFolder = Path.Combine(Environment.CurrentDirectory, "Library", "BurstCache", "JIT");
|
||||
internal const string DeleteCacheMarkerFileName = "DeleteCache.txt";
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static readonly string BackendNameOverride = Environment.GetEnvironmentVariable("UNITY_BURST_BACKEND_NAME_OVERRIDE");
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Common options used by the compiler
|
||||
// -------------------------------------------------------
|
||||
internal const string OptionBurstcSwitch = "+burstc";
|
||||
internal const string OptionGroup = "group";
|
||||
internal const string OptionPlatform = "platform=";
|
||||
internal const string OptionBackend = "backend=";
|
||||
internal const string OptionGlobalSafetyChecksSetting = "global-safety-checks-setting=";
|
||||
internal const string OptionDisableSafetyChecks = "disable-safety-checks";
|
||||
internal const string OptionDisableOpt = "disable-opt";
|
||||
internal const string OptionFastMath = "fastmath";
|
||||
internal const string OptionTarget = "target=";
|
||||
internal const string OptionOptLevel = "opt-level=";
|
||||
internal const string OptionLogTimings = "log-timings";
|
||||
internal const string OptionOptForSize = "opt-for-size";
|
||||
internal const string OptionFloatPrecision = "float-precision=";
|
||||
internal const string OptionFloatMode = "float-mode=";
|
||||
internal const string OptionBranchProtection = "branch-protection=";
|
||||
internal const string OptionDisableWarnings = "disable-warnings=";
|
||||
internal const string OptionCompilationDefines = "compilation-defines=";
|
||||
internal const string OptionDump = "dump=";
|
||||
internal const string OptionFormat = "format=";
|
||||
internal const string OptionDebugTrap = "debugtrap";
|
||||
internal const string OptionDisableVectors = "disable-vectors";
|
||||
internal const string OptionDebug = "debug=";
|
||||
internal const string OptionDebugMode = "debugMode";
|
||||
internal const string OptionStaticLinkage = "generate-static-linkage-methods";
|
||||
internal const string OptionJobMarshalling = "generate-job-marshalling-methods";
|
||||
internal const string OptionTempDirectory = "temp-folder=";
|
||||
internal const string OptionEnableDirectExternalLinking = "enable-direct-external-linking";
|
||||
internal const string OptionLinkerOptions = "linker-options=";
|
||||
internal const string OptionEnableAutoLayoutFallbackCheck = "enable-autolayout-fallback-check";
|
||||
internal const string OptionGenerateLinkXml = "generate-link-xml=";
|
||||
internal const string OptionMetaDataGeneration = "meta-data-generation=";
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Options used by the Jit and Bcl compilers
|
||||
// -------------------------------------------------------
|
||||
internal const string OptionCacheDirectory = "cache-directory=";
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Options used by the Jit compiler
|
||||
// -------------------------------------------------------
|
||||
internal const string OptionJitDisableFunctionCaching = "disable-function-caching";
|
||||
internal const string OptionJitDisableAssemblyCaching = "disable-assembly-caching";
|
||||
internal const string OptionJitEnableAssemblyCachingLogs = "enable-assembly-caching-logs";
|
||||
internal const string OptionJitEnableSynchronousCompilation = "enable-synchronous-compilation";
|
||||
internal const string OptionJitCompilationPriority = "compilation-priority=";
|
||||
|
||||
internal const string OptionJitIsForFunctionPointer = "is-for-function-pointer";
|
||||
|
||||
internal const string OptionJitManagedFunctionPointer = "managed-function-pointer=";
|
||||
internal const string OptionJitManagedDelegateHandle = "managed-delegate-handle=";
|
||||
|
||||
internal const string OptionEnableInterpreter = "enable-interpreter";
|
||||
|
||||
// -------------------------------------------------------
|
||||
// Options used by the Aot compiler
|
||||
// -------------------------------------------------------
|
||||
internal const string OptionAotAssemblyFolder = "assembly-folder=";
|
||||
internal const string OptionRootAssembly = "root-assembly=";
|
||||
internal const string OptionIncludeRootAssemblyReferences = "include-root-assembly-references=";
|
||||
internal const string OptionAotMethod = "method=";
|
||||
internal const string OptionAotType = "type=";
|
||||
internal const string OptionAotAssembly = "assembly=";
|
||||
internal const string OptionAotOutputPath = "output=";
|
||||
internal const string OptionAotKeepIntermediateFiles = "keep-intermediate-files";
|
||||
internal const string OptionAotNoLink = "nolink";
|
||||
internal const string OptionAotPatchedAssembliesOutputFolder = "patch-assemblies-into=";
|
||||
internal const string OptionAotPinvokeNameToPatch = "pinvoke-name=";
|
||||
internal const string OptionAotExecuteMethodNameToFind = "execute-method-name=";
|
||||
|
||||
internal const string OptionAotOnlyStaticMethods = "only-static-methods";
|
||||
internal const string OptionMethodPrefix = "method-prefix=";
|
||||
internal const string OptionAotNoNativeToolchain = "no-native-toolchain";
|
||||
internal const string OptionAotEmitLlvmObjects = "emit-llvm-objects";
|
||||
internal const string OptionAotKeyFolder = "key-folder=";
|
||||
internal const string OptionAotDecodeFolder = "decode-folder=";
|
||||
internal const string OptionVerbose = "verbose";
|
||||
internal const string OptionValidateExternalToolChain = "validate-external-tool-chain";
|
||||
internal const string OptionCompilerThreads = "threads=";
|
||||
internal const string OptionChunkSize = "chunk-size=";
|
||||
internal const string OptionPrintLogOnMissingPInvokeCallbackAttribute = "print-monopinvokecallbackmissing-message";
|
||||
internal const string OptionOutputMode = "output-mode=";
|
||||
internal const string OptionAlwaysCreateOutput = "always-create-output=";
|
||||
internal const string OptionAotPdbSearchPaths = "pdb-search-paths=";
|
||||
internal const string OptionSafetyChecks = "safety-checks";
|
||||
internal const string OptionLibraryOutputMode = "library-output-mode=";
|
||||
internal const string OptionCompilationId = "compilation-id=";
|
||||
|
||||
internal const string CompilerCommandShutdown = "$shutdown";
|
||||
internal const string CompilerCommandCancel = "$cancel";
|
||||
internal const string CompilerCommandEnableCompiler = "$enable_compiler";
|
||||
internal const string CompilerCommandDisableCompiler = "$disable_compiler";
|
||||
internal const string CompilerCommandSetDefaultOptions = "$set_default_options";
|
||||
internal const string CompilerCommandTriggerSetupRecompilation = "$trigger_setup_recompilation";
|
||||
internal const string CompilerCommandIsCurrentCompilationDone = "$is_current_compilation_done";
|
||||
|
||||
// This one is annoying special - the Unity editor has a detection for this string being in the command and does some
|
||||
// job specific logic - meaning that we **cannot** have this string be present in any other command or bugs will occur.
|
||||
internal const string CompilerCommandTriggerRecompilation = "$trigger_recompilation";
|
||||
internal const string CompilerCommandInitialize = "$initialize";
|
||||
internal const string CompilerCommandDomainReload = "$domain_reload";
|
||||
internal const string CompilerCommandVersionNotification = "$version";
|
||||
internal const string CompilerCommandSetProfileCallbacks = "$set_profile_callbacks";
|
||||
internal const string CompilerCommandUnloadBurstNatives = "$unload_burst_natives";
|
||||
internal const string CompilerCommandIsNativeApiAvailable = "$is_native_api_available";
|
||||
internal const string CompilerCommandILPPCompilation = "$ilpp_compilation";
|
||||
internal const string CompilerCommandIsArmTestEnv = "$is_arm_test_env";
|
||||
internal const string CompilerCommandNotifyAssemblyCompilationNotRequired = "$notify_assembly_compilation_not_required";
|
||||
internal const string CompilerCommandNotifyAssemblyCompilationFinished = "$notify_assembly_compilation_finished";
|
||||
internal const string CompilerCommandNotifyCompilationStarted = "$notify_compilation_started";
|
||||
internal const string CompilerCommandNotifyCompilationFinished = "$notify_compilation_finished";
|
||||
internal const string CompilerCommandAotCompilation = "$aot_compilation";
|
||||
internal const string CompilerCommandRequestInitialiseDebuggerCommmand = "$request_debug_command";
|
||||
internal const string CompilerCommandInitialiseDebuggerCommmand = "$load_debugger_interface";
|
||||
internal const string CompilerCommandRequestSetProtocolVersionEditor = "$request_set_protocol_version_editor";
|
||||
internal const string CompilerCommandSetProtocolVersionBurst = "$set_protocol_version_burst";
|
||||
|
||||
internal static string SerialiseCompilationOptionsSafe(string[] roots, string[] folders, string options)
|
||||
{
|
||||
var finalSerialise = new string[3];
|
||||
finalSerialise[0] = SafeStringArrayHelper.SerialiseStringArraySafe(roots);
|
||||
finalSerialise[1] = SafeStringArrayHelper.SerialiseStringArraySafe(folders);
|
||||
finalSerialise[2] = options;
|
||||
return SafeStringArrayHelper.SerialiseStringArraySafe(finalSerialise);
|
||||
}
|
||||
|
||||
internal static (string[] roots, string[] folders, string options) DeserialiseCompilationOptionsSafe(string from)
|
||||
{
|
||||
var set = SafeStringArrayHelper.DeserialiseStringArraySafe(from);
|
||||
|
||||
return (SafeStringArrayHelper.DeserialiseStringArraySafe(set[0]), SafeStringArrayHelper.DeserialiseStringArraySafe(set[1]), set[2]);
|
||||
}
|
||||
|
||||
// All the following content is exposed to the public interface
|
||||
|
||||
#if !BURST_COMPILER_SHARED
|
||||
// These fields are only setup at startup
|
||||
internal static readonly bool ForceDisableBurstCompilation;
|
||||
private static readonly bool ForceBurstCompilationSynchronously;
|
||||
internal static readonly bool IsSecondaryUnityProcess;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal bool IsInitializing;
|
||||
#endif
|
||||
|
||||
private bool _enableBurstCompilation;
|
||||
private bool _enableBurstCompileSynchronously;
|
||||
private bool _enableBurstSafetyChecks;
|
||||
private bool _enableBurstTimings;
|
||||
private bool _enableBurstDebug;
|
||||
private bool _forceEnableBurstSafetyChecks;
|
||||
|
||||
private BurstCompilerOptions() : this(false)
|
||||
{
|
||||
}
|
||||
|
||||
internal BurstCompilerOptions(bool isGlobal)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
IsInitializing = true;
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
IsGlobal = isGlobal;
|
||||
// By default, burst is enabled as well as safety checks
|
||||
EnableBurstCompilation = true;
|
||||
EnableBurstSafetyChecks = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
IsInitializing = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if this option is the global options that affects menus
|
||||
/// </summary>
|
||||
private bool IsGlobal { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether burst is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => EnableBurstCompilation && !ForceDisableBurstCompilation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean to enable or disable compilation of burst jobs.
|
||||
/// </summary>
|
||||
public bool EnableBurstCompilation
|
||||
{
|
||||
get => _enableBurstCompilation;
|
||||
set
|
||||
{
|
||||
// If we are in the global settings, and we are forcing to no burst compilation
|
||||
if (IsGlobal && ForceDisableBurstCompilation) value = false;
|
||||
|
||||
bool changed = _enableBurstCompilation != value;
|
||||
|
||||
_enableBurstCompilation = value;
|
||||
|
||||
// Modify only JobsUtility.JobCompilerEnabled when modifying global settings
|
||||
if (IsGlobal)
|
||||
{
|
||||
#if !BURST_INTERNAL
|
||||
// We need also to disable jobs as functions are being cached by the job system
|
||||
// and when we ask for disabling burst, we are also asking the job system
|
||||
// to no longer use the cached functions
|
||||
JobsUtility.JobCompilerEnabled = value;
|
||||
#if UNITY_EDITOR
|
||||
if (changed)
|
||||
{
|
||||
// Send the command to the compiler service
|
||||
if (value)
|
||||
{
|
||||
BurstCompiler.Enable();
|
||||
MaybeTriggerRecompilation();
|
||||
}
|
||||
else
|
||||
{
|
||||
BurstCompiler.Disable();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Store the option directly into BurstCompiler.IsEnabled
|
||||
BurstCompiler._IsEnabled = value;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
OnOptionsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean to force the compilation of all burst jobs synchronously.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only available at Editor time. Does not have an impact on player mode.
|
||||
/// </remarks>
|
||||
public bool EnableBurstCompileSynchronously
|
||||
{
|
||||
get => _enableBurstCompileSynchronously;
|
||||
set
|
||||
{
|
||||
bool changed = _enableBurstCompileSynchronously != value;
|
||||
_enableBurstCompileSynchronously = value;
|
||||
if (changed) OnOptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean to enable or disable safety checks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only available at Editor time. Does not have an impact on player mode.
|
||||
/// </remarks>
|
||||
public bool EnableBurstSafetyChecks
|
||||
{
|
||||
get => _enableBurstSafetyChecks;
|
||||
set
|
||||
{
|
||||
bool changed = _enableBurstSafetyChecks != value;
|
||||
|
||||
_enableBurstSafetyChecks = value;
|
||||
if (changed)
|
||||
{
|
||||
OnOptionsChanged();
|
||||
MaybeTriggerRecompilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean to force enable safety checks, irrespective of what
|
||||
/// <c>EnableBurstSafetyChecks</c> is set to, or whether the job or function
|
||||
/// has <c>DisableSafetyChecks</c> set.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only available at Editor time. Does not have an impact on player mode.
|
||||
/// </remarks>
|
||||
public bool ForceEnableBurstSafetyChecks
|
||||
{
|
||||
get => _forceEnableBurstSafetyChecks;
|
||||
set
|
||||
{
|
||||
bool changed = _forceEnableBurstSafetyChecks != value;
|
||||
|
||||
_forceEnableBurstSafetyChecks = value;
|
||||
if (changed)
|
||||
{
|
||||
OnOptionsChanged();
|
||||
MaybeTriggerRecompilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Enable debugging mode
|
||||
/// </summary>
|
||||
public bool EnableBurstDebug
|
||||
{
|
||||
get => _enableBurstDebug;
|
||||
set
|
||||
{
|
||||
bool changed = _enableBurstDebug != value;
|
||||
|
||||
_enableBurstDebug = value;
|
||||
if (changed)
|
||||
{
|
||||
OnOptionsChanged();
|
||||
MaybeTriggerRecompilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This property is no longer used and will be removed in a future major release.
|
||||
/// </summary>
|
||||
[Obsolete("This property is no longer used and will be removed in a future major release")]
|
||||
public bool DisableOptimizations
|
||||
{
|
||||
get => false;
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This property is no longer used and will be removed in a future major release. Use the [BurstCompile(FloatMode = FloatMode.Fast)] on the method directly to enable this feature
|
||||
/// </summary>
|
||||
[Obsolete("This property is no longer used and will be removed in a future major release. Use the [BurstCompile(FloatMode = FloatMode.Fast)] on the method directly to enable this feature")]
|
||||
public bool EnableFastMath
|
||||
{
|
||||
get => true;
|
||||
|
||||
set
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
internal bool EnableBurstTimings
|
||||
{
|
||||
get => _enableBurstTimings;
|
||||
set
|
||||
{
|
||||
bool changed = _enableBurstTimings != value;
|
||||
_enableBurstTimings = value;
|
||||
if (changed) OnOptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool RequiresSynchronousCompilation => EnableBurstCompileSynchronously || ForceBurstCompilationSynchronously;
|
||||
|
||||
internal Action OptionsChanged { get; set; }
|
||||
|
||||
internal BurstCompilerOptions Clone()
|
||||
{
|
||||
// WARNING: for some reason MemberwiseClone() is NOT WORKING on Mono/Unity
|
||||
// so we are creating a manual clone
|
||||
var clone = new BurstCompilerOptions
|
||||
{
|
||||
EnableBurstCompilation = EnableBurstCompilation,
|
||||
EnableBurstCompileSynchronously = EnableBurstCompileSynchronously,
|
||||
EnableBurstSafetyChecks = EnableBurstSafetyChecks,
|
||||
EnableBurstTimings = EnableBurstTimings,
|
||||
EnableBurstDebug = EnableBurstDebug,
|
||||
ForceEnableBurstSafetyChecks = ForceEnableBurstSafetyChecks,
|
||||
};
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static bool TryGetAttribute(MemberInfo member, out BurstCompileAttribute attribute)
|
||||
{
|
||||
attribute = null;
|
||||
// We don't fail if member == null as this method is being called by native code and doesn't expect to crash
|
||||
if (member == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch options from attribute
|
||||
attribute = GetBurstCompileAttribute(member);
|
||||
if (attribute == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryGetAttribute(Assembly assembly, out BurstCompileAttribute attribute)
|
||||
{
|
||||
// We don't fail if assembly == null as this method is being called by native code and doesn't expect to crash
|
||||
if (assembly == null)
|
||||
{
|
||||
attribute = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch options from attribute
|
||||
attribute = assembly.GetCustomAttribute<BurstCompileAttribute>();
|
||||
|
||||
return attribute != null;
|
||||
}
|
||||
|
||||
private static BurstCompileAttribute GetBurstCompileAttribute(MemberInfo memberInfo)
|
||||
{
|
||||
var result = memberInfo.GetCustomAttribute<BurstCompileAttribute>();
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var a in memberInfo.GetCustomAttributes())
|
||||
{
|
||||
var attributeType = a.GetType();
|
||||
if (attributeType.FullName == "Burst.Compiler.IL.Tests.TestCompilerAttribute")
|
||||
{
|
||||
var options = new List<string>();
|
||||
|
||||
return new BurstCompileAttribute(FloatPrecision.Standard, FloatMode.Default)
|
||||
{
|
||||
CompileSynchronously = true,
|
||||
Options = options.ToArray(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static bool HasBurstCompileAttribute(MemberInfo member)
|
||||
{
|
||||
if (member == null) throw new ArgumentNullException(nameof(member));
|
||||
BurstCompileAttribute attr;
|
||||
return TryGetAttribute(member, out attr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the attributes from the assembly into the member attribute, such that if any field of the member attribute
|
||||
/// was not specifically set by the user (or is a default), the assembly level setting is used for the Burst compilation.
|
||||
/// </summary>
|
||||
internal static void MergeAttributes(ref BurstCompileAttribute memberAttribute, in BurstCompileAttribute assemblyAttribute)
|
||||
{
|
||||
if (memberAttribute.FloatMode == FloatMode.Default)
|
||||
{
|
||||
memberAttribute.FloatMode = assemblyAttribute.FloatMode;
|
||||
}
|
||||
|
||||
if (memberAttribute.FloatPrecision == FloatPrecision.Standard)
|
||||
{
|
||||
memberAttribute.FloatPrecision = assemblyAttribute.FloatPrecision;
|
||||
}
|
||||
|
||||
if (memberAttribute.OptimizeFor == OptimizeFor.Default)
|
||||
{
|
||||
memberAttribute.OptimizeFor = assemblyAttribute.OptimizeFor;
|
||||
}
|
||||
|
||||
if (!memberAttribute._compileSynchronously.HasValue && assemblyAttribute._compileSynchronously.HasValue)
|
||||
{
|
||||
memberAttribute._compileSynchronously = assemblyAttribute._compileSynchronously;
|
||||
}
|
||||
|
||||
if (!memberAttribute._debug.HasValue && assemblyAttribute._debug.HasValue)
|
||||
{
|
||||
memberAttribute._debug = assemblyAttribute._debug;
|
||||
}
|
||||
|
||||
if (!memberAttribute._disableDirectCall.HasValue && assemblyAttribute._disableDirectCall.HasValue)
|
||||
{
|
||||
memberAttribute._disableDirectCall = assemblyAttribute._disableDirectCall;
|
||||
}
|
||||
|
||||
if (!memberAttribute._disableSafetyChecks.HasValue && assemblyAttribute._disableSafetyChecks.HasValue)
|
||||
{
|
||||
memberAttribute._disableSafetyChecks = assemblyAttribute._disableSafetyChecks;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the options for the specified member. Returns <c>false</c> if the `[BurstCompile]` attribute was not found.
|
||||
/// </summary>
|
||||
/// <returns><c>false</c> if the `[BurstCompile]` attribute was not found; otherwise <c>true</c></returns>
|
||||
internal bool TryGetOptions(MemberInfo member, bool isJit, out string flagsOut, bool isForILPostProcessing = false)
|
||||
{
|
||||
flagsOut = null;
|
||||
if (!TryGetAttribute(member, out var memberAttribute))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryGetAttribute(member.Module.Assembly, out var assemblyAttribute))
|
||||
{
|
||||
MergeAttributes(ref memberAttribute, in assemblyAttribute);
|
||||
}
|
||||
|
||||
flagsOut = GetOptions(isJit, memberAttribute, isForILPostProcessing);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal string GetOptions(bool isJit, BurstCompileAttribute attr = null, bool isForILPostProcessing = false, bool isForCompilerClient = false)
|
||||
{
|
||||
// Add debug to Jit options instead of passing it here
|
||||
// attr.Debug
|
||||
|
||||
var flagsBuilderOut = new StringBuilder();
|
||||
|
||||
if (isJit && !isForCompilerClient && ((attr?.CompileSynchronously ?? false) || RequiresSynchronousCompilation))
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionJitEnableSynchronousCompilation));
|
||||
}
|
||||
|
||||
if (isJit)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionDebug,
|
||||
#if UNITY_EDITOR
|
||||
BurstCompiler.IsScriptDebugInfoEnabled && EnableBurstDebug ? "Full" : "LineOnly"
|
||||
#else
|
||||
"LineOnly"
|
||||
#endif
|
||||
));
|
||||
}
|
||||
|
||||
if (isForILPostProcessing)
|
||||
{
|
||||
// IL Post Processing compiles are the only thing set to low priority.
|
||||
AddOption(flagsBuilderOut, GetOption(OptionJitCompilationPriority, CompilationPriority.ILPP));
|
||||
}
|
||||
|
||||
if (attr != null)
|
||||
{
|
||||
if (attr.FloatMode != FloatMode.Default)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionFloatMode, attr.FloatMode));
|
||||
}
|
||||
|
||||
if (attr.FloatPrecision != FloatPrecision.Standard)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionFloatPrecision, attr.FloatPrecision));
|
||||
}
|
||||
|
||||
// We disable safety checks for jobs with `[BurstCompile(DisableSafetyChecks = true)]`.
|
||||
if (attr.DisableSafetyChecks)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionDisableSafetyChecks));
|
||||
}
|
||||
|
||||
if (attr.Options != null)
|
||||
{
|
||||
foreach (var option in attr.Options)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
{
|
||||
AddOption(flagsBuilderOut, option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (attr.OptimizeFor)
|
||||
{
|
||||
case OptimizeFor.Default:
|
||||
case OptimizeFor.Balanced:
|
||||
AddOption(flagsBuilderOut, GetOption(OptionOptLevel, 2));
|
||||
break;
|
||||
case OptimizeFor.Performance:
|
||||
AddOption(flagsBuilderOut, GetOption(OptionOptLevel, 3));
|
||||
break;
|
||||
case OptimizeFor.Size:
|
||||
AddOption(flagsBuilderOut, GetOption(OptionOptForSize));
|
||||
AddOption(flagsBuilderOut, GetOption(OptionOptLevel, 3));
|
||||
break;
|
||||
case OptimizeFor.FastCompilation:
|
||||
AddOption(flagsBuilderOut, GetOption(OptionOptLevel, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ForceEnableBurstSafetyChecks)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionGlobalSafetyChecksSetting, GlobalSafetyChecksSettingKind.ForceOn));
|
||||
}
|
||||
else if (EnableBurstSafetyChecks)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionGlobalSafetyChecksSetting, GlobalSafetyChecksSettingKind.On));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionGlobalSafetyChecksSetting, GlobalSafetyChecksSettingKind.Off));
|
||||
}
|
||||
|
||||
if (isJit && EnableBurstTimings)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionLogTimings));
|
||||
}
|
||||
|
||||
if (EnableBurstDebug || (attr?.Debug ?? false))
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionDebugMode));
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (BackendNameOverride != null)
|
||||
{
|
||||
AddOption(flagsBuilderOut, GetOption(OptionBackend, BackendNameOverride));
|
||||
}
|
||||
#endif
|
||||
|
||||
AddOption(flagsBuilderOut, GetOption(OptionTempDirectory, Path.Combine(Environment.CurrentDirectory, "Temp", "Burst")));
|
||||
|
||||
return flagsBuilderOut.ToString();
|
||||
}
|
||||
|
||||
private static void AddOption(StringBuilder builder, string option)
|
||||
{
|
||||
if (builder.Length != 0)
|
||||
builder.Append('\n'); // Use \n to separate options
|
||||
|
||||
builder.Append(option);
|
||||
}
|
||||
internal static string GetOption(string optionName, object value = null)
|
||||
{
|
||||
if (optionName == null) throw new ArgumentNullException(nameof(optionName));
|
||||
return "--" + optionName + (value ?? String.Empty);
|
||||
}
|
||||
|
||||
private void OnOptionsChanged()
|
||||
{
|
||||
OptionsChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void MaybeTriggerRecompilation()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (IsGlobal && IsEnabled && !IsInitializing)
|
||||
{
|
||||
UnityEditor.EditorUtility.DisplayProgressBar("Burst", "Waiting for compilation to finish", -1);
|
||||
try
|
||||
{
|
||||
BurstCompiler.TriggerRecompilation();
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnityEditor.EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
/// <summary>
|
||||
/// Static initializer based on command line arguments
|
||||
/// </summary>
|
||||
static BurstCompilerOptions()
|
||||
{
|
||||
foreach (var arg in Environment.GetCommandLineArgs())
|
||||
{
|
||||
switch (arg)
|
||||
{
|
||||
case DisableCompilationArg:
|
||||
ForceDisableBurstCompilation = true;
|
||||
break;
|
||||
case ForceSynchronousCompilationArg:
|
||||
ForceBurstCompilationSynchronously = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckIsSecondaryUnityProcess())
|
||||
{
|
||||
ForceDisableBurstCompilation = true;
|
||||
IsSecondaryUnityProcess = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CheckIsSecondaryUnityProcess()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Secondary
|
||||
|| UnityEditor.AssetDatabase.IsAssetImportWorkerProcess())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if (UnityEditor.MPE.ProcessService.level == UnityEditor.MPE.ProcessLevel.Slave
|
||||
|| UnityEditor.AssetDatabase.IsAssetImportWorkerProcess())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif // !BURST_COMPILER_SHARED
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// NOTE: This must be synchronized with Backend.TargetPlatform
|
||||
internal enum TargetPlatform
|
||||
{
|
||||
Windows = 0,
|
||||
macOS = 1,
|
||||
Linux = 2,
|
||||
Android = 3,
|
||||
iOS = 4,
|
||||
PS4 = 5,
|
||||
XboxOne_Deprecated = 6,
|
||||
WASM = 7,
|
||||
UWP = 8,
|
||||
Lumin = 9,
|
||||
Switch = 10,
|
||||
Stadia_Deprecated = 11,
|
||||
tvOS = 12,
|
||||
EmbeddedLinux = 13,
|
||||
GameCoreXboxOne = 14,
|
||||
GameCoreXboxSeries = 15,
|
||||
PS5 = 16,
|
||||
QNX = 17,
|
||||
}
|
||||
#endif
|
||||
|
||||
// Need this enum for CPU intrinsics to work, so exposing it to Tiny too
|
||||
#endif //!UNITY_DOTSPLAYER && !NET_DOTS
|
||||
// Don't expose the enum in Burst.Compiler.IL, need only in Unity.Burst.dll which is referenced by Burst.Compiler.IL.Tests
|
||||
#if !BURST_COMPILER_SHARED
|
||||
// Make the enum public for btests via Unity.Burst.dll; leave it internal in the package
|
||||
#if BURST_INTERNAL
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
// NOTE: This must be synchronized with Backend.TargetCpu
|
||||
enum BurstTargetCpu
|
||||
{
|
||||
Auto = 0,
|
||||
X86_SSE2 = 1,
|
||||
X86_SSE4 = 2,
|
||||
X64_SSE2 = 3,
|
||||
X64_SSE4 = 4,
|
||||
AVX = 5,
|
||||
AVX2 = 6,
|
||||
WASM32 = 7,
|
||||
ARMV7A_NEON32 = 8,
|
||||
ARMV8A_AARCH64 = 9,
|
||||
THUMB2_NEON32 = 10,
|
||||
ARMV8A_AARCH64_HALFFP = 11,
|
||||
ARMV9A = 12,
|
||||
}
|
||||
#endif
|
||||
#if !UNITY_DOTSPLAYER && !NET_DOTS
|
||||
|
||||
/// <summary>
|
||||
/// Flags used by <see cref="NativeCompiler.CompileMethod"/> to dump intermediate compiler results.
|
||||
/// Note please ensure MonoDebuggerHandling/Constants.h is updated if you change this enum
|
||||
/// </summary>
|
||||
[Flags]
|
||||
#if BURST_COMPILER_SHARED
|
||||
public enum NativeDumpFlags
|
||||
#else
|
||||
internal enum NativeDumpFlags
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// Nothing is selected.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the IL of the method being compiled
|
||||
/// </summary>
|
||||
IL = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Unused dump state.
|
||||
/// </summary>
|
||||
Unused = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the generated module without optimizations
|
||||
/// </summary>
|
||||
IR = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the generated backend code after optimizations (if enabled)
|
||||
/// </summary>
|
||||
IROptimized = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the generated ASM code
|
||||
/// </summary>
|
||||
Asm = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Generate the native code
|
||||
/// </summary>
|
||||
Function = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the result of analysis
|
||||
/// </summary>
|
||||
Analysis = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the diagnostics from optimisation
|
||||
/// </summary>
|
||||
IRPassAnalysis = 1 << 7,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the IL before all transformation of the method being compiled
|
||||
/// </summary>
|
||||
ILPre = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps the per-entry-point module
|
||||
/// </summary>
|
||||
IRPerEntryPoint = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all normal output.
|
||||
/// </summary>
|
||||
All = IL | ILPre | IR | IROptimized | IRPerEntryPoint | Asm | Function | Analysis | IRPassAnalysis
|
||||
}
|
||||
|
||||
#if BURST_COMPILER_SHARED
|
||||
public enum CompilationPriority
|
||||
#else
|
||||
internal enum CompilationPriority
|
||||
#endif
|
||||
{
|
||||
EagerCompilationSynchronous = 0,
|
||||
Asynchronous = 1,
|
||||
ILPP = 2,
|
||||
EagerCompilationAsynchronous = 3,
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Some options cannot be applied until after an Editor restart, in Editor versions prior to 2019.3.
|
||||
/// This class assists with allowing the relevant settings to be changed via the menu,
|
||||
/// followed by displaying a message to the user to say a restart is necessary.
|
||||
/// </summary>
|
||||
internal static class RequiresRestartUtility
|
||||
{
|
||||
[ThreadStatic]
|
||||
public static bool CalledFromUI;
|
||||
|
||||
[ThreadStatic]
|
||||
public static bool RequiresRestart;
|
||||
}
|
||||
#endif
|
||||
#endif //!UNITY_DOTSPLAYER && !NET_DOTS
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#if UNITY_2019_3_OR_NEWER
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the types of compiled code that are run on the current thread.
|
||||
/// </summary>
|
||||
public enum BurstExecutionEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// Use the default (aka FloatMode specified via Compile Attribute - <see cref="FloatMode"/>
|
||||
/// </summary>
|
||||
Default=0,
|
||||
/// <summary>
|
||||
/// Override the specified float mode and run the non deterministic version
|
||||
/// </summary>
|
||||
NonDeterministic=0,
|
||||
/// <summary>
|
||||
/// Override the specified float mode and run the deterministic version
|
||||
/// </summary>
|
||||
Deterministic=1,
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,196 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helper intrinsics that can be used at runtime.
|
||||
/// </summary>
|
||||
#if BURST_COMPILER_SHARED
|
||||
internal static class BurstRuntimeInternal
|
||||
#else
|
||||
public static class BurstRuntime
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a 32-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to compute the hash from</typeparam>
|
||||
/// <returns>The 32-bit hashcode.</returns>
|
||||
public static int GetHashCode32<T>()
|
||||
{
|
||||
#if !UNITY_DOTSRUNTIME_IL2CPP
|
||||
return HashCode32<T>.Value;
|
||||
#else
|
||||
// DOTS Runtime IL2CPP Builds do not use C#'s lazy static initialization order (it uses a C like order, aka random)
|
||||
// As such we cannot rely on static init for caching types since any static constructor calling this function
|
||||
// may return uninitialized/default-initialized memory
|
||||
return HashStringWithFNV1A32(typeof(T).AssemblyQualifiedName);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a 32-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/>
|
||||
/// This method cannot be used from a burst job.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to compute the hash from</param>
|
||||
/// <returns>The 32-bit hashcode.</returns>
|
||||
public static int GetHashCode32(Type type)
|
||||
{
|
||||
return HashStringWithFNV1A32(type.AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a 64-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to compute the hash from</typeparam>
|
||||
/// <returns>The 64-bit hashcode.</returns>
|
||||
public static long GetHashCode64<T>()
|
||||
{
|
||||
#if !UNITY_DOTSRUNTIME_IL2CPP
|
||||
return HashCode64<T>.Value;
|
||||
#else
|
||||
// DOTS Runtime IL2CPP Builds do not use C#'s lazy static initialization order (it uses a C like order, aka random)
|
||||
// As such we cannot rely on static init for caching types since any static constructor calling this function
|
||||
// may return uninitialized/default-initialized memory
|
||||
return HashStringWithFNV1A64(typeof(T).AssemblyQualifiedName);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a 64-bits hashcode from a type computed for the <see cref="System.Type.AssemblyQualifiedName"/>.
|
||||
/// This method cannot be used from a burst job.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to calculate a hash for</param>
|
||||
/// <returns>The 64-bit hashcode.</returns>
|
||||
public static long GetHashCode64(Type type)
|
||||
{
|
||||
return HashStringWithFNV1A64(type.AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
// method internal as it is used by the compiler directly
|
||||
internal static int HashStringWithFNV1A32(string text)
|
||||
{
|
||||
// Using http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a
|
||||
// with basis and prime:
|
||||
const uint offsetBasis = 2166136261;
|
||||
const uint prime = 16777619;
|
||||
|
||||
uint result = offsetBasis;
|
||||
foreach (var c in text)
|
||||
{
|
||||
result = prime * (result ^ (byte)(c & 255));
|
||||
result = prime * (result ^ (byte)(c >> 8));
|
||||
}
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
// method internal as it is used by the compiler directly
|
||||
// WARNING: This **must** be kept in sync with the definition in ILPostProcessing.cs!
|
||||
internal static long HashStringWithFNV1A64(string text)
|
||||
{
|
||||
// Using http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a
|
||||
// with basis and prime:
|
||||
const ulong offsetBasis = 14695981039346656037;
|
||||
const ulong prime = 1099511628211;
|
||||
|
||||
ulong result = offsetBasis;
|
||||
|
||||
foreach (var c in text)
|
||||
{
|
||||
result = prime * (result ^ (byte)(c & 255));
|
||||
result = prime * (result ^ (byte)(c >> 8));
|
||||
}
|
||||
|
||||
return (long)result;
|
||||
}
|
||||
|
||||
private struct HashCode32<T>
|
||||
{
|
||||
public static readonly int Value = HashStringWithFNV1A32(typeof(T).AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
private struct HashCode64<T>
|
||||
{
|
||||
public static readonly long Value = HashStringWithFNV1A64(typeof(T).AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
#if !BURST_COMPILER_SHARED
|
||||
|
||||
/// <summary>
|
||||
/// Allows for loading additional Burst native libraries
|
||||
/// Important: Designed for Play mode / Desktop Standalone Players ONLY
|
||||
/// In Editor, any libraries that have been loaded will be unloaded on exit of playmode
|
||||
/// Only supported from 2021.1 and later. You can use BurstCompiler.IsLoadAdditionalLibrarySupported() to confirm it is available.
|
||||
/// </summary>
|
||||
/// <param name="pathToLibBurstGenerated">Absolute filesystem location of bursted library to load</param>
|
||||
/// <returns>true if the library was loaded successfully</returns>
|
||||
public static bool LoadAdditionalLibrary(string pathToLibBurstGenerated)
|
||||
{
|
||||
if (BurstCompiler.IsLoadAdditionalLibrarySupported())
|
||||
{
|
||||
return LoadAdditionalLibraryInternal(pathToLibBurstGenerated);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool LoadAdditionalLibraryInternal(string pathToLibBurstGenerated)
|
||||
{
|
||||
#if !UNITY_DOTSRUNTIME
|
||||
return (bool)typeof(Unity.Burst.LowLevel.BurstCompilerService).GetMethod("LoadBurstLibrary").Invoke(null, new object[] { pathToLibBurstGenerated });
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
[Preserve]
|
||||
internal static unsafe void RuntimeLog(byte* message, int logType, byte* fileName, int lineNumber)
|
||||
{
|
||||
Unity.Burst.LowLevel.BurstCompilerService.RuntimeLog((byte*) 0, (Unity.Burst.LowLevel.BurstCompilerService.BurstLogType)logType, message, fileName, lineNumber);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
// Prevent BurstCompilerService.Log from being stripped, introduce PreserveAttribute to avoid
|
||||
//requiring a unityengine using directive, il2cpp will see the attribute and know to not strip
|
||||
//the Log method and its BurstCompilerService.Log dependency
|
||||
internal class PreserveAttribute : System.Attribute {}
|
||||
|
||||
[Preserve]
|
||||
internal static void PreventRequiredAttributeStrip()
|
||||
{
|
||||
new BurstDiscardAttribute();
|
||||
// We also need to retain [Condition("UNITY_ASSERTION")] attributes in order to compile
|
||||
// some assertion correctly (i.e. not compile them)
|
||||
new ConditionalAttribute("HEJSA");
|
||||
}
|
||||
|
||||
[Preserve]
|
||||
internal static unsafe void Log(byte* message, int logType, byte* fileName, int lineNumber)
|
||||
{
|
||||
Unity.Burst.LowLevel.BurstCompilerService.Log((byte*) 0, (Unity.Burst.LowLevel.BurstCompilerService.BurstLogType)logType, message, (byte*) 0, lineNumber);
|
||||
}
|
||||
|
||||
#endif // !BURST_COMPILER_SHARED
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return a pointer to read-only memory consisting of the literal UTF-8 bytes of a string constant.
|
||||
/// </summary>
|
||||
/// <param name="str">A string which must a string literal</param>
|
||||
/// <param name="byteCount">Receives the number of UTF-8 encoded bytes the constant contains (excluding null terminator)</param>
|
||||
/// <returns>A pointer to constant data representing the UTF-8 encoded bytes of the string literal, terminated with a null terminator</returns>
|
||||
public unsafe static byte* GetUTF8LiteralPointer(string str, out int byteCount)
|
||||
{
|
||||
throw new NotImplementedException("This function only works from Burst");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,72 @@
|
|||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Compile-time aliasing intrinsics.
|
||||
/// </summary>
|
||||
public static class Aliasing
|
||||
{
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b do not alias.
|
||||
/// </summary>
|
||||
/// <param name="a">A pointer to do aliasing checks on.</param>
|
||||
/// <param name="b">A pointer to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectAliased(void* a, void* b) { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b do not alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="A">The type of a.</typeparam>
|
||||
/// <typeparam name="B">The type of b.</typeparam>
|
||||
/// <param name="a">A reference to do aliasing checks on.</param>
|
||||
/// <param name="b">A reference to do aliasing checks on.</param>
|
||||
public static void ExpectAliased<A, B>(in A a, in B b) where A : struct where B : struct { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b do not alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="B">The type of b.</typeparam>
|
||||
/// <param name="a">A pointer to do aliasing checks on.</param>
|
||||
/// <param name="b">A reference to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectAliased<B>(void* a, in B b) where B : struct { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b do not alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="A">The type of a.</typeparam>
|
||||
/// <param name="a">A reference to do aliasing checks on.</param>
|
||||
/// <param name="b">A pointer to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectAliased<A>(in A a, void* b) where A : struct { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b can alias.
|
||||
/// </summary>
|
||||
/// <param name="a">A pointer to do aliasing checks on.</param>
|
||||
/// <param name="b">A pointer to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectNotAliased(void* a, void* b) { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b can alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="A">The type of a.</typeparam>
|
||||
/// <typeparam name="B">The type of b.</typeparam>
|
||||
/// <param name="a">A reference to do aliasing checks on.</param>
|
||||
/// <param name="b">A reference to do aliasing checks on.</param>
|
||||
public static void ExpectNotAliased<A, B>(in A a, in B b) where A : struct where B : struct { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b can alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="B">The type of b.</typeparam>
|
||||
/// <param name="a">A pointer to do aliasing checks on.</param>
|
||||
/// <param name="b">A reference to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectNotAliased<B>(void* a, in B b) where B : struct { }
|
||||
|
||||
/// <summary>
|
||||
/// Will cause a compiler error in Burst-compiled code if a and b can alias.
|
||||
/// </summary>
|
||||
/// <typeparam name="A">The type of a.</typeparam>
|
||||
/// <param name="a">A reference to do aliasing checks on.</param>
|
||||
/// <param name="b">A pointer to do aliasing checks on.</param>
|
||||
public static unsafe void ExpectNotAliased<A>(in A a, void* b) where A : struct { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Can be used to specify that a parameter or return has a range assumption.
|
||||
/// Assumptions feed directly into the optimizer and allow better codegen.
|
||||
///
|
||||
/// Only usable on values of type scalar integer.
|
||||
///
|
||||
/// The range is a closed interval [min..max] - EG. the attributed value
|
||||
/// is greater-than-or-equal-to min and less-than-or-equal-to max.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
|
||||
public class AssumeRangeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Assume that an integer is in the signed closed interval [min..max].
|
||||
/// </summary>
|
||||
/// <param name="min">The inclusive minimum value.</param>
|
||||
/// <param name="max">The inclusive maximum value.</param>
|
||||
public AssumeRangeAttribute(long min, long max) { }
|
||||
|
||||
/// <summary>
|
||||
/// Assume that an integer is in the unsigned closed interval [min..max].
|
||||
/// </summary>
|
||||
/// <param name="min">The inclusive minimum value.</param>
|
||||
/// <param name="max">The inclusive maximum value.</param>
|
||||
public AssumeRangeAttribute(ulong min, ulong max) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Compile-time queries intrinsics.
|
||||
/// </summary>
|
||||
public static class Constant
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs a compile-time check on whether the provided argument is known to be constant by Burst.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="t">The value to check whether it is constant.</param>
|
||||
/// <returns>True if Burst knows at compile-time that it is a constant, false otherwise.</returns>
|
||||
public static bool IsConstantExpression<T>(T t) where T : unmanaged => false;
|
||||
|
||||
/// <summary>
|
||||
/// Performs a compile-time check on whether the provided argument is known to be constant by Burst.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to check whether it is constant.</param>
|
||||
/// <returns>True if Burst knows at compile-time that it is a constant, false otherwise.</returns>
|
||||
public static unsafe bool IsConstantExpression(void* t) => false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Compile-time hint intrinsics.
|
||||
/// </summary>
|
||||
public static class Hint
|
||||
{
|
||||
/// <summary>
|
||||
/// Hints to the compiler that the condition is likely to be true.
|
||||
/// </summary>
|
||||
/// <param name="condition">The boolean condition that is likely to be true.</param>
|
||||
/// <returns>The condition.</returns>
|
||||
public static bool Likely(bool condition) => condition;
|
||||
|
||||
/// <summary>
|
||||
/// Hints to the compiler that the condition is unlikely to be true.
|
||||
/// </summary>
|
||||
/// <param name="condition">The boolean condition that is unlikely to be true.</param>
|
||||
/// <returns>The condition.</returns>
|
||||
public static bool Unlikely(bool condition) => condition;
|
||||
|
||||
/// <summary>
|
||||
/// Hints to the compiler that the condition can be assumed to be true.
|
||||
/// </summary>
|
||||
/// <param name="condition">The boolean condition that can be assumed to be true.</param>
|
||||
public static void Assume(bool condition) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Can be used to specify that a warning produced by Burst for a given
|
||||
/// method should be ignored.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class IgnoreWarningAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore a single warning produced by Burst.
|
||||
/// </summary>
|
||||
/// <param name="warning">The warning to ignore.</param>
|
||||
public IgnoreWarningAttribute(int warning) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
#if UNITY_BURST_EXPERIMENTAL_LOOP_INTRINSICS
|
||||
public static class Loop
|
||||
{
|
||||
/// <summary>
|
||||
/// Must be called from inside a loop.
|
||||
/// Will cause a compiler error in Burst-compiled code if the loop is not auto-vectorized.
|
||||
/// </summary>
|
||||
public static void ExpectVectorized() { }
|
||||
|
||||
/// <summary>
|
||||
/// Must be called from inside a loop.
|
||||
/// Will cause a compiler error in Burst-compiled code if the loop is auto-vectorized.
|
||||
/// </summary>
|
||||
public static void ExpectNotVectorized() { }
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst.CompilerServices.Spmd
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that multiple calls to a method act as if they are
|
||||
/// executing in a Single Program, Multiple Data (SPMD) paradigm.
|
||||
/// </summary>
|
||||
#if UNITY_BURST_EXPERIMENTAL_SPMD_ATTRIBUTE
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class SpmdAttribute : Attribute
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst.CompilerServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Skip zero-initialization of local variables.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class SkipLocalsInitAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the possible diagnostic IDs.
|
||||
/// </summary>
|
||||
#if BURST_COMPILER_SHARED
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
enum DiagnosticId
|
||||
{
|
||||
// General
|
||||
ERR_InternalCompilerErrorInBackend = 100,
|
||||
ERR_InternalCompilerErrorInFunction = 101,
|
||||
ERR_InternalCompilerErrorInInstruction = 102,
|
||||
|
||||
// ILBuilder
|
||||
ERR_OnlyStaticMethodsAllowed = 1000,
|
||||
ERR_UnableToAccessManagedMethod = 1001,
|
||||
ERR_UnableToFindInterfaceMethod = 1002,
|
||||
|
||||
// ILCompiler
|
||||
ERR_UnexpectedEmptyMethodBody = 1003,
|
||||
ERR_ManagedArgumentsNotSupported = 1004,
|
||||
// ERR_TryConstructionNotSupported = 1005, // not used anymore
|
||||
ERR_CatchConstructionNotSupported = 1006,
|
||||
ERR_CatchAndFilterConstructionNotSupported = 1007,
|
||||
ERR_LdfldaWithFixedArrayExpected = 1008,
|
||||
ERR_PointerExpected = 1009,
|
||||
ERR_LoadingFieldFromManagedObjectNotSupported = 1010,
|
||||
ERR_LoadingFieldWithManagedTypeNotSupported = 1011,
|
||||
ERR_LoadingArgumentWithManagedTypeNotSupported = 1012,
|
||||
ERR_CallingBurstDiscardMethodWithReturnValueNotSupported = 1015,
|
||||
ERR_CallingManagedMethodNotSupported = 1016,
|
||||
//ERR_BinaryPointerOperationNotSupported = 1017,
|
||||
//ERR_AddingPointersWithNonPointerResultNotSupported = 1018,
|
||||
ERR_InstructionUnboxNotSupported = 1019,
|
||||
ERR_InstructionBoxNotSupported = 1020,
|
||||
ERR_InstructionNewobjWithManagedTypeNotSupported = 1021,
|
||||
ERR_AccessingManagedArrayNotSupported = 1022,
|
||||
ERR_InstructionLdtokenFieldNotSupported = 1023,
|
||||
ERR_InstructionLdtokenMethodNotSupported = 1024,
|
||||
ERR_InstructionLdtokenTypeNotSupported = 1025,
|
||||
ERR_InstructionLdtokenNotSupported = 1026,
|
||||
ERR_InstructionLdvirtftnNotSupported = 1027,
|
||||
ERR_InstructionNewarrNotSupported = 1028,
|
||||
ERR_InstructionRethrowNotSupported = 1029,
|
||||
ERR_InstructionCastclassNotSupported = 1030,
|
||||
//ERR_InstructionIsinstNotSupported = 1031,
|
||||
ERR_InstructionLdftnNotSupported = 1032,
|
||||
ERR_InstructionLdstrNotSupported = 1033,
|
||||
ERR_InstructionStsfldNotSupported = 1034,
|
||||
ERR_InstructionEndfilterNotSupported = 1035,
|
||||
ERR_InstructionEndfinallyNotSupported = 1036,
|
||||
ERR_InstructionLeaveNotSupported = 1037,
|
||||
ERR_InstructionNotSupported = 1038,
|
||||
ERR_LoadingFromStaticFieldNotSupported = 1039,
|
||||
ERR_LoadingFromNonReadonlyStaticFieldNotSupported = 1040,
|
||||
ERR_LoadingFromManagedStaticFieldNotSupported = 1041,
|
||||
ERR_LoadingFromManagedNonReadonlyStaticFieldNotSupported = 1042,
|
||||
ERR_InstructionStfldToManagedObjectNotSupported = 1043,
|
||||
ERR_InstructionLdlenNonConstantLengthNotSupported = 1044,
|
||||
ERR_StructWithAutoLayoutNotSupported = 1045,
|
||||
//ERR_StructWithPackNotSupported = 1046,
|
||||
ERR_StructWithGenericParametersAndExplicitLayoutNotSupported = 1047,
|
||||
ERR_StructSizeNotSupported = 1048,
|
||||
ERR_StructZeroSizeNotSupported = 1049,
|
||||
ERR_MarshalAsOnFieldNotSupported = 1050,
|
||||
ERR_TypeNotSupported = 1051,
|
||||
ERR_RequiredTypeModifierNotSupported = 1052,
|
||||
ERR_ErrorWhileProcessingVariable = 1053,
|
||||
|
||||
// CecilExtensions
|
||||
ERR_UnableToResolveType = 1054,
|
||||
|
||||
// ILFunctionReference
|
||||
ERR_UnableToResolveMethod = 1055,
|
||||
ERR_ConstructorNotSupported = 1056,
|
||||
ERR_FunctionPointerMethodMissingBurstCompileAttribute = 1057,
|
||||
ERR_FunctionPointerTypeMissingBurstCompileAttribute = 1058,
|
||||
ERR_FunctionPointerMethodAndTypeMissingBurstCompileAttribute = 1059,
|
||||
INF_FunctionPointerMethodAndTypeMissingMonoPInvokeCallbackAttribute = 10590,
|
||||
|
||||
// ILVisitor
|
||||
// ERR_EntryPointFunctionCannotBeCalledInternally = 1060, // no longer used
|
||||
|
||||
// ExternalFunctionParameterChecks
|
||||
ERR_MarshalAsOnParameterNotSupported = 1061,
|
||||
ERR_MarshalAsOnReturnTypeNotSupported = 1062,
|
||||
ERR_TypeNotBlittableForFunctionPointer = 1063,
|
||||
ERR_StructByValueNotSupported = 1064,
|
||||
ERR_StructsWithNonUnicodeCharsNotSupported = 1066,
|
||||
ERR_VectorsByValueNotSupported = 1067,
|
||||
|
||||
// JitCompiler
|
||||
ERR_MissingExternBindings = 1068,
|
||||
|
||||
// More ExternalFunctionParameterChecks
|
||||
ERR_MarshalAsNativeTypeOnReturnTypeNotSupported = 1069,
|
||||
|
||||
// AssertProcessor
|
||||
ERR_AssertTypeNotSupported = 1071,
|
||||
|
||||
// ReadOnlyProcessor
|
||||
ERR_StoringToReadOnlyFieldNotAllowed = 1072,
|
||||
ERR_StoringToFieldInReadOnlyParameterNotAllowed = 1073,
|
||||
ERR_StoringToReadOnlyParameterNotAllowed = 1074,
|
||||
|
||||
// TypeManagerProcessor
|
||||
ERR_TypeManagerStaticFieldNotCompatible = 1075,
|
||||
ERR_UnableToFindTypeIndexForTypeManagerType = 1076,
|
||||
ERR_UnableToFindFieldForTypeManager = 1077,
|
||||
|
||||
// Deprecated NoAliasAnalyzer
|
||||
// WRN_DisablingNoaliasUnsupportedLdobjImplicitNativeContainer = 1078,
|
||||
// WRN_DisablingNoaliasLoadingDirectlyFromFieldOfNativeArray = 1079,
|
||||
// WRN_DisablingNoaliasWritingDirectlyToFieldOfNativeArray = 1080,
|
||||
// WRN_DisablingNoaliasStoringImplicitNativeContainerToField = 1081,
|
||||
// WRN_DisablingNoaliasStoringImplicitNativeContainerToLocalVariable = 1082,
|
||||
// WRN_DisablingNoaliasStoringImplicitNativeContainerToPointer = 1083,
|
||||
// WRN_DisablingNoaliasCannotLoadNativeContainerAsBothArgumentAndField = 1084,
|
||||
// WRN_DisablingNoaliasSameArgumentPath = 1085,
|
||||
// WRN_DisablingNoaliasCannotPassMultipleNativeContainersConcurrently = 1086,
|
||||
// WRN_DisablingNoaliasUnsupportedNativeArrayUnsafeUtilityMethod = 1087,
|
||||
// WRN_DisablingNoaliasUnsupportedNativeArrayMethod = 1088,
|
||||
// WRN_DisablingNoaliasUnsupportedThisArgument = 1089,
|
||||
|
||||
// StaticFieldAccessTransform
|
||||
ERR_CircularStaticConstructorUsage = 1090,
|
||||
ERR_ExternalInternalCallsInStaticConstructorsNotSupported = 1091,
|
||||
|
||||
// AotCompiler
|
||||
ERR_PlatformNotSupported = 1092,
|
||||
ERR_InitModuleVerificationError = 1093,
|
||||
|
||||
// NativeCompiler
|
||||
ERR_ModuleVerificationError = 1094,
|
||||
|
||||
// TypeManagerProcessor
|
||||
ERR_UnableToFindTypeRequiredForTypeManager = 1095,
|
||||
|
||||
// ILBuilder
|
||||
ERR_UnexpectedIntegerTypesForBinaryOperation = 1096,
|
||||
ERR_BinaryOperationNotSupported = 1097,
|
||||
ERR_CalliWithThisNotSupported = 1098,
|
||||
ERR_CalliNonCCallingConventionNotSupported = 1099,
|
||||
ERR_StringLiteralTooBig = 1100,
|
||||
ERR_LdftnNonCCallingConventionNotSupported = 1101,
|
||||
|
||||
// CheckIntrinsicUsageTransform
|
||||
ERR_InstructionTargetCpuFeatureNotAllowedInThisBlock = 1200,
|
||||
|
||||
// AssumeRange
|
||||
ERR_AssumeRangeTypeMustBeInteger = 1201,
|
||||
ERR_AssumeRangeTypeMustBeSameSign = 1202,
|
||||
|
||||
// LdfldaTransform
|
||||
ERR_UnsupportedSpillTransform = 1300,
|
||||
ERR_UnsupportedSpillTransformTooManyUsers = 1301,
|
||||
|
||||
// Intrinsics
|
||||
ERR_MethodNotSupported = 1302,
|
||||
ERR_VectorsLoadFieldIsAddress = 1303,
|
||||
ERR_ConstantExpressionRequired = 1304,
|
||||
|
||||
// UBAA
|
||||
ERR_PointerArgumentsUnexpectedAliasing = 1310,
|
||||
|
||||
// Loop intrinsics
|
||||
ERR_LoopIntrinsicMustBeCalledInsideLoop = 1320,
|
||||
ERR_LoopUnexpectedAutoVectorization = 1321,
|
||||
WRN_LoopIntrinsicCalledButLoopOptimizedAway = 1322,
|
||||
|
||||
// AssertProcessor
|
||||
ERR_AssertArgTypesDiffer = 1330,
|
||||
|
||||
// StringUsageTransform
|
||||
ERR_StringInternalCompilerFixedStringTooManyUsers = 1340,
|
||||
ERR_StringInvalidFormatMissingClosingBrace = 1341,
|
||||
ERR_StringInvalidIntegerForArgumentIndex = 1342,
|
||||
ERR_StringInvalidFormatForArgument = 1343,
|
||||
ERR_StringArgumentIndexOutOfRange = 1344,
|
||||
ERR_StringInvalidArgumentType = 1345,
|
||||
ERR_DebugLogNotSupported = 1346,
|
||||
ERR_StringInvalidNonLiteralFormat = 1347,
|
||||
ERR_StringInvalidStringFormatMethod = 1348,
|
||||
ERR_StringInvalidArgument = 1349,
|
||||
|
||||
ERR_StringArrayInvalidArrayCreation = 1350,
|
||||
ERR_StringArrayInvalidArraySize = 1351,
|
||||
ERR_StringArrayInvalidControlFlow = 1352,
|
||||
ERR_StringArrayInvalidArrayIndex = 1353,
|
||||
ERR_StringArrayInvalidArrayIndexOutOfRange = 1354,
|
||||
|
||||
ERR_UnmanagedStringMethodMissing = 1355,
|
||||
ERR_UnmanagedStringMethodInvalid = 1356,
|
||||
|
||||
// Static constructor
|
||||
ERR_ManagedStaticConstructor = 1360,
|
||||
ERR_StaticConstantArrayInStaticConstructor = 1361,
|
||||
|
||||
// Safety check warning
|
||||
WRN_ExceptionThrownInNonSafetyCheckGuardedFunction = 1370,
|
||||
|
||||
// Discarded method warning
|
||||
WRN_ACallToMethodHasBeenDiscarded = 1371,
|
||||
|
||||
// Accessing a nested managed array is not supported
|
||||
ERR_AccessingNestedManagedArrayNotSupported = 1380,
|
||||
|
||||
// Loading from a non-pointer / non-reference is not supported
|
||||
ERR_LdobjFromANonPointerNonReference = 1381,
|
||||
|
||||
ERR_StringLiteralRequired = 1382,
|
||||
|
||||
ERR_MultiDimensionalArrayUnsupported = 1383,
|
||||
|
||||
ERR_NonBlittableAndNonManagedSequentialStructNotSupported = 1384,
|
||||
|
||||
ERR_VarArgFunctionNotSupported = 1385,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
[DebuggerDisplay("{GetDisplayName(),nq}")]
|
||||
internal class BurstCompileTarget
|
||||
{
|
||||
public BurstCompileTarget(MethodInfo method, Type jobType, Type interfaceType, bool isStaticMethod)
|
||||
{
|
||||
Method = method ?? throw new ArgumentNullException(nameof(method));
|
||||
JobType = jobType ?? throw new ArgumentNullException(nameof(jobType));
|
||||
JobInterfaceType = interfaceType; // can be null
|
||||
// This is important to clone the options as we don't want to modify the global instance
|
||||
Options = BurstCompiler.Options.Clone();
|
||||
Options.EnableBurstCompilation = true;
|
||||
// Enable safety checks by default to match inspector default behavior
|
||||
Options.EnableBurstSafetyChecks = true;
|
||||
TargetCpu = BurstTargetCpu.Auto;
|
||||
// The BurstCompilerAttribute can be either on the type or on the method
|
||||
IsStaticMethod = isStaticMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the <see cref="Method"/> is directly tagged with a [BurstCompile] attribute
|
||||
/// </summary>
|
||||
public readonly bool IsStaticMethod;
|
||||
|
||||
/// <summary>
|
||||
/// The Execute method of the target's producer type.
|
||||
/// </summary>
|
||||
public readonly MethodInfo Method;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the actual job (i.e. BoidsSimulationJob).
|
||||
/// </summary>
|
||||
public readonly Type JobType;
|
||||
|
||||
/// <summary>
|
||||
/// The interface of the job (IJob, IJobParallelFor...)
|
||||
/// </summary>
|
||||
public readonly Type JobInterfaceType;
|
||||
|
||||
/// <summary>
|
||||
/// The default compiler options
|
||||
/// </summary>
|
||||
public readonly BurstCompilerOptions Options;
|
||||
|
||||
public BurstTargetCpu TargetCpu { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if burst compilation is actually requested via proper `[BurstCompile]` attribute:
|
||||
/// - On the job if it is a job only
|
||||
/// - On the method and parent class it if is a static method
|
||||
/// </summary>
|
||||
public bool HasRequiredBurstCompileAttributes => BurstCompilerOptions.HasBurstCompileAttribute(JobType) && (!IsStaticMethod || BurstCompilerOptions.HasBurstCompileAttribute(Method));
|
||||
|
||||
/// <summary>
|
||||
/// Generated raw disassembly (IR, IL, ASM...), or null if disassembly failed (only valid for the current TargetCpu)
|
||||
/// </summary>
|
||||
public string RawDisassembly;
|
||||
|
||||
/// <summary>
|
||||
/// Formatted disassembly for the associated <see cref="RawDisassembly"/>, currently only valid for <see cref="Unity.Burst.Editor.DisassemblyKind.Asm"/>
|
||||
/// </summary>
|
||||
public string FormattedDisassembly;
|
||||
|
||||
public DisassemblyKind DisassemblyKind;
|
||||
|
||||
public bool IsDarkMode { get; set; }
|
||||
|
||||
public bool IsBurstError { get; set; }
|
||||
|
||||
public bool IsLoading = false;
|
||||
|
||||
public bool JustLoaded = false;
|
||||
|
||||
public string GetDisplayName()
|
||||
{
|
||||
var displayName = IsStaticMethod ? Pretty(Method) : $"{Pretty(JobType)} - ({Pretty(JobInterfaceType)})";
|
||||
|
||||
// Remove the '<>c__DisplayClass_' part of the name - this is only added for C# Entities.ForEach jobs to trick the C# debugging tools into
|
||||
// treating them like lambdas. This is removed wherever possible from user facing tools (like the Unity profiler), so we should do the same.
|
||||
return displayName.Replace("<>c__DisplayClass_", "");
|
||||
}
|
||||
|
||||
private static string Pretty(MethodInfo method)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(Pretty(method.DeclaringType));
|
||||
builder.Append(".");
|
||||
builder.Append(method.Name);
|
||||
builder.Append("(");
|
||||
var parameters = method.GetParameters();
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var param = parameters[i];
|
||||
if (i > 0) builder.Append(", ");
|
||||
builder.Append(Pretty(param.ParameterType));
|
||||
}
|
||||
|
||||
builder.Append(")");
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
internal static string Pretty(Type type)
|
||||
{
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
return "bool";
|
||||
}
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return "int";
|
||||
}
|
||||
if (type == typeof(long))
|
||||
{
|
||||
return "long";
|
||||
}
|
||||
if (type == typeof(uint))
|
||||
{
|
||||
return "uint";
|
||||
}
|
||||
if (type == typeof(ulong))
|
||||
{
|
||||
return "ulong";
|
||||
}
|
||||
if (type == typeof(short))
|
||||
{
|
||||
return "short";
|
||||
}
|
||||
if (type == typeof(ushort))
|
||||
{
|
||||
return "ushort";
|
||||
}
|
||||
if (type == typeof(byte))
|
||||
{
|
||||
return "byte";
|
||||
}
|
||||
if (type == typeof(sbyte))
|
||||
{
|
||||
return "sbyte";
|
||||
}
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return "float";
|
||||
}
|
||||
if (type == typeof(double))
|
||||
{
|
||||
return "double";
|
||||
}
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
if (type == typeof(object))
|
||||
{
|
||||
return "object";
|
||||
}
|
||||
if (type == typeof(char))
|
||||
{
|
||||
return "char";
|
||||
}
|
||||
|
||||
// When displaying job interface type, display the interface name of Unity.Jobs namespace
|
||||
var typeName = type.IsInterface && type.Name.StartsWith("IJob") ? type.Name : type.ToString();
|
||||
return typeName.Replace("+", ".");
|
||||
}
|
||||
}
|
||||
|
||||
internal enum DisassemblyKind
|
||||
{
|
||||
Asm = 0,
|
||||
IL = 1,
|
||||
UnoptimizedIR = 2,
|
||||
OptimizedIR = 3,
|
||||
IRPassAnalysis = 4
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,124 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible to synchronize <see cref="BurstCompiler.Options"/> with the menu
|
||||
/// </summary>
|
||||
internal static class BurstEditorOptions
|
||||
{
|
||||
// Properties stored in SessionState (survive between domain reloads, but stays alive only during the life of the editor)
|
||||
private const string EnableBurstSafetyChecksText = "BurstSafetyChecks";
|
||||
|
||||
// Properties stored in EditorPrefs (survive between editor restart)
|
||||
private const string EnableBurstCompilationText = "BurstCompilation";
|
||||
private const string EnableBurstTimingsText = "BurstShowTimings";
|
||||
private const string EnableBurstCompileSynchronouslyText = "BurstCompileSynchronously";
|
||||
private const string EnableBurstDebugText = "BurstDebug";
|
||||
private const string ForceEnableBurstSafetyChecksText = "BurstForceSafetyChecks";
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if the menu options are synchronized with <see cref="BurstCompiler.Options"/>
|
||||
/// </summary>
|
||||
private static bool _isSynchronized;
|
||||
|
||||
public static void EnsureSynchronized()
|
||||
{
|
||||
GetGlobalOptions();
|
||||
}
|
||||
|
||||
public static bool EnableBurstCompilation
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstCompilation;
|
||||
set
|
||||
{
|
||||
var enabled = GetGlobalOptions().EnableBurstCompilation;
|
||||
GetGlobalOptions().EnableBurstCompilation = value;
|
||||
if (!enabled && value)
|
||||
{
|
||||
// Must be called AFTER we actually enable compilation
|
||||
BurstCompiler.TriggerUnsafeStaticMethodRecompilation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableBurstSafetyChecks
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstSafetyChecks;
|
||||
set => GetGlobalOptions().EnableBurstSafetyChecks = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstCompileSynchronously
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstCompileSynchronously;
|
||||
set => GetGlobalOptions().EnableBurstCompileSynchronously = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstTimings
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstTimings;
|
||||
set => GetGlobalOptions().EnableBurstTimings = value;
|
||||
}
|
||||
|
||||
public static bool EnableBurstDebug
|
||||
{
|
||||
get => GetGlobalOptions().EnableBurstDebug;
|
||||
set => GetGlobalOptions().EnableBurstDebug = value;
|
||||
}
|
||||
|
||||
public static bool ForceEnableBurstSafetyChecks
|
||||
{
|
||||
get => GetGlobalOptions().ForceEnableBurstSafetyChecks;
|
||||
set => GetGlobalOptions().ForceEnableBurstSafetyChecks = value;
|
||||
}
|
||||
|
||||
private static BurstCompilerOptions GetGlobalOptions()
|
||||
{
|
||||
var global = BurstCompiler.Options;
|
||||
// If options are not synchronize with our global instance, setup the sync
|
||||
if (!_isSynchronized)
|
||||
{
|
||||
global.IsInitializing = true;
|
||||
|
||||
try
|
||||
{
|
||||
// Setup the synchronization
|
||||
global.EnableBurstCompilation = EditorPrefs.GetBool(EnableBurstCompilationText, true);
|
||||
global.EnableBurstCompileSynchronously = EditorPrefs.GetBool(EnableBurstCompileSynchronouslyText, false);
|
||||
global.EnableBurstTimings = EditorPrefs.GetBool(EnableBurstTimingsText, false);
|
||||
global.EnableBurstDebug = EditorPrefs.GetBool(EnableBurstDebugText, false);
|
||||
global.ForceEnableBurstSafetyChecks = EditorPrefs.GetBool(ForceEnableBurstSafetyChecksText, false);
|
||||
|
||||
// Session only properties
|
||||
global.EnableBurstSafetyChecks = SessionState.GetBool(EnableBurstSafetyChecksText, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
global.IsInitializing = false;
|
||||
}
|
||||
|
||||
global.OptionsChanged += GlobalOnOptionsChanged;
|
||||
_isSynchronized = true;
|
||||
}
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
private static void GlobalOnOptionsChanged()
|
||||
{
|
||||
var global = BurstCompiler.Options;
|
||||
// We are not optimizing anything here, so whenever one option is set, we reset all of them
|
||||
EditorPrefs.SetBool(EnableBurstCompilationText, global.EnableBurstCompilation);
|
||||
EditorPrefs.SetBool(EnableBurstCompileSynchronouslyText, global.EnableBurstCompileSynchronously);
|
||||
EditorPrefs.SetBool(EnableBurstTimingsText, global.EnableBurstTimings);
|
||||
EditorPrefs.SetBool(EnableBurstDebugText, global.EnableBurstDebug);
|
||||
EditorPrefs.SetBool(ForceEnableBurstSafetyChecksText, global.ForceEnableBurstSafetyChecks);
|
||||
|
||||
// Session only properties
|
||||
SessionState.SetBool(EnableBurstSafetyChecksText, global.EnableBurstSafetyChecks);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,667 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Burst.LowLevel;
|
||||
using Unity.Profiling;
|
||||
using Unity.Profiling.LowLevel;
|
||||
using Unity.Profiling.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using UnityEditor.Scripting.ScriptCompilation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Main entry point for initializing the burst compiler service for both JIT and AOT
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
internal class BurstLoader
|
||||
{
|
||||
private const int BURST_PROTOCOL_VERSION = 1;
|
||||
|
||||
// Cache the delegate to make sure it doesn't get collected.
|
||||
private static readonly BurstCompilerService.ExtractCompilerFlags TryGetOptionsFromMemberDelegate = TryGetOptionsFromMember;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location to the runtime path of burst.
|
||||
/// </summary>
|
||||
public static string RuntimePath { get; private set; }
|
||||
|
||||
public static bool IsDebugging { get; private set; }
|
||||
|
||||
public static bool SafeShutdown { get; private set; }
|
||||
|
||||
public static int ProtocolVersion { get; private set; }
|
||||
|
||||
private static void VersionUpdateCheck()
|
||||
{
|
||||
var seek = "com.unity.burst@";
|
||||
var first = RuntimePath.LastIndexOf(seek);
|
||||
var last = RuntimePath.LastIndexOf(".Runtime");
|
||||
string version;
|
||||
if (first == -1 || last == -1 || last <= first)
|
||||
{
|
||||
version = "Unknown";
|
||||
}
|
||||
else
|
||||
{
|
||||
first += seek.Length;
|
||||
last -= 1;
|
||||
version = RuntimePath.Substring(first, last - first);
|
||||
}
|
||||
|
||||
var result = BurstCompiler.VersionNotify(version);
|
||||
// result will be empty if we are shutting down, and thus we shouldn't popup a dialog
|
||||
if (!String.IsNullOrEmpty(result) && result != version)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[com.unity.burst] - '{result}' != '{version}'");
|
||||
}
|
||||
OnVersionChangeDetected();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UnityBurstRuntimePathOverwritten(out string path)
|
||||
{
|
||||
path = Environment.GetEnvironmentVariable("UNITY_BURST_RUNTIME_PATH");
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
private static void OnVersionChangeDetected()
|
||||
{
|
||||
// Write marker file to tell Burst to delete the cache at next startup.
|
||||
try
|
||||
{
|
||||
File.Create(Path.Combine(BurstCompilerOptions.DefaultCacheFolder, BurstCompilerOptions.DeleteCacheMarkerFileName)).Dispose();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// In the unlikely scenario that two processes are creating this marker file at the same time,
|
||||
// and one of them fails, do nothing because the other one has hopefully succeeded.
|
||||
}
|
||||
|
||||
// Skip checking if we are using an explicit runtime path.
|
||||
if (!UnityBurstRuntimePathOverwritten(out var _))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Burst Package Update Detected", "The version of Burst used by your project has changed. Please restart the Editor to continue.", "OK");
|
||||
BurstCompiler.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static CompilationTaskReason _currentBuildKind;
|
||||
|
||||
static BurstLoader()
|
||||
{
|
||||
if (BurstCompilerOptions.ForceDisableBurstCompilation)
|
||||
{
|
||||
if (!BurstCompilerOptions.IsSecondaryUnityProcess)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[com.unity.burst] Burst is disabled entirely from the command line");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// This can be setup to get more diagnostics
|
||||
var debuggingStr = Environment.GetEnvironmentVariable("UNITY_BURST_DEBUG");
|
||||
IsDebugging = debuggingStr != null;
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[com.unity.burst] Extra debugging is turned on.");
|
||||
}
|
||||
|
||||
// Try to load the runtime through an environment variable
|
||||
if (!UnityBurstRuntimePathOverwritten(out var path))
|
||||
{
|
||||
// Otherwise try to load it from the package itself
|
||||
path = Path.GetFullPath("Packages/com.unity.burst/.Runtime");
|
||||
}
|
||||
|
||||
RuntimePath = path;
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[com.unity.burst] Runtime directory set to {RuntimePath}");
|
||||
}
|
||||
|
||||
BurstCompilerService.Initialize(RuntimePath, TryGetOptionsFromMemberDelegate);
|
||||
|
||||
ProtocolVersion = BurstCompiler.RequestSetProtocolVersion(BURST_PROTOCOL_VERSION);
|
||||
|
||||
BurstCompiler.Initialize(GetAssemblyFolders());
|
||||
|
||||
// It's important that this call comes *after* BurstCompilerService.Initialize,
|
||||
// otherwise any calls from within EnsureSynchronized to BurstCompilerService,
|
||||
// such as BurstCompiler.Disable(), will silently fail.
|
||||
BurstEditorOptions.EnsureSynchronized();
|
||||
|
||||
EditorApplication.quitting += OnEditorApplicationQuitting;
|
||||
|
||||
CompilationPipeline.compilationStarted += OnCompilationStarted;
|
||||
CompilationPipeline.compilationFinished += OnCompilationFinished;
|
||||
|
||||
// We use this internal event because it's the only way to get access to the ScriptAssembly.HasCompileErrors,
|
||||
// which tells us whether C# compilation succeeded or failed for this assembly.
|
||||
EditorCompilationInterface.Instance.assemblyCompilationFinished += OnAssemblyCompilationFinished;
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
CompilationPipeline.assemblyCompilationNotRequired += OnAssemblyCompilationNotRequired;
|
||||
#endif
|
||||
|
||||
EditorApplication.playModeStateChanged += EditorApplicationOnPlayModeStateChanged;
|
||||
AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;
|
||||
|
||||
SafeShutdown = false;
|
||||
UnityEditor.PackageManager.Events.registeringPackages += PackageRegistrationEvent;
|
||||
SafeShutdown = BurstCompiler.IsApiAvailable("SafeShutdown");
|
||||
|
||||
if (!SafeShutdown)
|
||||
{
|
||||
VersionUpdateCheck();
|
||||
}
|
||||
|
||||
// Notify the compiler about a domain reload
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log("Burst - Domain Reload");
|
||||
}
|
||||
|
||||
BurstCompiler.OnProgress += OnProgress;
|
||||
|
||||
BurstCompiler.EagerCompilationLoggingEnabled = true;
|
||||
|
||||
// Make sure BurstRuntime is initialized. This needs to happen before BurstCompiler.DomainReload,
|
||||
// because that can cause calls to BurstRuntime.Log.
|
||||
BurstRuntime.Initialize();
|
||||
|
||||
// Notify the JitCompilerService about a domain reload
|
||||
BurstCompiler.SetDefaultOptions();
|
||||
BurstCompiler.DomainReload();
|
||||
|
||||
BurstCompiler.OnProfileBegin += OnProfileBegin;
|
||||
BurstCompiler.OnProfileEnd += OnProfileEnd;
|
||||
BurstCompiler.SetProfilerCallbacks();
|
||||
|
||||
BurstCompiler.InitialiseDebuggerHooks();
|
||||
}
|
||||
|
||||
private static bool _isQuitting;
|
||||
private static void OnEditorApplicationQuitting()
|
||||
{
|
||||
_isQuitting = true;
|
||||
}
|
||||
|
||||
public static Action OnBurstShutdown;
|
||||
|
||||
private static void PackageRegistrationEvent(UnityEditor.PackageManager.PackageRegistrationEventArgs obj)
|
||||
{
|
||||
bool requireCleanup = false;
|
||||
if (SafeShutdown)
|
||||
{
|
||||
foreach (var changed in obj.changedFrom)
|
||||
{
|
||||
if (changed.name.Contains("com.unity.burst"))
|
||||
{
|
||||
requireCleanup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var removed in obj.removed)
|
||||
{
|
||||
if (removed.name.Contains("com.unity.burst"))
|
||||
{
|
||||
requireCleanup = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (requireCleanup)
|
||||
{
|
||||
OnBurstShutdown?.Invoke();
|
||||
if (!SafeShutdown)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Burst Package Has Been Removed", "Please restart the Editor to continue.", "OK");
|
||||
}
|
||||
BurstCompiler.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't initialize to 0 because that could be a valid progress ID.
|
||||
private static int BurstProgressId = -1;
|
||||
|
||||
// If this enum changes, update the benchmarks tool accordingly as we rely on integer value related to this enum
|
||||
internal enum BurstEagerCompilationStatus
|
||||
{
|
||||
NotScheduled,
|
||||
Scheduled,
|
||||
Completed
|
||||
}
|
||||
|
||||
// For the time being, this field is only read through reflection
|
||||
internal static BurstEagerCompilationStatus EagerCompilationStatus;
|
||||
|
||||
private static void OnProgress(int current, int total)
|
||||
{
|
||||
if (current == total)
|
||||
{
|
||||
EagerCompilationStatus = BurstEagerCompilationStatus.Completed;
|
||||
}
|
||||
|
||||
// OnProgress is called from a background thread,
|
||||
// but we need to update the progress UI on the main thread.
|
||||
EditorApplication.CallDelayed(() =>
|
||||
{
|
||||
if (current == total)
|
||||
{
|
||||
// We've finished - remove progress bar.
|
||||
if (Progress.Exists(BurstProgressId))
|
||||
{
|
||||
Progress.Remove(BurstProgressId);
|
||||
BurstProgressId = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do we need to create the progress bar?
|
||||
if (!Progress.Exists(BurstProgressId))
|
||||
{
|
||||
BurstProgressId = Progress.Start(
|
||||
"Burst",
|
||||
"Compiling...",
|
||||
Progress.Options.Unmanaged);
|
||||
}
|
||||
|
||||
Progress.Report(
|
||||
BurstProgressId,
|
||||
current / (float)total,
|
||||
$"Compiled {current} / {total} libraries");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static Dictionary<string, IntPtr> ProfilerMarkers;
|
||||
|
||||
private static unsafe void OnProfileBegin(string markerName, string metadataName, string metadataValue)
|
||||
{
|
||||
if (ProfilerMarkers == null)
|
||||
{
|
||||
// Initialize thread-static dictionary.
|
||||
ProfilerMarkers = new Dictionary<string, IntPtr>();
|
||||
}
|
||||
|
||||
if (!ProfilerMarkers.TryGetValue(markerName, out var markerPtr))
|
||||
{
|
||||
ProfilerMarkers.Add(markerName, markerPtr = ProfilerUnsafeUtility.CreateMarker(
|
||||
markerName,
|
||||
ProfilerUnsafeUtility.CategoryScripts,
|
||||
MarkerFlags.Script,
|
||||
metadataName != null ? 1 : 0));
|
||||
|
||||
// metadataName is assumed to be consistent for a given markerName.
|
||||
if (metadataName != null)
|
||||
{
|
||||
ProfilerUnsafeUtility.SetMarkerMetadata(
|
||||
markerPtr,
|
||||
0,
|
||||
metadataName,
|
||||
(byte)ProfilerMarkerDataType.String16,
|
||||
(byte)ProfilerMarkerDataUnit.Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
if (metadataName != null && metadataValue != null)
|
||||
{
|
||||
fixed (char* methodNamePtr = metadataValue)
|
||||
{
|
||||
var metadata = new ProfilerMarkerData
|
||||
{
|
||||
Type = (byte)ProfilerMarkerDataType.String16,
|
||||
Size = ((uint)metadataValue.Length + 1) * 2,
|
||||
Ptr = methodNamePtr
|
||||
};
|
||||
ProfilerUnsafeUtility.BeginSampleWithMetadata(markerPtr, 1, &metadata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfilerUnsafeUtility.BeginSample(markerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnProfileEnd(string markerName)
|
||||
{
|
||||
if (ProfilerMarkers == null)
|
||||
{
|
||||
// If we got here it means we had a domain reload between when we called profile begin and
|
||||
// now profile end, and so we need to bail out.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ProfilerMarkers.TryGetValue(markerName, out var markerPtr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProfilerUnsafeUtility.EndSample(markerPtr);
|
||||
}
|
||||
|
||||
private static void EditorApplicationOnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"Burst - Change of Editor State: {state}");
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PlayModeStateChange.ExitingPlayMode:
|
||||
// Cleanup any loaded burst natives so users have a clean point to update the libraries.
|
||||
BurstCompiler.UnloadAdditionalLibraries();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
enum CompilationTaskReason
|
||||
{
|
||||
IsForEditor, // Compilation should proceed as its for an editor build
|
||||
IsForPlayer, // Skip this compilation
|
||||
IsForPreviousScriptingMode, // We are about to enter a domain reload, don't start any new compilations
|
||||
IsForAssemblyBuilder, // Request is coming from an 'AssemblyBuilder' and should be skipped as not supported
|
||||
}
|
||||
|
||||
static CompilationTaskReason CurrentCompilationTaskShouldStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (BurstCompiler.WasScriptDebugInfoEnabledAtDomainReload != UnityEditor.Compilation.CompilationPipeline.IsScriptDebugInfoEnabled())
|
||||
{
|
||||
// If the scripting compilation mode has changed since we last had our domain reloaded, then we ignore all requests, and act as if
|
||||
//loading for the first time. This is to avoid having compilations kick off right before a Shutdown triggered by domain reload, that
|
||||
//would cause the a significant stall as we had to wait for those compilations to finish, thus blocking the main thread.
|
||||
return CompilationTaskReason.IsForPreviousScriptingMode;
|
||||
}
|
||||
|
||||
var inst = EditorCompilationInterface.Instance;
|
||||
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
var editorCompilationType = inst.GetType();
|
||||
var activeBeeBuildField = editorCompilationType.GetField("_currentBeeScriptCompilationState", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (activeBeeBuildField == null)
|
||||
{
|
||||
activeBeeBuildField = editorCompilationType.GetField("activeBeeBuild", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
}
|
||||
var activeBeeBuild = activeBeeBuildField.GetValue(inst);
|
||||
|
||||
// If a user is doing an `AssemblyBuilder` compilation, we do not support that in Burst.
|
||||
// This seems to manifest as a null `activeBeeBuild`, so we bail here if that happens.
|
||||
if (activeBeeBuild == null)
|
||||
{
|
||||
return CompilationTaskReason.IsForAssemblyBuilder;
|
||||
}
|
||||
|
||||
var settings = activeBeeBuild.GetType().GetProperty("settings", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(activeBeeBuild);
|
||||
var opt = (EditorScriptCompilationOptions)settings.GetType().GetProperty("CompilationOptions").GetValue(settings);
|
||||
#else
|
||||
var task = inst.GetType()
|
||||
.GetField("compilationTask", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
.GetValue(inst);
|
||||
|
||||
// If a user is doing an `AssemblyBuilder` compilation, we do not support that in Burst.
|
||||
// This seems to manifest as a null `task`, so we bail here if that happens.
|
||||
if (task == null)
|
||||
{
|
||||
return CompilationTaskReason.IsForAssemblyBuilder;
|
||||
}
|
||||
|
||||
var opt = (EditorScriptCompilationOptions)task.GetType()
|
||||
.GetField("options", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
.GetValue(task);
|
||||
#endif
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
if ((opt & EditorScriptCompilationOptions.BuildingSkipCompile) != 0)
|
||||
{
|
||||
return CompilationTaskReason.IsForPlayer;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((opt & EditorScriptCompilationOptions.BuildingForEditor) != 0)
|
||||
{
|
||||
return CompilationTaskReason.IsForEditor;
|
||||
}
|
||||
|
||||
return CompilationTaskReason.IsForPlayer;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("Burst - Unknown private compilation pipeline API\nAssuming editor build\n" + ex.ToString());
|
||||
|
||||
return CompilationTaskReason.IsForEditor;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnCompilationStarted(object value)
|
||||
{
|
||||
_currentBuildKind = CurrentCompilationTaskShouldStart();
|
||||
if (_currentBuildKind != CompilationTaskReason.IsForEditor)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - not handling '{value}' because '{_currentBuildKind}'");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - compilation started for '{value}'");
|
||||
}
|
||||
|
||||
BurstCompiler.NotifyCompilationStarted(GetAssemblyFolders());
|
||||
}
|
||||
|
||||
private static string[] GetAssemblyFolders()
|
||||
{
|
||||
var assemblyFolders = new HashSet<string>();
|
||||
|
||||
// First, we get the path to Mono system libraries. This will be something like
|
||||
// <EditorPath>/Data/MonoBleedingEdge/lib/mono/unityjit-win32
|
||||
//
|
||||
// You might think we could use MonoLibraryHelpers.GetSystemReferenceDirectories
|
||||
// here, but we can't, because that returns the _reference assembly_ directories,
|
||||
// not the actual implementation assembly directory.
|
||||
assemblyFolders.Add(Path.GetDirectoryName(typeof(object).Assembly.Location));
|
||||
|
||||
// Now add the default assembly search paths.
|
||||
// This will include
|
||||
// - Unity dlls in <EditorPath>/Data/Managed and <EditorPath>/Data/Managed/UnityEngine
|
||||
// - Platform support dlls e.g. <EditorPath>/Data/PlaybackEngines/WindowsStandaloneSupport
|
||||
// - Package paths. These are interesting because they are "virtual" paths, of the form
|
||||
// Packages/<MyPackageName>. They need to be resolved to physical paths.
|
||||
// - Library/ScriptAssemblies. This needs to be resolved to the full path.
|
||||
var defaultAssemblySearchPaths = AssemblyHelper.GetDefaultAssemblySearchPaths();
|
||||
var packagesLookup = GetPackagesLookup();
|
||||
foreach (var searchPath in defaultAssemblySearchPaths)
|
||||
{
|
||||
if (TryResolvePath(searchPath, packagesLookup, out var resolvedPath))
|
||||
{
|
||||
assemblyFolders.Add(resolvedPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - AssemblyFolders : \n{string.Join("\n", assemblyFolders)}");
|
||||
}
|
||||
|
||||
return assemblyFolders.ToArray();
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetPackagesLookup()
|
||||
{
|
||||
var packages = new Dictionary<string, string>();
|
||||
|
||||
// Fetch list of packages
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
var allPackages = UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages();
|
||||
#else
|
||||
var allPackages = UnityEditor.PackageManager.PackageInfo.GetAll();
|
||||
#endif
|
||||
foreach (var p in allPackages)
|
||||
{
|
||||
packages.Add(p.name, p.resolvedPath);
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
private const string PackagesPath = "Packages/";
|
||||
private static readonly int PackagesPathLength = PackagesPath.Length;
|
||||
|
||||
private static bool TryResolvePath(string path, Dictionary<string, string> packagesLookup, out string resolvedPath)
|
||||
{
|
||||
if (path.StartsWith("Packages/", StringComparison.InvariantCulture))
|
||||
{
|
||||
var secondSlashIndex = path.IndexOf('/', PackagesPathLength);
|
||||
var packageName = secondSlashIndex > -1
|
||||
? path.Substring(PackagesPathLength, secondSlashIndex - PackagesPathLength)
|
||||
: path.Substring(PackagesPathLength);
|
||||
|
||||
if (packagesLookup.TryGetValue(packageName, out var resolvedPathTemp))
|
||||
{
|
||||
path = secondSlashIndex > -1
|
||||
? Path.Combine(resolvedPathTemp, path.Substring(secondSlashIndex + 1))
|
||||
: resolvedPathTemp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - unknown package path '{path}'");
|
||||
}
|
||||
resolvedPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
resolvedPath = Path.GetFullPath(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void OnCompilationFinished(object value)
|
||||
{
|
||||
if (_currentBuildKind!=CompilationTaskReason.IsForEditor)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring finished compilation '{value}' because it's '{_currentBuildKind}'");
|
||||
}
|
||||
|
||||
_currentBuildKind = CompilationTaskReason.IsForEditor;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - compilation finished for '{value}'");
|
||||
}
|
||||
|
||||
BurstCompiler.NotifyCompilationFinished();
|
||||
}
|
||||
|
||||
#if UNITY_2021_1_OR_NEWER
|
||||
private static void OnAssemblyCompilationFinished(ScriptAssembly assembly, CompilerMessage[] messages)
|
||||
#else
|
||||
private static void OnAssemblyCompilationFinished(ScriptAssembly assembly, CompilerMessage[] messages, EditorScriptCompilationOptions options)
|
||||
#endif
|
||||
{
|
||||
if (_currentBuildKind!=CompilationTaskReason.IsForEditor)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{assembly.Filename}' because it's '{_currentBuildKind}'");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - Assembly compilation finished for '{assembly.Filename}'");
|
||||
}
|
||||
|
||||
if (assembly.HasCompileErrors)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{assembly.Filename}' because it failed C# compilation");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BurstCompiler.NotifyAssemblyCompilationFinished(Path.GetFileNameWithoutExtension(assembly.Filename));
|
||||
}
|
||||
|
||||
private static void OnAssemblyCompilationNotRequired(string arg1)
|
||||
{
|
||||
if (_currentBuildKind!=CompilationTaskReason.IsForEditor)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - ignoring '{arg1}' because it's '{_currentBuildKind}'");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"{DateTime.UtcNow} Burst - Assembly compilation not required for '{arg1}'");
|
||||
}
|
||||
|
||||
BurstCompiler.NotifyAssemblyCompilationNotRequired(Path.GetFileNameWithoutExtension(arg1));
|
||||
}
|
||||
|
||||
private static bool TryGetOptionsFromMember(MemberInfo member, out string flagsOut)
|
||||
{
|
||||
return BurstCompiler.Options.TryGetOptions(member, true, out flagsOut);
|
||||
}
|
||||
|
||||
private static void OnDomainUnload(object sender, EventArgs e)
|
||||
{
|
||||
if (IsDebugging)
|
||||
{
|
||||
UnityEngine.Debug.Log($"Burst - OnDomainUnload");
|
||||
}
|
||||
|
||||
BurstCompiler.Cancel();
|
||||
|
||||
// This check here is to execute shutdown after all OnDisable's. EditorApplication.quitting event is called before OnDisable's, so we need to shutdown in here.
|
||||
if (_isQuitting)
|
||||
{
|
||||
BurstCompiler.Shutdown();
|
||||
}
|
||||
|
||||
// Because of a check in Unity (specifically SCRIPTINGAPI_THREAD_AND_SERIALIZATION_CHECK),
|
||||
// we are not allowed to call thread-unsafe methods (like Progress.Exists) after the
|
||||
// kApplicationTerminating bit has been set. And because the domain is unloaded
|
||||
// (thus triggering AppDomain.DomainUnload) *after* that bit is set, we can't call Progress.Exists
|
||||
// during shutdown. So we check _isQuitting here. When quitting, it's fine for the progress item
|
||||
// not to be removed since it's all being torn down anyway.
|
||||
if (!_isQuitting && Progress.Exists(BurstProgressId))
|
||||
{
|
||||
Progress.Remove(BurstProgressId);
|
||||
BurstProgressId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,644 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.Burst.Editor.Tests")]
|
||||
namespace Unity.Burst.Editor
|
||||
{
|
||||
using static BurstCompilerOptions;
|
||||
|
||||
internal static class BurstReflection
|
||||
{
|
||||
// The TypeCache API was added in 2019.2. So there are two versions of FindExecuteMethods,
|
||||
// one that uses TypeCache and one that doesn't.
|
||||
public static FindExecuteMethodsResult FindExecuteMethods(List<System.Reflection.Assembly> assemblyList, BurstReflectionAssemblyOptions options)
|
||||
{
|
||||
var methodsToCompile = new List<BurstCompileTarget>();
|
||||
var methodsToCompileSet = new HashSet<MethodInfo>();
|
||||
var logMessages = new List<LogMessage>();
|
||||
var interfaceToProducer = new Dictionary<Type, Type>();
|
||||
|
||||
var assemblySet = new HashSet<System.Reflection.Assembly>(assemblyList);
|
||||
|
||||
void AddTarget(BurstCompileTarget target)
|
||||
{
|
||||
if (target.Method.Name.EndsWith("$BurstManaged")) return;
|
||||
|
||||
// We will not try to record more than once a method in the methods to compile
|
||||
// This can happen if a job interface is inheriting from another job interface which are using in the end the same
|
||||
// job producer type
|
||||
if (!target.IsStaticMethod && !methodsToCompileSet.Add(target.Method))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies) &&
|
||||
target.JobType.Assembly.GetReferencedAssemblies().Any(x => IsNUnitDll(x.Name)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
methodsToCompile.Add(target);
|
||||
}
|
||||
|
||||
var staticMethodTypes = new HashSet<Type>();
|
||||
|
||||
// -------------------------------------------
|
||||
// Find job structs using TypeCache.
|
||||
// -------------------------------------------
|
||||
|
||||
var jobProducerImplementations = TypeCache.GetTypesWithAttribute<JobProducerTypeAttribute>();
|
||||
foreach (var jobProducerImplementation in jobProducerImplementations)
|
||||
{
|
||||
var attrs = jobProducerImplementation.GetCustomAttributes(typeof(JobProducerTypeAttribute), false);
|
||||
if (attrs.Length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
staticMethodTypes.Add(jobProducerImplementation);
|
||||
|
||||
var attr = (JobProducerTypeAttribute)attrs[0];
|
||||
interfaceToProducer.Add(jobProducerImplementation, attr.ProducerType);
|
||||
}
|
||||
|
||||
foreach (var jobProducerImplementation in jobProducerImplementations)
|
||||
{
|
||||
if (!jobProducerImplementation.IsInterface)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var jobTypes = TypeCache.GetTypesDerivedFrom(jobProducerImplementation);
|
||||
|
||||
foreach (var jobType in jobTypes)
|
||||
{
|
||||
if (jobType.IsGenericType || !jobType.IsValueType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanJobType(jobType, interfaceToProducer, logMessages, AddTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Find static methods using TypeCache.
|
||||
// -------------------------------------------
|
||||
|
||||
void AddStaticMethods(TypeCache.MethodCollection methods)
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (HasBurstCompileAttribute(method.DeclaringType))
|
||||
{
|
||||
staticMethodTypes.Add(method.DeclaringType);
|
||||
|
||||
// NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
|
||||
// We are only working on plain type or generic type instance!
|
||||
if (!method.DeclaringType.IsGenericTypeDefinition &&
|
||||
method.IsStatic &&
|
||||
!method.ContainsGenericParameters)
|
||||
{
|
||||
AddTarget(new BurstCompileTarget(method, method.DeclaringType, null, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add [BurstCompile] static methods.
|
||||
AddStaticMethods(TypeCache.GetMethodsWithAttribute<BurstCompileAttribute>());
|
||||
|
||||
// Add [TestCompiler] static methods.
|
||||
if (!options.HasFlag(BurstReflectionAssemblyOptions.ExcludeTestAssemblies))
|
||||
{
|
||||
var testCompilerAttributeType = Type.GetType("Burst.Compiler.IL.Tests.TestCompilerAttribute, Unity.Burst.Tests.UnitTests, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
|
||||
if (testCompilerAttributeType != null)
|
||||
{
|
||||
AddStaticMethods(TypeCache.GetMethodsWithAttribute(testCompilerAttributeType));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------
|
||||
// Find job types and static methods based on
|
||||
// generic instances types. These will not be
|
||||
// found by the TypeCache scanning above.
|
||||
// -------------------------------------------
|
||||
FindExecuteMethodsForGenericInstances(
|
||||
assemblySet,
|
||||
staticMethodTypes,
|
||||
interfaceToProducer,
|
||||
AddTarget,
|
||||
logMessages);
|
||||
|
||||
return new FindExecuteMethodsResult(methodsToCompile, logMessages);
|
||||
}
|
||||
|
||||
private static void ScanJobType(
|
||||
Type jobType,
|
||||
Dictionary<Type, Type> interfaceToProducer,
|
||||
List<LogMessage> logMessages,
|
||||
Action<BurstCompileTarget> addTarget)
|
||||
{
|
||||
foreach (var interfaceType in jobType.GetInterfaces())
|
||||
{
|
||||
var genericLessInterface = interfaceType;
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
genericLessInterface = interfaceType.GetGenericTypeDefinition();
|
||||
}
|
||||
|
||||
if (interfaceToProducer.TryGetValue(genericLessInterface, out var foundProducer))
|
||||
{
|
||||
var genericParams = new List<Type> { jobType };
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
genericParams.AddRange(interfaceType.GenericTypeArguments);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var executeType = foundProducer.MakeGenericType(genericParams.ToArray());
|
||||
var executeMethod = executeType.GetMethod("Execute", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (executeMethod == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Burst reflection error. The type `{executeType}` does not contain an `Execute` method");
|
||||
}
|
||||
|
||||
addTarget(new BurstCompileTarget(executeMethod, jobType, interfaceType, false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FindExecuteMethodsForGenericInstances(
|
||||
HashSet<System.Reflection.Assembly> assemblyList,
|
||||
HashSet<Type> staticMethodTypes,
|
||||
Dictionary<Type, Type> interfaceToProducer,
|
||||
Action<BurstCompileTarget> addTarget,
|
||||
List<LogMessage> logMessages)
|
||||
{
|
||||
var valueTypes = new List<TypeToVisit>();
|
||||
|
||||
//Debug.Log("Filtered Assembly List: " + string.Join(", ", assemblyList.Select(assembly => assembly.GetName().Name)));
|
||||
|
||||
// Find all ways to execute job types (via producer attributes)
|
||||
var typesVisited = new HashSet<string>();
|
||||
var typesToVisit = new HashSet<string>();
|
||||
var allTypesAssembliesCollected = new HashSet<Type>();
|
||||
foreach (var assembly in assemblyList)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
try
|
||||
{
|
||||
// Collect all generic type instances (excluding indirect instances)
|
||||
CollectGenericTypeInstances(
|
||||
assembly,
|
||||
x => assemblyList.Contains(x.Assembly),
|
||||
types,
|
||||
allTypesAssembliesCollected);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(LogType.Warning, "Unexpected exception while collecting types in assembly `" + assembly.FullName + "` Exception: " + ex));
|
||||
}
|
||||
|
||||
for (var i = 0; i < types.Count; i++)
|
||||
{
|
||||
var t = types[i];
|
||||
if (typesToVisit.Add(t.AssemblyQualifiedName))
|
||||
{
|
||||
// Because the list of types returned by CollectGenericTypeInstances does not detect nested generic classes that are not
|
||||
// used explicitly, we need to create them if a declaring type is actually used
|
||||
// so for example if we have:
|
||||
// class MyClass<T> { class MyNestedClass { } }
|
||||
// class MyDerived : MyClass<int> { }
|
||||
// The CollectGenericTypeInstances will return typically the type MyClass<int>, but will not list MyClass<int>.MyNestedClass
|
||||
// So the following code is correcting this in order to fully query the full graph of generic instance types, including indirect types
|
||||
var nestedTypes = t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var nestedType in nestedTypes)
|
||||
{
|
||||
if (t.IsGenericType && !t.IsGenericTypeDefinition)
|
||||
{
|
||||
var parentGenericTypeArguments = t.GetGenericArguments();
|
||||
// Only create nested types that are closed generic types (full generic instance types)
|
||||
// It happens if for example the parent class is `class MClass<T> { class MyNestedGeneric<T1> {} }`
|
||||
// In that case, MyNestedGeneric<T1> is opened in the context of MClass<int>, so we don't process them
|
||||
if (nestedType.GetGenericArguments().Length == parentGenericTypeArguments.Length)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instanceNestedType = nestedType.MakeGenericType(parentGenericTypeArguments);
|
||||
types.Add(instanceNestedType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var error = $"Unexpected Burst Inspector error. Invalid generic type instance. Trying to instantiate the generic type {nestedType.FullName} with the generic arguments <{string.Join(", ", parentGenericTypeArguments.Select(x => x.FullName))}> is not supported: {ex}";
|
||||
logMessages.Add(new LogMessage(LogType.Warning, error));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
types.Add(nestedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var t in types)
|
||||
{
|
||||
// If the type has been already visited, don't try to visit it
|
||||
if (!typesVisited.Add(t.AssemblyQualifiedName) || (t.IsGenericTypeDefinition && !t.IsInterface))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// collect methods with types having a [BurstCompile] attribute
|
||||
var staticMethodDeclaringType = t;
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
staticMethodDeclaringType = t.GetGenericTypeDefinition();
|
||||
}
|
||||
bool visitStaticMethods = staticMethodTypes.Contains(staticMethodDeclaringType);
|
||||
bool isValueType = false;
|
||||
|
||||
if (t.IsValueType)
|
||||
{
|
||||
// NOTE: Make sure that we don't use a value type generic definition (e.g `class Outer<T> { struct Inner { } }`)
|
||||
// We are only working on plain type or generic type instance!
|
||||
if (!t.IsGenericTypeDefinition)
|
||||
isValueType = true;
|
||||
}
|
||||
|
||||
if (isValueType || visitStaticMethods)
|
||||
{
|
||||
valueTypes.Add(new TypeToVisit(t, visitStaticMethods));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(LogType.Warning,
|
||||
"Unexpected exception while inspecting type `" + t +
|
||||
"` IsConstructedGenericType: " + t.IsConstructedGenericType +
|
||||
" IsGenericTypeDef: " + t.IsGenericTypeDefinition +
|
||||
" IsGenericParam: " + t.IsGenericParameter +
|
||||
" Exception: " + ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Revisit all types to find things that are compilable using the above producers.
|
||||
foreach (var typePair in valueTypes)
|
||||
{
|
||||
var type = typePair.Type;
|
||||
|
||||
// collect static [BurstCompile] methods
|
||||
if (typePair.CollectStaticMethods)
|
||||
{
|
||||
try
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (HasBurstCompileAttribute(method))
|
||||
{
|
||||
addTarget(new BurstCompileTarget(method, type, null, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logMessages.Add(new LogMessage(ex));
|
||||
}
|
||||
}
|
||||
|
||||
// If the type is not a value type, we don't need to proceed with struct Jobs
|
||||
if (!type.IsValueType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanJobType(type, interfaceToProducer, logMessages, addTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FindExecuteMethodsResult
|
||||
{
|
||||
public readonly List<BurstCompileTarget> CompileTargets;
|
||||
public readonly List<LogMessage> LogMessages;
|
||||
|
||||
public FindExecuteMethodsResult(List<BurstCompileTarget> compileTargets, List<LogMessage> logMessages)
|
||||
{
|
||||
CompileTargets = compileTargets;
|
||||
LogMessages = logMessages;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class LogMessage
|
||||
{
|
||||
public readonly LogType LogType;
|
||||
public readonly string Message;
|
||||
public readonly Exception Exception;
|
||||
|
||||
public LogMessage(LogType logType, string message)
|
||||
{
|
||||
LogType = logType;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public LogMessage(Exception exception)
|
||||
{
|
||||
LogType = LogType.Exception;
|
||||
Exception = exception;
|
||||
}
|
||||
}
|
||||
|
||||
public enum LogType
|
||||
{
|
||||
Warning,
|
||||
Exception,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method exists solely to ensure that the static constructor has been called.
|
||||
/// </summary>
|
||||
public static void EnsureInitialized() { }
|
||||
|
||||
public static readonly List<System.Reflection.Assembly> EditorAssembliesThatCanPossiblyContainJobs;
|
||||
public static readonly List<System.Reflection.Assembly> EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies;
|
||||
|
||||
/// <summary>
|
||||
/// Collects (and caches) all editor assemblies - transitively.
|
||||
/// </summary>
|
||||
static BurstReflection()
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobs = new List<System.Reflection.Assembly>();
|
||||
EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies = new List<System.Reflection.Assembly>();
|
||||
|
||||
// TODO: Not sure there is a better way to match assemblies returned by CompilationPipeline.GetAssemblies
|
||||
// with runtime assemblies contained in the AppDomain.CurrentDomain.GetAssemblies()
|
||||
|
||||
// Filter the assemblies
|
||||
var assemblyList = CompilationPipeline.GetAssemblies(AssembliesType.Editor);
|
||||
|
||||
var assemblyNames = new HashSet<string>();
|
||||
foreach (var assembly in assemblyList)
|
||||
{
|
||||
CollectAssemblyNames(assembly, assemblyNames);
|
||||
}
|
||||
|
||||
var allAssemblies = new HashSet<System.Reflection.Assembly>();
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (!assemblyNames.Contains(assembly.GetName().Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CollectAssembly(assembly, allAssemblies);
|
||||
}
|
||||
}
|
||||
|
||||
// For an assembly to contain something "interesting" when we're scanning for things to compile,
|
||||
// it needs to either:
|
||||
// (a) be one of these assemblies, or
|
||||
// (b) reference one of these assemblies
|
||||
private static readonly string[] ScanMarkerAssemblies = new[]
|
||||
{
|
||||
// Contains [BurstCompile] attribute
|
||||
"Unity.Burst",
|
||||
|
||||
// Contains [JobProducerType] attribute
|
||||
"UnityEngine.CoreModule"
|
||||
};
|
||||
|
||||
private static void CollectAssembly(System.Reflection.Assembly assembly, HashSet<System.Reflection.Assembly> collect)
|
||||
{
|
||||
if (!collect.Add(assembly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var referencedAssemblies = assembly.GetReferencedAssemblies();
|
||||
|
||||
var shouldCollectReferences = false;
|
||||
|
||||
var name = assembly.GetName().Name;
|
||||
if (ScanMarkerAssemblies.Contains(name) || referencedAssemblies.Any(x => ScanMarkerAssemblies.Contains(x.Name)))
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobs.Add(assembly);
|
||||
shouldCollectReferences = true;
|
||||
|
||||
if (!assembly.GetReferencedAssemblies().Any(x => IsNUnitDll(x.Name)))
|
||||
{
|
||||
EditorAssembliesThatCanPossiblyContainJobsExcludingTestAssemblies.Add(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldCollectReferences)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var assemblyName in referencedAssemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
CollectAssembly(System.Reflection.Assembly.Load(assemblyName), collect);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (BurstLoader.IsDebugging)
|
||||
{
|
||||
Debug.LogWarning("Could not load assembly " + assemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNUnitDll(string value)
|
||||
{
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(value, "nunit.framework") >= 0;
|
||||
}
|
||||
|
||||
private static void CollectAssemblyNames(UnityEditor.Compilation.Assembly assembly, HashSet<string> collect)
|
||||
{
|
||||
if (assembly == null || assembly.name == null) return;
|
||||
|
||||
if (!collect.Add(assembly.name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var assemblyRef in assembly.assemblyReferences)
|
||||
{
|
||||
CollectAssemblyNames(assemblyRef, collect);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of concrete generic type instances used in an assembly.
|
||||
/// See remarks
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly</param>
|
||||
/// <param name="types"></param>
|
||||
/// <returns>The list of generic type instances</returns>
|
||||
/// <remarks>
|
||||
/// Note that this method fetchs only direct type instances but
|
||||
/// cannot fetch transitive generic type instances.
|
||||
/// </remarks>
|
||||
private static void CollectGenericTypeInstances(
|
||||
System.Reflection.Assembly assembly,
|
||||
Func<Type, bool> typeFilter,
|
||||
List<Type> types,
|
||||
HashSet<Type> visited)
|
||||
{
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// WARNING: THIS CODE HAS TO BE MAINTAINED IN SYNC WITH BclApp.cs
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
// From: https://gist.github.com/xoofx/710aaf86e0e8c81649d1261b1ef9590e
|
||||
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
|
||||
const int mdMaxCount = 1 << 24;
|
||||
foreach (var module in assembly.Modules)
|
||||
{
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for TypeSpec
|
||||
const int mdTypeSpec = 0x1B000000;
|
||||
var type = module.ResolveType(mdTypeSpec | i);
|
||||
CollectGenericTypeInstances(type, types, visited, typeFilter);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for MethodSpec
|
||||
const int mdMethodSpec = 0x2B000000;
|
||||
var method = module.ResolveMethod(mdMethodSpec | i);
|
||||
var genericArgs = method.GetGenericArguments();
|
||||
foreach (var genArgType in genericArgs)
|
||||
{
|
||||
CollectGenericTypeInstances(genArgType, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i < mdMaxCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Token base id for Field
|
||||
const int mdField = 0x04000000;
|
||||
var field = module.ResolveField(mdField | i);
|
||||
CollectGenericTypeInstances(field.FieldType, types, visited, typeFilter);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Can happen on ResolveType on certain generic types, so we continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for types used in constructor arguments to assembly-level attributes,
|
||||
// such as [RegisterGenericJobType(typeof(...))].
|
||||
foreach (var customAttribute in assembly.CustomAttributes)
|
||||
{
|
||||
foreach (var argument in customAttribute.ConstructorArguments)
|
||||
{
|
||||
if (argument.ArgumentType == typeof(Type))
|
||||
{
|
||||
CollectGenericTypeInstances((Type)argument.Value, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CollectGenericTypeInstances(
|
||||
Type type,
|
||||
List<Type> types,
|
||||
HashSet<Type> visited,
|
||||
Func<Type, bool> typeFilter)
|
||||
{
|
||||
if (type.IsPrimitive) return;
|
||||
if (!visited.Add(type)) return;
|
||||
|
||||
// Add only concrete types
|
||||
if (type.IsConstructedGenericType && !type.ContainsGenericParameters && typeFilter(type))
|
||||
{
|
||||
types.Add(type);
|
||||
}
|
||||
|
||||
// Collect recursively generic type arguments
|
||||
var genericTypeArguments = type.GenericTypeArguments;
|
||||
foreach (var genericTypeArgument in genericTypeArguments)
|
||||
{
|
||||
if (!genericTypeArgument.IsPrimitive)
|
||||
{
|
||||
CollectGenericTypeInstances(genericTypeArgument, types, visited, typeFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{Type} (static methods: {CollectStaticMethods})")]
|
||||
private struct TypeToVisit
|
||||
{
|
||||
public TypeToVisit(Type type, bool collectStaticMethods)
|
||||
{
|
||||
Type = type;
|
||||
CollectStaticMethods = collectStaticMethods;
|
||||
}
|
||||
|
||||
public readonly Type Type;
|
||||
|
||||
public readonly bool CollectStaticMethods;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum BurstReflectionAssemblyOptions
|
||||
{
|
||||
None = 0,
|
||||
ExcludeTestAssemblies = 1,
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"reference": "Unity.Burst"
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// Base interface for a function pointer.
|
||||
/// </summary>
|
||||
public interface IFunctionPointer
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a pointer to a function pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr">The native pointer.</param>
|
||||
/// <returns>An instance of this interface.</returns>
|
||||
[Obsolete("This method will be removed in a future version of Burst")]
|
||||
IFunctionPointer FromIntPtr(IntPtr ptr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A function pointer that can be used from a Burst Job or from regular C#.
|
||||
/// It needs to be compiled through <see cref="BurstCompiler.CompileFunctionPointer{T}"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the delegate of this function pointer</typeparam>
|
||||
public readonly struct FunctionPointer<T> : IFunctionPointer
|
||||
{
|
||||
// DOTSPLAYER's shim package relies on Burst for it's jobs code
|
||||
// so Burst does not see the DOTSPLAYER definition of this attribute
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
#endif
|
||||
private readonly IntPtr _ptr;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this function pointer with the following native pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
public FunctionPointer(IntPtr ptr)
|
||||
{
|
||||
_ptr = ptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying pointer.
|
||||
/// </summary>
|
||||
public IntPtr Value => _ptr;
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private void CheckIsCreated()
|
||||
{
|
||||
if (!IsCreated)
|
||||
{
|
||||
throw new NullReferenceException("Object reference not set to an instance of an object");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delegate associated to this function pointer in order to call the function pointer.
|
||||
/// This delegate can be called from a Burst Job or from regular C#.
|
||||
/// If calling from regular C#, it is recommended to cache the returned delegate of this property
|
||||
/// instead of using this property every time you need to call the delegate.
|
||||
/// </summary>
|
||||
public T Invoke
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckIsCreated();
|
||||
return Marshal.GetDelegateForFunctionPointer<T>(_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the function pointer is valid.
|
||||
/// </summary>
|
||||
public bool IsCreated => _ptr != IntPtr.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a pointer to a function pointer.
|
||||
/// </summary>
|
||||
/// <param name="ptr">The native pointer.</param>
|
||||
/// <returns>An instance of this interface.</returns>
|
||||
IFunctionPointer IFunctionPointer.FromIntPtr(IntPtr ptr) => new FunctionPointer<T>(ptr);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst.Intrinsics
|
||||
{
|
||||
/// <summary>
|
||||
/// Common intrinsics that are exposed across all Burst targets.
|
||||
/// </summary>
|
||||
public static class Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Hint that the current thread should pause.
|
||||
///
|
||||
/// In Burst compiled code this will map to platform specific
|
||||
/// ways to hint that the current thread should be paused as
|
||||
/// it is performing a calculation that would benefit from
|
||||
/// not contending with other threads. Atomic operations in
|
||||
/// tight loops (like spin-locks) can benefit from use of this
|
||||
/// intrinsic.
|
||||
///
|
||||
/// - On x86 systems this maps to the `pause` instruction.
|
||||
/// - On ARM systems this maps to the `yield` instruction.
|
||||
///
|
||||
/// Note that this is not an operating system level thread yield,
|
||||
/// it only provides a hint to the CPU that the current thread can
|
||||
/// afford to pause its execution temporarily.
|
||||
/// </summary>
|
||||
public static void Pause() { }
|
||||
|
||||
#if UNITY_BURST_EXPERIMENTAL_PREFETCH_INTRINSIC
|
||||
public enum ReadWrite : int
|
||||
{
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
}
|
||||
|
||||
public enum Locality : int
|
||||
{
|
||||
NoTemporalLocality = 0,
|
||||
LowTemporalLocality = 1,
|
||||
ModerateTemporalLocality = 2,
|
||||
HighTemporalLocality = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefetch a pointer.
|
||||
/// </summary>
|
||||
/// <param name="v">The pointer to prefetch.</param>
|
||||
/// <param name="rw">Whether the pointer will be used for reading or writing.</param>
|
||||
/// <param name="locality">The cache locality of the pointer.</param>
|
||||
public static unsafe void Prefetch(void* v, ReadWrite rw, Locality locality = Locality.HighTemporalLocality) { }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Return the low half of the multiplication of two numbers, and the high part as an out parameter.
|
||||
/// </summary>
|
||||
/// <param name="x">A value to multiply.</param>
|
||||
/// <param name="y">A value to multiply.</param>
|
||||
/// <param name="high">The high-half of the multiplication result.</param>
|
||||
/// <returns>The low-half of the multiplication result.</returns>
|
||||
public static ulong umul128(ulong x, ulong y, out ulong high)
|
||||
{
|
||||
// Provide a software fallback for the cases Burst isn't being used.
|
||||
|
||||
// Split the inputs into high/low sections.
|
||||
ulong xLo = (uint)x;
|
||||
ulong xHi = x >> 32;
|
||||
ulong yLo = (uint)y;
|
||||
ulong yHi = y >> 32;
|
||||
|
||||
// We have to use 4 multiples to compute the full range of the result.
|
||||
ulong hi = xHi * yHi;
|
||||
ulong m1 = xHi * yLo;
|
||||
ulong m2 = yHi * xLo;
|
||||
ulong lo = xLo * yLo;
|
||||
|
||||
ulong m1Lo = (uint)m1;
|
||||
ulong loHi = lo >> 32;
|
||||
ulong m1Hi = m1 >> 32;
|
||||
|
||||
high = hi + m1Hi + ((loHi + m1Lo + m2) >> 32);
|
||||
return x * y;
|
||||
}
|
||||
|
||||
#if UNITY_BURST_EXPERIMENTAL_ATOMIC_INTRINSICS
|
||||
/// <summary>
|
||||
/// Bitwise and as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically and the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and"/>
|
||||
public static int InterlockedAnd(ref int location, int value)
|
||||
{
|
||||
// Provide a software fallback for the cases Burst isn't being used.
|
||||
|
||||
var currentValue = System.Threading.Interlocked.Add(ref location, 0);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var updatedValue = currentValue & value;
|
||||
|
||||
// If nothing would change by and'ing in our thing, bail out early.
|
||||
if (updatedValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
var newValue = System.Threading.Interlocked.CompareExchange(ref location, updatedValue, currentValue);
|
||||
|
||||
// If the original value was the same as the what we just got back from the compare exchange, it means our update succeeded.
|
||||
if (newValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
// Lastly update the last known good value of location and try again!
|
||||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise and as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically and the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and"/>
|
||||
public static uint InterlockedAnd(ref uint location, uint value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ref int locationAsInt = ref Unsafe.AsRef<int>(Unsafe.AsPointer(ref location));
|
||||
int valueAsInt = (int)value;
|
||||
|
||||
return (uint)InterlockedAnd(ref locationAsInt, valueAsInt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise and as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically and the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and"/>
|
||||
public static long InterlockedAnd(ref long location, long value)
|
||||
{
|
||||
// Provide a software fallback for the cases Burst isn't being used.
|
||||
|
||||
var currentValue = System.Threading.Interlocked.Read(ref location);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var updatedValue = currentValue & value;
|
||||
|
||||
// If nothing would change by and'ing in our thing, bail out early.
|
||||
if (updatedValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
var newValue = System.Threading.Interlocked.CompareExchange(ref location, updatedValue, currentValue);
|
||||
|
||||
// If the original value was the same as the what we just got back from the compare exchange, it means our update succeeded.
|
||||
if (newValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
// Lastly update the last known good value of location and try again!
|
||||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise and as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically and the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.and"/>
|
||||
public static ulong InterlockedAnd(ref ulong location, ulong value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ref long locationAsInt = ref Unsafe.AsRef<long>(Unsafe.AsPointer(ref location));
|
||||
long valueAsInt = (long)value;
|
||||
|
||||
return (ulong)InterlockedAnd(ref locationAsInt, valueAsInt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise or as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically or the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or"/>
|
||||
public static int InterlockedOr(ref int location, int value)
|
||||
{
|
||||
// Provide a software fallback for the cases Burst isn't being used.
|
||||
|
||||
var currentValue = System.Threading.Interlocked.Add(ref location, 0);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var updatedValue = currentValue | value;
|
||||
|
||||
// If nothing would change by or'ing in our thing, bail out early.
|
||||
if (updatedValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
var newValue = System.Threading.Interlocked.CompareExchange(ref location, updatedValue, currentValue);
|
||||
|
||||
// If the original value was the same as the what we just got back from the compare exchange, it means our update succeeded.
|
||||
if (newValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
// Lastly update the last known good value of location and try again!
|
||||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise or as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically or the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or"/>
|
||||
public static uint InterlockedOr(ref uint location, uint value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ref int locationAsInt = ref Unsafe.AsRef<int>(Unsafe.AsPointer(ref location));
|
||||
int valueAsInt = (int)value;
|
||||
|
||||
return (uint)InterlockedOr(ref locationAsInt, valueAsInt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise or as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically or the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or"/>
|
||||
public static long InterlockedOr(ref long location, long value)
|
||||
{
|
||||
// Provide a software fallback for the cases Burst isn't being used.
|
||||
|
||||
var currentValue = System.Threading.Interlocked.Read(ref location);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var updatedValue = currentValue | value;
|
||||
|
||||
// If nothing would change by or'ing in our thing, bail out early.
|
||||
if (updatedValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
var newValue = System.Threading.Interlocked.CompareExchange(ref location, updatedValue, currentValue);
|
||||
|
||||
// If the original value was the same as the what we just got back from the compare exchange, it means our update succeeded.
|
||||
if (newValue == currentValue)
|
||||
{
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
// Lastly update the last known good value of location and try again!
|
||||
currentValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitwise or as an atomic operation.
|
||||
/// </summary>
|
||||
/// <param name="location">Where to atomically or the result into.</param>
|
||||
/// <param name="value">The value to be combined.</param>
|
||||
/// <returns>The original value in <paramref name="location" />.</returns>
|
||||
/// <remarks>Using the return value of this intrinsic may result in worse code-generation on some platforms (a compare-exchange loop), rather than a single atomic instruction being generated.</remarks>
|
||||
/// <seealso cref="https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.or"/>
|
||||
public static ulong InterlockedOr(ref ulong location, ulong value)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ref long locationAsInt = ref Unsafe.AsRef<long>(Unsafe.AsPointer(ref location));
|
||||
long valueAsInt = (long)value;
|
||||
|
||||
return (ulong)InterlockedOr(ref locationAsInt, valueAsInt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||
// expose the type to btests via Unity.Burst.dll
|
||||
#if BURST_INTERNAL
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class BurstTargetCpuAttribute : Attribute
|
||||
{
|
||||
public BurstTargetCpuAttribute(BurstTargetCpu TargetCpu)
|
||||
{
|
||||
this.TargetCpu = TargetCpu;
|
||||
}
|
||||
|
||||
public readonly BurstTargetCpu TargetCpu;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Unity.Burst.Intrinsics
|
||||
{
|
||||
internal unsafe class V64DebugView
|
||||
{
|
||||
v64 m_Value;
|
||||
|
||||
public V64DebugView(v64 value)
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe byte[] Byte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new byte[]
|
||||
{
|
||||
m_Value.Byte0, m_Value.Byte1, m_Value.Byte2, m_Value.Byte3,
|
||||
m_Value.Byte4, m_Value.Byte5, m_Value.Byte6, m_Value.Byte7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe sbyte[] SByte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new sbyte[]
|
||||
{
|
||||
m_Value.SByte0, m_Value.SByte1, m_Value.SByte2, m_Value.SByte3,
|
||||
m_Value.SByte4, m_Value.SByte5, m_Value.SByte6, m_Value.SByte7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ushort[] UShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ushort[]
|
||||
{
|
||||
m_Value.UShort0, m_Value.UShort1, m_Value.UShort2, m_Value.UShort3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe short[] SShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new short[]
|
||||
{
|
||||
m_Value.SShort0, m_Value.SShort1, m_Value.SShort2, m_Value.SShort3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe uint[] UInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
m_Value.UInt0, m_Value.UInt1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe int[] SInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new int[]
|
||||
{
|
||||
m_Value.SInt0, m_Value.SInt1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe float[] Float
|
||||
{
|
||||
get
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
m_Value.Float0, m_Value.Float1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe long[] SLong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new long[]
|
||||
{
|
||||
m_Value.SLong0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ulong[] ULong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ulong[]
|
||||
{
|
||||
m_Value.ULong0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe double[] Double
|
||||
{
|
||||
get
|
||||
{
|
||||
return new double[]
|
||||
{
|
||||
m_Value.Double0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class V128DebugView
|
||||
{
|
||||
v128 m_Value;
|
||||
|
||||
public V128DebugView(v128 value)
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe byte[] Byte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new byte[]
|
||||
{
|
||||
m_Value.Byte0, m_Value.Byte1, m_Value.Byte2, m_Value.Byte3,
|
||||
m_Value.Byte4, m_Value.Byte5, m_Value.Byte6, m_Value.Byte7,
|
||||
m_Value.Byte8, m_Value.Byte9, m_Value.Byte10, m_Value.Byte11,
|
||||
m_Value.Byte12, m_Value.Byte13, m_Value.Byte14, m_Value.Byte15,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe sbyte[] SByte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new sbyte[]
|
||||
{
|
||||
m_Value.SByte0, m_Value.SByte1, m_Value.SByte2, m_Value.SByte3,
|
||||
m_Value.SByte4, m_Value.SByte5, m_Value.SByte6, m_Value.SByte7,
|
||||
m_Value.SByte8, m_Value.SByte9, m_Value.SByte10, m_Value.SByte11,
|
||||
m_Value.SByte12, m_Value.SByte13, m_Value.SByte14, m_Value.SByte15,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ushort[] UShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ushort[]
|
||||
{
|
||||
m_Value.UShort0, m_Value.UShort1, m_Value.UShort2, m_Value.UShort3,
|
||||
m_Value.UShort4, m_Value.UShort5, m_Value.UShort6, m_Value.UShort7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe short[] SShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new short[]
|
||||
{
|
||||
m_Value.SShort0, m_Value.SShort1, m_Value.SShort2, m_Value.SShort3,
|
||||
m_Value.SShort4, m_Value.SShort5, m_Value.SShort6, m_Value.SShort7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe uint[] UInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
m_Value.UInt0, m_Value.UInt1, m_Value.UInt2, m_Value.UInt3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe int[] SInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new int[]
|
||||
{
|
||||
m_Value.SInt0, m_Value.SInt1, m_Value.SInt2, m_Value.SInt3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe float[] Float
|
||||
{
|
||||
get
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
m_Value.Float0, m_Value.Float1, m_Value.Float2, m_Value.Float3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe long[] SLong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new long[]
|
||||
{
|
||||
m_Value.SLong0, m_Value.SLong1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ulong[] ULong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ulong[]
|
||||
{
|
||||
m_Value.ULong0, m_Value.ULong1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe double[] Double
|
||||
{
|
||||
get
|
||||
{
|
||||
return new double[]
|
||||
{
|
||||
m_Value.Double0, m_Value.Double1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class V256DebugView
|
||||
{
|
||||
v256 m_Value;
|
||||
|
||||
public V256DebugView(v256 value)
|
||||
{
|
||||
m_Value = value;
|
||||
}
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe byte[] Byte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new byte[]
|
||||
{
|
||||
m_Value.Byte0, m_Value.Byte1, m_Value.Byte2, m_Value.Byte3,
|
||||
m_Value.Byte4, m_Value.Byte5, m_Value.Byte6, m_Value.Byte7,
|
||||
m_Value.Byte8, m_Value.Byte9, m_Value.Byte10, m_Value.Byte11,
|
||||
m_Value.Byte12, m_Value.Byte13, m_Value.Byte14, m_Value.Byte15,
|
||||
m_Value.Byte16, m_Value.Byte17, m_Value.Byte18, m_Value.Byte19,
|
||||
m_Value.Byte20, m_Value.Byte21, m_Value.Byte22, m_Value.Byte23,
|
||||
m_Value.Byte24, m_Value.Byte25, m_Value.Byte26, m_Value.Byte27,
|
||||
m_Value.Byte28, m_Value.Byte29, m_Value.Byte30, m_Value.Byte31,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe sbyte[] SByte
|
||||
{
|
||||
get
|
||||
{
|
||||
return new sbyte[]
|
||||
{
|
||||
m_Value.SByte0, m_Value.SByte1, m_Value.SByte2, m_Value.SByte3,
|
||||
m_Value.SByte4, m_Value.SByte5, m_Value.SByte6, m_Value.SByte7,
|
||||
m_Value.SByte8, m_Value.SByte9, m_Value.SByte10, m_Value.SByte11,
|
||||
m_Value.SByte12, m_Value.SByte13, m_Value.SByte14, m_Value.SByte15,
|
||||
m_Value.SByte16, m_Value.SByte17, m_Value.SByte18, m_Value.SByte19,
|
||||
m_Value.SByte20, m_Value.SByte21, m_Value.SByte22, m_Value.SByte23,
|
||||
m_Value.SByte24, m_Value.SByte25, m_Value.SByte26, m_Value.SByte27,
|
||||
m_Value.SByte28, m_Value.SByte29, m_Value.SByte30, m_Value.SByte31,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ushort[] UShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ushort[]
|
||||
{
|
||||
m_Value.UShort0, m_Value.UShort1, m_Value.UShort2, m_Value.UShort3,
|
||||
m_Value.UShort4, m_Value.UShort5, m_Value.UShort6, m_Value.UShort7,
|
||||
m_Value.UShort8, m_Value.UShort9, m_Value.UShort10, m_Value.UShort11,
|
||||
m_Value.UShort12, m_Value.UShort13, m_Value.UShort14, m_Value.UShort15,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe short[] SShort
|
||||
{
|
||||
get
|
||||
{
|
||||
return new short[]
|
||||
{
|
||||
m_Value.SShort0, m_Value.SShort1, m_Value.SShort2, m_Value.SShort3,
|
||||
m_Value.SShort4, m_Value.SShort5, m_Value.SShort6, m_Value.SShort7,
|
||||
m_Value.SShort8, m_Value.SShort9, m_Value.SShort10, m_Value.SShort11,
|
||||
m_Value.SShort12, m_Value.SShort13, m_Value.SShort14, m_Value.SShort15,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe uint[] UInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new uint[]
|
||||
{
|
||||
m_Value.UInt0, m_Value.UInt1, m_Value.UInt2, m_Value.UInt3,
|
||||
m_Value.UInt4, m_Value.UInt5, m_Value.UInt6, m_Value.UInt7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe int[] SInt
|
||||
{
|
||||
get
|
||||
{
|
||||
return new int[]
|
||||
{
|
||||
m_Value.SInt0, m_Value.SInt1, m_Value.SInt2, m_Value.SInt3,
|
||||
m_Value.SInt4, m_Value.SInt5, m_Value.SInt6, m_Value.SInt7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe float[] Float
|
||||
{
|
||||
get
|
||||
{
|
||||
return new float[]
|
||||
{
|
||||
m_Value.Float0, m_Value.Float1, m_Value.Float2, m_Value.Float3,
|
||||
m_Value.Float4, m_Value.Float5, m_Value.Float6, m_Value.Float7,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe long[] SLong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new long[]
|
||||
{
|
||||
m_Value.SLong0, m_Value.SLong1, m_Value.SLong2, m_Value.SLong3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe ulong[] ULong
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ulong[]
|
||||
{
|
||||
m_Value.ULong0, m_Value.ULong1, m_Value.ULong2, m_Value.ULong3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_DOTSPLAYER
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
|
||||
#endif
|
||||
public unsafe double[] Double
|
||||
{
|
||||
get
|
||||
{
|
||||
return new double[]
|
||||
{
|
||||
m_Value.Double0, m_Value.Double1, m_Value.Double2, m_Value.Double3,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Unity.Burst.Intrinsics.Arm.Neon;
|
||||
using static Unity.Burst.Intrinsics.X86.F16C;
|
||||
|
||||
namespace Unity.Burst.Intrinsics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a 16-bit floating point value (half precision)
|
||||
/// Warning: this type may not be natively supported by your hardware, or its usage may be suboptimal
|
||||
/// </summary>
|
||||
public readonly struct f16 : System.IEquatable<f16>
|
||||
{
|
||||
/// <summary>
|
||||
/// The container for the actual 16-bit half precision floating point value
|
||||
/// </summary>
|
||||
private readonly ushort value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint f32tof16(float x)
|
||||
{
|
||||
if (IsF16CSupported)
|
||||
{
|
||||
var v = new v128();
|
||||
v.Float0 = x;
|
||||
var result = cvtps_ph(v, (int)X86.RoundingMode.FROUND_TRUNC_NOEXC);
|
||||
return result.UShort0;
|
||||
}
|
||||
else if (IsNeonHalfFPSupported)
|
||||
{
|
||||
var v = new v128();
|
||||
v.Float0 = x;
|
||||
var result = vcvt_f16_f32(v);
|
||||
return result.UShort0;
|
||||
}
|
||||
// Managed fallback
|
||||
const int infinity_32 = 255 << 23;
|
||||
const uint msk = 0x7FFFF000u;
|
||||
|
||||
uint ux = asuint(x);
|
||||
uint uux = ux & msk;
|
||||
uint h = (uint)(asuint(min(asfloat(uux) * 1.92592994e-34f, 260042752.0f)) + 0x1000) >> 13; // Clamp to signed infinity if overflowed
|
||||
h = select(h,
|
||||
select(0x7c00u, 0x7e00u, (int)uux > infinity_32),
|
||||
(int)uux >= infinity_32); // NaN->qNaN and Inf->Inf
|
||||
return h | (ux & ~msk) >> 16;
|
||||
}
|
||||
|
||||
/// <summary>Returns the bit pattern of a float as a uint.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint asuint(float x) { return (uint)asint(x); }
|
||||
|
||||
/// <summary>Returns the minimum of two float values.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float min(float x, float y) { return float.IsNaN(y) || x < y ? x : y; }
|
||||
|
||||
/// <summary>Returns b if c is true, a otherwise.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint select(uint a, uint b, bool c) { return c ? b : a; }
|
||||
|
||||
/// <summary>Returns the bit pattern of a uint as a float.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float asfloat(uint x) { return asfloat((int)x); }
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct IntFloatUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int intValue;
|
||||
[FieldOffset(0)]
|
||||
public float floatValue;
|
||||
}
|
||||
|
||||
/// <summary>Returns the bit pattern of an int as a float.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static float asfloat(int x)
|
||||
{
|
||||
IntFloatUnion u;
|
||||
u.floatValue = 0;
|
||||
u.intValue = x;
|
||||
|
||||
return u.floatValue;
|
||||
}
|
||||
|
||||
/// <summary>Returns the bit pattern of a float as an int.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int asint(float x)
|
||||
{
|
||||
IntFloatUnion u;
|
||||
u.intValue = 0;
|
||||
u.floatValue = x;
|
||||
return u.intValue;
|
||||
}
|
||||
|
||||
/// <summary>Constructs a half value from a half value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public f16(f16 x)
|
||||
{
|
||||
value = x.value;
|
||||
}
|
||||
|
||||
/// <summary>Constructs a half value from a float value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public f16(float v)
|
||||
{
|
||||
value = (ushort)f32tof16(v);
|
||||
}
|
||||
|
||||
/// <summary>Returns whether two f16 values are equal.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(f16 lhs, f16 rhs)
|
||||
{
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
/// <summary>Returns whether two f16 values are different.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(f16 lhs, f16 rhs)
|
||||
{
|
||||
return lhs.value != rhs.value;
|
||||
}
|
||||
|
||||
/// <summary>Returns true if the f16 is equal to a given f16, false otherwise.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(f16 rhs)
|
||||
{
|
||||
return value == rhs.value;
|
||||
}
|
||||
|
||||
/// <summary>Returns true if the half is equal to a given half, false otherwise.</summary>
|
||||
public override bool Equals(object o) { return Equals((f16)o); }
|
||||
|
||||
/// <summary>Returns a hash code for the half.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() { return (int)value; }
|
||||
|
||||
}
|
||||
}
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
|
@ -0,0 +1,808 @@
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Unity.Burst.Intrinsics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a 128-bit SIMD value
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[DebuggerTypeProxy(typeof(V128DebugView))]
|
||||
public struct v128
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the 0th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public byte Byte0;
|
||||
/// <summary>
|
||||
/// Get the 1st Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(1)] public byte Byte1;
|
||||
/// <summary>
|
||||
/// Get the 2nd Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public byte Byte2;
|
||||
/// <summary>
|
||||
/// Get the 3rd Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(3)] public byte Byte3;
|
||||
/// <summary>
|
||||
/// Get the 4th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public byte Byte4;
|
||||
/// <summary>
|
||||
/// Get the 5th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(5)] public byte Byte5;
|
||||
/// <summary>
|
||||
/// Get the 6th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public byte Byte6;
|
||||
/// <summary>
|
||||
/// Get the 7th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(7)] public byte Byte7;
|
||||
/// <summary>
|
||||
/// Get the 8th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public byte Byte8;
|
||||
/// <summary>
|
||||
/// Get the 9th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(9)] public byte Byte9;
|
||||
/// <summary>
|
||||
/// Get the 10th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(10)] public byte Byte10;
|
||||
/// <summary>
|
||||
/// Get the 11th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(11)] public byte Byte11;
|
||||
/// <summary>
|
||||
/// Get the 12 Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public byte Byte12;
|
||||
/// <summary>
|
||||
/// Get the 13th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(13)] public byte Byte13;
|
||||
/// <summary>
|
||||
/// Get the 14th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(14)] public byte Byte14;
|
||||
/// <summary>
|
||||
/// Get the 15th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(15)] public byte Byte15;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public sbyte SByte0;
|
||||
/// <summary>
|
||||
/// Get the 1st SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(1)] public sbyte SByte1;
|
||||
/// <summary>
|
||||
/// Get the 2nd SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public sbyte SByte2;
|
||||
/// <summary>
|
||||
/// Get the 3rd SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(3)] public sbyte SByte3;
|
||||
/// <summary>
|
||||
/// Get the 4th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public sbyte SByte4;
|
||||
/// <summary>
|
||||
/// Get the 5th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(5)] public sbyte SByte5;
|
||||
/// <summary>
|
||||
/// Get the 6th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public sbyte SByte6;
|
||||
/// <summary>
|
||||
/// Get the 7th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(7)] public sbyte SByte7;
|
||||
/// <summary>
|
||||
/// Get the 8th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public sbyte SByte8;
|
||||
/// <summary>
|
||||
/// Get the 9th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(9)] public sbyte SByte9;
|
||||
/// <summary>
|
||||
/// Get the 10th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(10)] public sbyte SByte10;
|
||||
/// <summary>
|
||||
/// Get the 11th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(11)] public sbyte SByte11;
|
||||
/// <summary>
|
||||
/// Get the 12th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public sbyte SByte12;
|
||||
/// <summary>
|
||||
/// Get the 13th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(13)] public sbyte SByte13;
|
||||
/// <summary>
|
||||
/// Get the 14th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(14)] public sbyte SByte14;
|
||||
/// <summary>
|
||||
/// Get the 15th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(15)] public sbyte SByte15;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public ushort UShort0;
|
||||
/// <summary>
|
||||
/// Get the 1st UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public ushort UShort1;
|
||||
/// <summary>
|
||||
/// Get the 2nd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public ushort UShort2;
|
||||
/// <summary>
|
||||
/// Get the 3rd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public ushort UShort3;
|
||||
/// <summary>
|
||||
/// Get the 4th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public ushort UShort4;
|
||||
/// <summary>
|
||||
/// Get the 5th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(10)] public ushort UShort5;
|
||||
/// <summary>
|
||||
/// Get the 6th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public ushort UShort6;
|
||||
/// <summary>
|
||||
/// Get the 7th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(14)] public ushort UShort7;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public short SShort0;
|
||||
/// <summary>
|
||||
/// Get the 1st UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public short SShort1;
|
||||
/// <summary>
|
||||
/// Get the 2nd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public short SShort2;
|
||||
/// <summary>
|
||||
/// Get the 3rd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public short SShort3;
|
||||
/// <summary>
|
||||
/// Get the 4th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public short SShort4;
|
||||
/// <summary>
|
||||
/// Get the 5th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(10)] public short SShort5;
|
||||
/// <summary>
|
||||
/// Get the 6th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public short SShort6;
|
||||
/// <summary>
|
||||
/// Get the 7th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(14)] public short SShort7;
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Get the 0th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public f16 Half0;
|
||||
/// <summary>
|
||||
/// Get the 1st f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public f16 Half1;
|
||||
/// <summary>
|
||||
/// Get the 2nd f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public f16 Half2;
|
||||
/// <summary>
|
||||
/// Get the 3rd f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public f16 Half3;
|
||||
/// <summary>
|
||||
/// Get the 4th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public f16 Half4;
|
||||
/// <summary>
|
||||
/// Get the 5th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(10)] public f16 Half5;
|
||||
/// <summary>
|
||||
/// Get the 6th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public f16 Half6;
|
||||
/// <summary>
|
||||
/// Get the 7th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(14)] public f16 Half7;
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public uint UInt0;
|
||||
/// <summary>
|
||||
/// Get the 1st UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public uint UInt1;
|
||||
/// <summary>
|
||||
/// Get the 2nd UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public uint UInt2;
|
||||
/// <summary>
|
||||
/// Get the 3rd UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public uint UInt3;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public int SInt0;
|
||||
/// <summary>
|
||||
/// Get the 1st SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public int SInt1;
|
||||
/// <summary>
|
||||
/// Get the 2nd SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public int SInt2;
|
||||
/// <summary>
|
||||
/// Get the 3rd SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public int SInt3;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th ULong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public ulong ULong0;
|
||||
/// <summary>
|
||||
/// Get the 1st ULong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public ulong ULong1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SLong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public long SLong0;
|
||||
/// <summary>
|
||||
/// Get the 1st SLong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public long SLong1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public float Float0;
|
||||
/// <summary>
|
||||
/// Get the 1st Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public float Float1;
|
||||
/// <summary>
|
||||
/// Get the 2nd Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public float Float2;
|
||||
/// <summary>
|
||||
/// Get the 3rd Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public float Float3;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th Double of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public double Double0;
|
||||
/// <summary>
|
||||
/// Get the 1st Double of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public double Double1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the low half of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v64 Lo64;
|
||||
/// <summary>
|
||||
/// Get the high half of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public v64 Hi64;
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single byte across the v128
|
||||
/// </summary>
|
||||
/// <param name="b">Splatted byte.</param>
|
||||
public v128(byte b)
|
||||
{
|
||||
this = default(v128);
|
||||
Byte0 = Byte1 = Byte2 = Byte3 = Byte4 = Byte5 = Byte6 = Byte7 = Byte8 = Byte9 = Byte10 = Byte11 = Byte12 = Byte13 = Byte14 = Byte15 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 16 bytes
|
||||
/// </summary>
|
||||
/// <param name="a">byte a.</param>
|
||||
/// <param name="b">byte b.</param>
|
||||
/// <param name="c">byte c.</param>
|
||||
/// <param name="d">byte d.</param>
|
||||
/// <param name="e">byte e.</param>
|
||||
/// <param name="f">byte f.</param>
|
||||
/// <param name="g">byte g.</param>
|
||||
/// <param name="h">byte h.</param>
|
||||
/// <param name="i">byte i.</param>
|
||||
/// <param name="j">byte j.</param>
|
||||
/// <param name="k">byte k.</param>
|
||||
/// <param name="l">byte l.</param>
|
||||
/// <param name="m">byte m.</param>
|
||||
/// <param name="n">byte n.</param>
|
||||
/// <param name="o">byte o.</param>
|
||||
/// <param name="p">byte p.</param>
|
||||
public v128(
|
||||
byte a, byte b, byte c, byte d,
|
||||
byte e, byte f, byte g, byte h,
|
||||
byte i, byte j, byte k, byte l,
|
||||
byte m, byte n, byte o, byte p)
|
||||
{
|
||||
this = default(v128);
|
||||
Byte0 = a;
|
||||
Byte1 = b;
|
||||
Byte2 = c;
|
||||
Byte3 = d;
|
||||
Byte4 = e;
|
||||
Byte5 = f;
|
||||
Byte6 = g;
|
||||
Byte7 = h;
|
||||
Byte8 = i;
|
||||
Byte9 = j;
|
||||
Byte10 = k;
|
||||
Byte11 = l;
|
||||
Byte12 = m;
|
||||
Byte13 = n;
|
||||
Byte14 = o;
|
||||
Byte15 = p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single sbyte across the v128
|
||||
/// </summary>
|
||||
/// <param name="b">Splatted sbyte.</param>
|
||||
public v128(sbyte b)
|
||||
{
|
||||
this = default(v128);
|
||||
SByte0 = SByte1 = SByte2 = SByte3 = SByte4 = SByte5 = SByte6 = SByte7 = SByte8 = SByte9 = SByte10 = SByte11 = SByte12 = SByte13 = SByte14 = SByte15 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 16 sbytes
|
||||
/// </summary>
|
||||
/// <param name="a">sbyte a.</param>
|
||||
/// <param name="b">sbyte b.</param>
|
||||
/// <param name="c">sbyte c.</param>
|
||||
/// <param name="d">sbyte d.</param>
|
||||
/// <param name="e">sbyte e.</param>
|
||||
/// <param name="f">sbyte f.</param>
|
||||
/// <param name="g">sbyte g.</param>
|
||||
/// <param name="h">sbyte h.</param>
|
||||
/// <param name="i">sbyte i.</param>
|
||||
/// <param name="j">sbyte j.</param>
|
||||
/// <param name="k">sbyte k.</param>
|
||||
/// <param name="l">sbyte l.</param>
|
||||
/// <param name="m">sbyte m.</param>
|
||||
/// <param name="n">sbyte n.</param>
|
||||
/// <param name="o">sbyte o.</param>
|
||||
/// <param name="p">sbyte p.</param>
|
||||
public v128(
|
||||
sbyte a, sbyte b, sbyte c, sbyte d,
|
||||
sbyte e, sbyte f, sbyte g, sbyte h,
|
||||
sbyte i, sbyte j, sbyte k, sbyte l,
|
||||
sbyte m, sbyte n, sbyte o, sbyte p)
|
||||
{
|
||||
this = default(v128);
|
||||
SByte0 = a;
|
||||
SByte1 = b;
|
||||
SByte2 = c;
|
||||
SByte3 = d;
|
||||
SByte4 = e;
|
||||
SByte5 = f;
|
||||
SByte6 = g;
|
||||
SByte7 = h;
|
||||
SByte8 = i;
|
||||
SByte9 = j;
|
||||
SByte10 = k;
|
||||
SByte11 = l;
|
||||
SByte12 = m;
|
||||
SByte13 = n;
|
||||
SByte14 = o;
|
||||
SByte15 = p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single short across the v128
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted short.</param>
|
||||
public v128(short v)
|
||||
{
|
||||
this = default(v128);
|
||||
SShort0 = SShort1 = SShort2 = SShort3 = SShort4 = SShort5 = SShort6 = SShort7 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 8 shorts
|
||||
/// </summary>
|
||||
/// <param name="a">short a.</param>
|
||||
/// <param name="b">short b.</param>
|
||||
/// <param name="c">short c.</param>
|
||||
/// <param name="d">short d.</param>
|
||||
/// <param name="e">short e.</param>
|
||||
/// <param name="f">short f.</param>
|
||||
/// <param name="g">short g.</param>
|
||||
/// <param name="h">short h.</param>
|
||||
public v128(short a, short b, short c, short d, short e, short f, short g, short h)
|
||||
{
|
||||
this = default(v128);
|
||||
SShort0 = a;
|
||||
SShort1 = b;
|
||||
SShort2 = c;
|
||||
SShort3 = d;
|
||||
SShort4 = e;
|
||||
SShort5 = f;
|
||||
SShort6 = g;
|
||||
SShort7 = h;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single ushort across the v128
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted ushort.</param>
|
||||
public v128(ushort v)
|
||||
{
|
||||
this = default(v128);
|
||||
UShort0 = UShort1 = UShort2 = UShort3 = UShort4 = UShort5 = UShort6 = UShort7 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 8 ushorts
|
||||
/// </summary>
|
||||
/// <param name="a">ushort a.</param>
|
||||
/// <param name="b">ushort b.</param>
|
||||
/// <param name="c">ushort c.</param>
|
||||
/// <param name="d">ushort d.</param>
|
||||
/// <param name="e">ushort e.</param>
|
||||
/// <param name="f">ushort f.</param>
|
||||
/// <param name="g">ushort g.</param>
|
||||
/// <param name="h">ushort h.</param>
|
||||
public v128(ushort a, ushort b, ushort c, ushort d, ushort e, ushort f, ushort g, ushort h)
|
||||
{
|
||||
this = default(v128);
|
||||
UShort0 = a;
|
||||
UShort1 = b;
|
||||
UShort2 = c;
|
||||
UShort3 = d;
|
||||
UShort4 = e;
|
||||
UShort5 = f;
|
||||
UShort6 = g;
|
||||
UShort7 = h;
|
||||
}
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Splat a single f16 across the v128
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted f16.</param>
|
||||
public v128(f16 v)
|
||||
{
|
||||
this = default(v128);
|
||||
Half0 = Half1 = Half2 = Half3 = Half4 = Half5 = Half6 = Half7 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 8 half's
|
||||
/// </summary>
|
||||
/// <param name="a">f16 a.</param>
|
||||
/// <param name="b">f16 b.</param>
|
||||
/// <param name="c">f16 c.</param>
|
||||
/// <param name="d">f16 d.</param>
|
||||
/// <param name="e">f16 e.</param>
|
||||
/// <param name="f">f16 f.</param>
|
||||
/// <param name="g">f16 g.</param>
|
||||
/// <param name="h">f16 h.</param>
|
||||
public v128(f16 a, f16 b, f16 c, f16 d, f16 e, f16 f, f16 g, f16 h)
|
||||
{
|
||||
this = default(v128);
|
||||
Half0 = a;
|
||||
Half1 = b;
|
||||
Half2 = c;
|
||||
Half3 = d;
|
||||
Half4 = e;
|
||||
Half5 = f;
|
||||
Half6 = g;
|
||||
Half7 = h;
|
||||
}
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single int across the v128
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted int.</param>
|
||||
public v128(int v)
|
||||
{
|
||||
this = default(v128);
|
||||
SInt0 = SInt1 = SInt2 = SInt3 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 4 ints
|
||||
/// </summary>
|
||||
/// <param name="a">int a.</param>
|
||||
/// <param name="b">int b.</param>
|
||||
/// <param name="c">int c.</param>
|
||||
/// <param name="d">int d.</param>
|
||||
public v128(int a, int b, int c, int d)
|
||||
{
|
||||
this = default(v128);
|
||||
SInt0 = a;
|
||||
SInt1 = b;
|
||||
SInt2 = c;
|
||||
SInt3 = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single uint across the v128
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted uint.</param>
|
||||
public v128(uint v)
|
||||
{
|
||||
this = default(v128);
|
||||
UInt0 = UInt1 = UInt2 = UInt3 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 4 uints
|
||||
/// </summary>
|
||||
/// <param name="a">uint a.</param>
|
||||
/// <param name="b">uint b.</param>
|
||||
/// <param name="c">uint c.</param>
|
||||
/// <param name="d">uint d.</param>
|
||||
public v128(uint a, uint b, uint c, uint d)
|
||||
{
|
||||
this = default(v128);
|
||||
UInt0 = a;
|
||||
UInt1 = b;
|
||||
UInt2 = c;
|
||||
UInt3 = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single float across the v128
|
||||
/// </summary>
|
||||
/// <param name="f">Splatted float.</param>
|
||||
public v128(float f)
|
||||
{
|
||||
this = default(v128);
|
||||
Float0 = Float1 = Float2 = Float3 = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 4 floats
|
||||
/// </summary>
|
||||
/// <param name="a">float a.</param>
|
||||
/// <param name="b">float b.</param>
|
||||
/// <param name="c">float c.</param>
|
||||
/// <param name="d">float d.</param>
|
||||
public v128(float a, float b, float c, float d)
|
||||
{
|
||||
this = default(v128);
|
||||
Float0 = a;
|
||||
Float1 = b;
|
||||
Float2 = c;
|
||||
Float3 = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single double across the v128
|
||||
/// </summary>
|
||||
/// <param name="f">Splatted double.</param>
|
||||
public v128(double f)
|
||||
{
|
||||
this = default(v128);
|
||||
Double0 = Double1 = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 2 doubles
|
||||
/// </summary>
|
||||
/// <param name="a">double a.</param>
|
||||
/// <param name="b">double b.</param>
|
||||
public v128(double a, double b)
|
||||
{
|
||||
this = default(v128);
|
||||
Double0 = a;
|
||||
Double1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single long across the v128
|
||||
/// </summary>
|
||||
/// <param name="f">Splatted long.</param>
|
||||
public v128(long f)
|
||||
{
|
||||
this = default(v128);
|
||||
SLong0 = SLong1 = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 2 longs
|
||||
/// </summary>
|
||||
/// <param name="a">long a.</param>
|
||||
/// <param name="b">long b.</param>
|
||||
public v128(long a, long b)
|
||||
{
|
||||
this = default(v128);
|
||||
SLong0 = a;
|
||||
SLong1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single ulong across the v128
|
||||
/// </summary>
|
||||
/// <param name="f">Splatted ulong.</param>
|
||||
public v128(ulong f)
|
||||
{
|
||||
this = default(v128);
|
||||
ULong0 = ULong1 = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 2 ulongs
|
||||
/// </summary>
|
||||
/// <param name="a">ulong a.</param>
|
||||
/// <param name="b">ulong b.</param>
|
||||
public v128(ulong a, ulong b)
|
||||
{
|
||||
this = default(v128);
|
||||
ULong0 = a;
|
||||
ULong1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128 with 2 v64's
|
||||
/// </summary>
|
||||
/// <param name="lo">Low half of v64.</param>
|
||||
/// <param name="hi">High half of v64.</param>
|
||||
public v128(v64 lo, v64 hi)
|
||||
{
|
||||
this = default(v128);
|
||||
Lo64 = lo;
|
||||
Hi64 = hi;
|
||||
}
|
||||
}
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Represents a 256-bit SIMD value (Arm only)
|
||||
/// (a combination of 2 128-bit values, equivalent to Arm Neon *x2 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v128x2
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v128 v128_0;
|
||||
/// <summary>
|
||||
/// Get the second 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public v128 v128_1;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128x2 with 2 v128's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v128.</param>
|
||||
/// <param name="v1">Second v128.</param>
|
||||
public v128x2(v128 v0, v128 v1)
|
||||
{
|
||||
this = default(v128x2);
|
||||
v128_0 = v0;
|
||||
v128_1 = v1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 384-bit SIMD value (Arm only)
|
||||
/// (a combination of 3 128-bit values, equivalent to Arm Neon *x3 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v128x3
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v128 v128_0;
|
||||
/// <summary>
|
||||
/// Get the second 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public v128 v128_1;
|
||||
/// <summary>
|
||||
/// Get the third 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(32)] public v128 v128_2;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128x3 with 3 v128's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v128.</param>
|
||||
/// <param name="v1">Second v128.</param>
|
||||
/// <param name="v2">Third v128.</param>
|
||||
public v128x3(v128 v0, v128 v1, v128 v2)
|
||||
{
|
||||
this = default(v128x3);
|
||||
v128_0 = v0;
|
||||
v128_1 = v1;
|
||||
v128_2 = v2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 512-bit SIMD value (Arm only)
|
||||
/// (a combination of 4 128-bit values, equivalent to Arm Neon *x4 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v128x4
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v128 v128_0;
|
||||
/// <summary>
|
||||
/// Get the second 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public v128 v128_1;
|
||||
/// <summary>
|
||||
/// Get the third 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(32)] public v128 v128_2;
|
||||
/// <summary>
|
||||
/// Get the fourth 128 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(48)] public v128 v128_3;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v128x4 with 4 v128's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v128.</param>
|
||||
/// <param name="v1">Second v128.</param>
|
||||
/// <param name="v2">Third v128.</param>
|
||||
/// <param name="v3">Fourth v128.</param>
|
||||
public v128x4(v128 v0, v128 v1, v128 v2, v128 v3)
|
||||
{
|
||||
this = default(v128x4);
|
||||
v128_0 = v0;
|
||||
v128_1 = v1;
|
||||
v128_2 = v2;
|
||||
v128_3 = v3;
|
||||
}
|
||||
}
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,530 @@
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Unity.Burst.Intrinsics
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a 64-bit SIMD value (Arm only)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
[DebuggerTypeProxy(typeof(V64DebugView))]
|
||||
public struct v64
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the 0th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public byte Byte0;
|
||||
/// <summary>
|
||||
/// Get the 1st Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(1)] public byte Byte1;
|
||||
/// <summary>
|
||||
/// Get the 2nd Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public byte Byte2;
|
||||
/// <summary>
|
||||
/// Get the 3rd Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(3)] public byte Byte3;
|
||||
/// <summary>
|
||||
/// Get the 4th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public byte Byte4;
|
||||
/// <summary>
|
||||
/// Get the 5th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(5)] public byte Byte5;
|
||||
/// <summary>
|
||||
/// Get the 6th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public byte Byte6;
|
||||
/// <summary>
|
||||
/// Get the 7th Byte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(7)] public byte Byte7;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public sbyte SByte0;
|
||||
/// <summary>
|
||||
/// Get the 1st SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(1)] public sbyte SByte1;
|
||||
/// <summary>
|
||||
/// Get the 2nd SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public sbyte SByte2;
|
||||
/// <summary>
|
||||
/// Get the 3rd SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(3)] public sbyte SByte3;
|
||||
/// <summary>
|
||||
/// Get the 4th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public sbyte SByte4;
|
||||
/// <summary>
|
||||
/// Get the 5th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(5)] public sbyte SByte5;
|
||||
/// <summary>
|
||||
/// Get the 6th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public sbyte SByte6;
|
||||
/// <summary>
|
||||
/// Get the 7th SByte of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(7)] public sbyte SByte7;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public ushort UShort0;
|
||||
/// <summary>
|
||||
/// Get the 1st UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public ushort UShort1;
|
||||
/// <summary>
|
||||
/// Get the 2nd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public ushort UShort2;
|
||||
/// <summary>
|
||||
/// Get the 3rd UShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public ushort UShort3;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public short SShort0;
|
||||
/// <summary>
|
||||
/// Get the 1st SShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public short SShort1;
|
||||
/// <summary>
|
||||
/// Get the 2nd SShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public short SShort2;
|
||||
/// <summary>
|
||||
/// Get the 3rd SShort of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public short SShort3;
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Get the 0th f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public f16 Half0;
|
||||
/// <summary>
|
||||
/// Get the 1st f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(2)] public f16 Half1;
|
||||
/// <summary>
|
||||
/// Get the 2nd f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public f16 Half2;
|
||||
/// <summary>
|
||||
/// Get the 3rd f16 of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(6)] public f16 Half3;
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public uint UInt0;
|
||||
/// <summary>
|
||||
/// Get the 1st UInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public uint UInt1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public int SInt0;
|
||||
/// <summary>
|
||||
/// Get the 1st SInt of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public int SInt1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th ULong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public ulong ULong0;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th SLong of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public long SLong0;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public float Float0;
|
||||
/// <summary>
|
||||
/// Get the 1st Float of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public float Float1;
|
||||
|
||||
/// <summary>
|
||||
/// Get the 0th Double of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public double Double0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single byte across the v64
|
||||
/// </summary>
|
||||
/// <param name="b">Splatted byte</param>
|
||||
public v64(byte b)
|
||||
{
|
||||
this = default(v64);
|
||||
Byte0 = Byte1 = Byte2 = Byte3 = Byte4 = Byte5 = Byte6 = Byte7 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 8 bytes
|
||||
/// </summary>
|
||||
/// <param name="a">byte a</param>
|
||||
/// <param name="b">byte b</param>
|
||||
/// <param name="c">byte c</param>
|
||||
/// <param name="d">byte d</param>
|
||||
/// <param name="e">byte e</param>
|
||||
/// <param name="f">byte f</param>
|
||||
/// <param name="g">byte g</param>
|
||||
/// <param name="h">byte h</param>
|
||||
public v64(
|
||||
byte a, byte b, byte c, byte d,
|
||||
byte e, byte f, byte g, byte h)
|
||||
{
|
||||
this = default(v64);
|
||||
Byte0 = a;
|
||||
Byte1 = b;
|
||||
Byte2 = c;
|
||||
Byte3 = d;
|
||||
Byte4 = e;
|
||||
Byte5 = f;
|
||||
Byte6 = g;
|
||||
Byte7 = h;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single sbyte across the v64
|
||||
/// </summary>
|
||||
/// <param name="b">Splatted sbyte</param>
|
||||
public v64(sbyte b)
|
||||
{
|
||||
this = default(v64);
|
||||
SByte0 = SByte1 = SByte2 = SByte3 = SByte4 = SByte5 = SByte6 = SByte7 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 8 sbytes
|
||||
/// </summary>
|
||||
/// <param name="a">sbyte a</param>
|
||||
/// <param name="b">sbyte b</param>
|
||||
/// <param name="c">sbyte c</param>
|
||||
/// <param name="d">sbyte d</param>
|
||||
/// <param name="e">sbyte e</param>
|
||||
/// <param name="f">sbyte f</param>
|
||||
/// <param name="g">sbyte g</param>
|
||||
/// <param name="h">sbyte h</param>
|
||||
public v64(
|
||||
sbyte a, sbyte b, sbyte c, sbyte d,
|
||||
sbyte e, sbyte f, sbyte g, sbyte h)
|
||||
{
|
||||
this = default(v64);
|
||||
SByte0 = a;
|
||||
SByte1 = b;
|
||||
SByte2 = c;
|
||||
SByte3 = d;
|
||||
SByte4 = e;
|
||||
SByte5 = f;
|
||||
SByte6 = g;
|
||||
SByte7 = h;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single short across the v64
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted short</param>
|
||||
public v64(short v)
|
||||
{
|
||||
this = default(v64);
|
||||
SShort0 = SShort1 = SShort2 = SShort3 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 4 shorts
|
||||
/// </summary>
|
||||
/// <param name="a">short a</param>
|
||||
/// <param name="b">short b</param>
|
||||
/// <param name="c">short c</param>
|
||||
/// <param name="d">short d</param>
|
||||
public v64(short a, short b, short c, short d)
|
||||
{
|
||||
this = default(v64);
|
||||
SShort0 = a;
|
||||
SShort1 = b;
|
||||
SShort2 = c;
|
||||
SShort3 = d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single ushort across the v64
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted ushort</param>
|
||||
public v64(ushort v)
|
||||
{
|
||||
this = default(v64);
|
||||
UShort0 = UShort1 = UShort2 = UShort3 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 4 ushorts
|
||||
/// </summary>
|
||||
/// <param name="a">ushort a</param>
|
||||
/// <param name="b">ushort b</param>
|
||||
/// <param name="c">ushort c</param>
|
||||
/// <param name="d">ushort d</param>
|
||||
public v64(ushort a, ushort b, ushort c, ushort d)
|
||||
{
|
||||
this = default(v64);
|
||||
UShort0 = a;
|
||||
UShort1 = b;
|
||||
UShort2 = c;
|
||||
UShort3 = d;
|
||||
}
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Splat a single f16 across the v64
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted f16</param>
|
||||
public v64(f16 v)
|
||||
{
|
||||
this = default(v64);
|
||||
Half0 = Half1 = Half2 = Half3 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 4 half's
|
||||
/// </summary>
|
||||
/// <param name="a">f16 a</param>
|
||||
/// <param name="b">f16 b</param>
|
||||
/// <param name="c">f16 c</param>
|
||||
/// <param name="d">f16 d</param>
|
||||
public v64(f16 a, f16 b, f16 c, f16 d)
|
||||
{
|
||||
this = default(v64);
|
||||
Half0 = a;
|
||||
Half1 = b;
|
||||
Half2 = c;
|
||||
Half3 = d;
|
||||
}
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single int across the v64
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted int</param>
|
||||
public v64(int v)
|
||||
{
|
||||
this = default(v64);
|
||||
SInt0 = SInt1 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 2 ints
|
||||
/// </summary>
|
||||
/// <param name="a">int a</param>
|
||||
/// <param name="b">int b</param>
|
||||
public v64(int a, int b)
|
||||
{
|
||||
this = default(v64);
|
||||
SInt0 = a;
|
||||
SInt1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single uint across the v64
|
||||
/// </summary>
|
||||
/// <param name="v">Splatted uint</param>
|
||||
public v64(uint v)
|
||||
{
|
||||
this = default(v64);
|
||||
UInt0 = UInt1 = v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 2 uints
|
||||
/// </summary>
|
||||
/// <param name="a">uint a</param>
|
||||
/// <param name="b">uint b</param>
|
||||
public v64(uint a, uint b)
|
||||
{
|
||||
this = default(v64);
|
||||
UInt0 = a;
|
||||
UInt1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splat a single float across the v64
|
||||
/// </summary>
|
||||
/// <param name="f">Splatted float</param>
|
||||
public v64(float f)
|
||||
{
|
||||
this = default(v64);
|
||||
Float0 = Float1 = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with 2 floats
|
||||
/// </summary>
|
||||
/// <param name="a">float a</param>
|
||||
/// <param name="b">float b</param>
|
||||
public v64(float a, float b)
|
||||
{
|
||||
this = default(v64);
|
||||
Float0 = a;
|
||||
Float1 = b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with a double
|
||||
/// </summary>
|
||||
/// <param name="a">Splatted double</param>
|
||||
public v64(double a)
|
||||
{
|
||||
this = default(v64);
|
||||
Double0 = a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with a long
|
||||
/// </summary>
|
||||
/// <param name="a">long a</param>
|
||||
public v64(long a)
|
||||
{
|
||||
this = default(v64);
|
||||
SLong0 = a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64 with a ulong
|
||||
/// </summary>
|
||||
/// <param name="a">ulong a</param>
|
||||
public v64(ulong a)
|
||||
{
|
||||
this = default(v64);
|
||||
ULong0 = a;
|
||||
}
|
||||
}
|
||||
|
||||
#if BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
/// <summary>
|
||||
/// Represents a 128-bit SIMD value (Arm only)
|
||||
/// (a combination of 2 64-bit values, equivalent to Arm Neon *x2 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v64x2
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v64 v64_0;
|
||||
/// <summary>
|
||||
/// Get the second 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public v64 v64_1;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64x2 with 2 v64's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v64.</param>
|
||||
/// <param name="v1">Second v64.</param>
|
||||
public v64x2(v64 v0, v64 v1)
|
||||
{
|
||||
this = default(v64x2);
|
||||
v64_0 = v0;
|
||||
v64_1 = v1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 192-bit SIMD value (Arm only)
|
||||
/// (a combination of 3 64-bit values, equivalent to Arm Neon *x3 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v64x3
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v64 v64_0;
|
||||
/// <summary>
|
||||
/// Get the second 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public v64 v64_1;
|
||||
/// <summary>
|
||||
/// Get the third 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public v64 v64_2;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64x3 with 3 v64's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v64.</param>
|
||||
/// <param name="v1">Second v64.</param>
|
||||
/// <param name="v2">Third v64.</param>
|
||||
public v64x3(v64 v0, v64 v1, v64 v2)
|
||||
{
|
||||
this = default(v64x3);
|
||||
v64_0 = v0;
|
||||
v64_1 = v1;
|
||||
v64_2 = v2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a 256-bit SIMD value (Arm only)
|
||||
/// (a combination of 4 64-bit values, equivalent to Arm Neon *x4 types)
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct v64x4
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the first 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public v64 v64_0;
|
||||
/// <summary>
|
||||
/// Get the second 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public v64 v64_1;
|
||||
/// <summary>
|
||||
/// Get the third 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public v64 v64_2;
|
||||
/// <summary>
|
||||
/// Get the fourth 64 bits of the vector
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public v64 v64_3;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the v64x4 with 4 v64's
|
||||
/// </summary>
|
||||
/// <param name="v0">First v64.</param>
|
||||
/// <param name="v1">Second v64.</param>
|
||||
/// <param name="v2">Third v64.</param>
|
||||
/// <param name="v3">Fourth v64.</param>
|
||||
public v64x4(v64 v0, v64 v1, v64 v2, v64 v3)
|
||||
{
|
||||
this = default(v64x4);
|
||||
v64_0 = v0;
|
||||
v64_1 = v1;
|
||||
v64_2 = v2;
|
||||
v64_3 = v3;
|
||||
}
|
||||
}
|
||||
#endif // BURST_INTERNAL || UNITY_BURST_EXPERIMENTAL_NEON_INTRINSICS
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// Can be used to specify that a parameter to a function, a field of a struct, a struct, or a function return will not alias. (Advanced - see User Manual for a description of Aliasing).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Struct | AttributeTargets.ReturnValue)]
|
||||
public class NoAliasAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
#if BURST_COMPILER_SHARED
|
||||
public static class SafeStringArrayHelper
|
||||
#else
|
||||
internal static class SafeStringArrayHelper
|
||||
#endif
|
||||
{
|
||||
// Methods to help when needing to serialise arrays of strings safely
|
||||
public static string SerialiseStringArraySafe(string[] array)
|
||||
{
|
||||
var s = new StringBuilder();
|
||||
foreach (var entry in array)
|
||||
{
|
||||
s.Append($"{Encoding.UTF8.GetByteCount(entry)}]");
|
||||
s.Append(entry);
|
||||
}
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
public static string[] DeserialiseStringArraySafe(string input)
|
||||
{
|
||||
// Safer method of serialisation (len]path) e.g. "5]frank8]c:\\billy" ( new [] {"frank","c:\\billy"} )
|
||||
|
||||
// Since the len part of `len]path` is specified in bytes we'll be working on a byte array instead
|
||||
// of a string, because functions like Substring expects char offsets and number of chars.
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
var listFolders = new List<string>();
|
||||
var index = 0;
|
||||
var length = bytes.Length;
|
||||
while (index < length)
|
||||
{
|
||||
int len = 0;
|
||||
// Read the decimal encoded length, terminated by an ']'
|
||||
while (true)
|
||||
{
|
||||
if (index >= length)
|
||||
{
|
||||
throw new FormatException($"Invalid input `{input}`: reached end while reading length");
|
||||
}
|
||||
|
||||
var d = bytes[index];
|
||||
|
||||
if (d == ']')
|
||||
{
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (d < '0' || d > '9')
|
||||
{
|
||||
throw new FormatException(
|
||||
$"Invalid input `{input}` at {index}: Got non-digit character while reading length");
|
||||
}
|
||||
|
||||
len = len * 10 + (d - '0');
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
listFolders.Add(Encoding.UTF8.GetString(bytes, index, len));
|
||||
index += len;
|
||||
}
|
||||
|
||||
return listFolders.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
#if BURST_UNITY_MOCK
|
||||
using System.Runtime.CompilerServices;
|
||||
#endif
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Unity.Burst
|
||||
{
|
||||
/// <summary>
|
||||
/// A structure that allows to share mutable static data between C# and HPC#.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the data to share (must not contain any reference types)</typeparam>
|
||||
public readonly unsafe struct SharedStatic<T> where T : struct
|
||||
{
|
||||
private readonly void* _buffer;
|
||||
|
||||
private SharedStatic(void* buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
CheckIf_T_IsUnmanagedOrThrow(); // We will remove this once we have full support for unmanaged constraints with C# 8.0
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a writable reference to the shared data.
|
||||
/// </summary>
|
||||
public ref T Data
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_DOTSPLAYER
|
||||
return ref UnsafeUtility.AsRef<T>(_buffer);
|
||||
#else
|
||||
return ref Unsafe.AsRef<T>(_buffer);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a direct unsafe pointer to the shared data.
|
||||
/// </summary>
|
||||
public void* UnsafeDataPointer
|
||||
{
|
||||
get { return _buffer; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data for the specified context (usable from both C# and HPC#)
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">A type class that uniquely identifies the this shared data.</typeparam>
|
||||
/// <param name="alignment">Optional alignment</param>
|
||||
/// <returns>A shared static for the specified context</returns>
|
||||
public static SharedStatic<T> GetOrCreate<TContext>(uint alignment = 0)
|
||||
{
|
||||
return GetOrCreateUnsafe(
|
||||
alignment,
|
||||
BurstRuntime.GetHashCode64<TContext>(),
|
||||
0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data for the specified context and sub-context (usable from both C# and HPC#)
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">A type class that uniquely identifies the this shared data.</typeparam>
|
||||
/// <typeparam name="TSubContext">A type class that uniquely identifies this shared data within a sub-context of the primary context</typeparam>
|
||||
/// <param name="alignment">Optional alignment</param>
|
||||
/// <returns>A shared static for the specified context</returns>
|
||||
public static SharedStatic<T> GetOrCreate<TContext, TSubContext>(uint alignment = 0)
|
||||
{
|
||||
return GetOrCreateUnsafe(
|
||||
alignment,
|
||||
BurstRuntime.GetHashCode64<TContext>(),
|
||||
BurstRuntime.GetHashCode64<TSubContext>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default alignment is a user specified one is not provided.
|
||||
/// </summary>
|
||||
private const uint DefaultAlignment = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data unsafely for the specified context and sub-context (usable from both C# and HPC#).
|
||||
/// </summary>
|
||||
/// <param name="alignment">The alignment (specified in bytes).</param>
|
||||
/// <param name="hashCode">The 64-bit hashcode for the shared-static.</param>
|
||||
/// <param name="subHashCode">The 64-bit sub-hashcode for the shared-static.</param>
|
||||
/// <returns>A newly created or previously cached shared-static for the hashcodes provided.</returns>
|
||||
public static SharedStatic<T> GetOrCreateUnsafe(uint alignment, long hashCode, long subHashCode)
|
||||
{
|
||||
return new SharedStatic<T>(SharedStatic.GetOrCreateSharedStaticInternal(
|
||||
hashCode,
|
||||
subHashCode,
|
||||
(uint)UnsafeUtility.SizeOf<T>(),
|
||||
alignment == 0 ? DefaultAlignment : alignment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data unsafely for the specified context and sub-context (usable from both C# and HPC#).
|
||||
/// </summary>
|
||||
/// <typeparam name="TSubContext">A type class that uniquely identifies this shared data within a sub-context of the primary context</typeparam>
|
||||
/// <param name="alignment">The alignment (specified in bytes).</param>
|
||||
/// <param name="hashCode">The 64-bit hashcode for the shared-static.</param>
|
||||
/// <returns>A newly created or previously cached shared-static for the hashcodes provided.</returns>
|
||||
public static SharedStatic<T> GetOrCreatePartiallyUnsafeWithHashCode<TSubContext>(uint alignment, long hashCode)
|
||||
{
|
||||
return new SharedStatic<T>(SharedStatic.GetOrCreateSharedStaticInternal(
|
||||
hashCode,
|
||||
BurstRuntime.GetHashCode64<TSubContext>(),
|
||||
(uint)UnsafeUtility.SizeOf<T>(),
|
||||
alignment == 0 ? DefaultAlignment : alignment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data unsafely for the specified context and sub-context (usable from both C# and HPC#).
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">A type class that uniquely identifies the this shared data.</typeparam>
|
||||
/// <param name="alignment">The alignment (specified in bytes).</param>
|
||||
/// <param name="subHashCode">The 64-bit sub-hashcode for the shared-static.</param>
|
||||
/// <returns>A newly created or previously cached shared-static for the hashcodes provided.</returns>
|
||||
public static SharedStatic<T> GetOrCreatePartiallyUnsafeWithSubHashCode<TContext>(uint alignment, long subHashCode)
|
||||
{
|
||||
return new SharedStatic<T>(SharedStatic.GetOrCreateSharedStaticInternal(
|
||||
BurstRuntime.GetHashCode64<TContext>(),
|
||||
subHashCode,
|
||||
(uint)UnsafeUtility.SizeOf<T>(),
|
||||
alignment == 0 ? DefaultAlignment : alignment));
|
||||
}
|
||||
|
||||
#if !NET_DOTS
|
||||
/// <summary>
|
||||
/// Creates a shared static data for the specified context (reflection based, only usable from C#, but not from HPC#)
|
||||
/// </summary>
|
||||
/// <param name="contextType">A type class that uniquely identifies the this shared data</param>
|
||||
/// <param name="alignment">Optional alignment</param>
|
||||
/// <returns>A shared static for the specified context</returns>
|
||||
public static SharedStatic<T> GetOrCreate(Type contextType, uint alignment = 0)
|
||||
{
|
||||
return GetOrCreateUnsafe(
|
||||
alignment,
|
||||
BurstRuntime.GetHashCode64(contextType),
|
||||
0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a shared static data for the specified context and sub-context (usable from both C# and HPC#)
|
||||
/// </summary>
|
||||
/// <param name="contextType">A type class that uniquely identifies the this shared data</param>
|
||||
/// <param name="subContextType">A type class that uniquely identifies this shared data within a sub-context of the primary context</param>
|
||||
/// <param name="alignment">Optional alignment</param>
|
||||
/// <returns>A shared static for the specified context</returns>
|
||||
public static SharedStatic<T> GetOrCreate(Type contextType, Type subContextType, uint alignment = 0)
|
||||
{
|
||||
return GetOrCreateUnsafe(
|
||||
alignment,
|
||||
BurstRuntime.GetHashCode64(contextType),
|
||||
BurstRuntime.GetHashCode64(subContextType));
|
||||
}
|
||||
#endif
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private static void CheckIf_T_IsUnmanagedOrThrow()
|
||||
{
|
||||
if (!UnsafeUtility.IsUnmanaged<T>())
|
||||
throw new InvalidOperationException($"The type {typeof(T)} used in SharedStatic<{typeof(T)}> must be unmanaged (contain no managed types).");
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SharedStatic
|
||||
{
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private static void CheckSizeOf(uint sizeOf)
|
||||
{
|
||||
if (sizeOf == 0) throw new ArgumentException("sizeOf must be > 0", nameof(sizeOf));
|
||||
}
|
||||
|
||||
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||||
private static unsafe void CheckResult(void* result)
|
||||
{
|
||||
if (result == null)
|
||||
throw new InvalidOperationException("Unable to create a SharedStatic for this key. This is most likely due to the size of the struct inside of the SharedStatic having changed or the same key being reused for differently sized values. To fix this the editor needs to be restarted.");
|
||||
}
|
||||
|
||||
// Prevent GetOrCreateSharedMemory from being stripped, by preventing GetOrCreateSharedStaticInteranl fromm being stripped.
|
||||
internal class PreserveAttribute : System.Attribute {}
|
||||
|
||||
/// <summary>
|
||||
/// Get or create a shared-static.
|
||||
/// </summary>
|
||||
/// <param name="getHashCode64">The 64-bit hashcode for the shared-static.</param>
|
||||
/// <param name="getSubHashCode64">The 64-bit sub-hashcode for the shared-static.</param>
|
||||
/// <param name="sizeOf">The size (in bytes) of the shared static memory region.</param>
|
||||
/// <param name="alignment">The alignment (in bytes) of the shared static memory region.</param>
|
||||
/// <returns>Either a newly created or a previously created memory region that matches the hashcodes provided.
|
||||
[Preserve]
|
||||
public static unsafe void* GetOrCreateSharedStaticInternal(long getHashCode64, long getSubHashCode64, uint sizeOf, uint alignment)
|
||||
{
|
||||
CheckSizeOf(sizeOf);
|
||||
var hash128 = new UnityEngine.Hash128((ulong)getHashCode64, (ulong)getSubHashCode64);
|
||||
var result = Unity.Burst.LowLevel.BurstCompilerService.GetOrCreateSharedMemory(ref hash128, sizeOf, alignment);
|
||||
CheckResult(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "Unity.Burst",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"Unity.Burst.Unsafe.dll"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -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)}.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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() { }
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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> { }
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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#)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue