// As BurstCompiler.Compile is not supported on Tiny/ZeroPlayer, we can ifdef the entire file
#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 Unity.Jobs.LowLevel.Unsafe;
#endif
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// NOTE: This file is shared via a csproj cs link in Burst.Compiler.IL
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
namespace Unity.Burst
{
///
/// Options available at Editor time and partially at runtime to control the behavior of the compilation and to enable/disable burst jobs.
///
#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";
#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
internal const string OptionDoNotEagerCompile = "do-not-eager-compile";
internal const string DoNotEagerCompile = "--" + OptionDoNotEagerCompile;
// -------------------------------------------------------
// Common options used by the compiler
// -------------------------------------------------------
internal const string OptionGroup = "group";
internal const string OptionPlatform = "platform=";
internal const string OptionBackend = "backend=";
internal const string OptionSafetyChecks = "safety-checks";
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 OptionIROpt = "ir-opt";
internal const string OptionCpuOpt = "cpu-opt=";
internal const string OptionFloatPrecision = "float-precision=";
internal const string OptionFloatMode = "float-mode=";
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=";
// -------------------------------------------------------
// 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=";
// TODO: Remove this option and use proper dump flags or revisit how we log timings
internal const string OptionJitLogTimings = "log-timings";
internal const string OptionJitIsForFunctionPointer = "is-for-function-pointer";
internal const string OptionJitManagedFunctionPointer = "managed-function-pointer=";
internal const string OptionJitProvider = "jit-provider=";
internal const string OptionJitSkipCheckDiskCache = "skip-check-disk-cache";
internal const string OptionJitSkipBurstInitialize = "skip-burst-initialize";
// -------------------------------------------------------
// 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 OptionAotUsePlatformSDKLinkers = "use-platform-sdk-linkers";
internal const string OptionAotOnlyStaticMethods = "only-static-methods";
internal const string OptionMethodPrefix = "method-prefix=";
internal const string OptionAotNoNativeToolchain = "no-native-toolchain";
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 CompilerCommandShutdown = "$shutdown";
internal const string CompilerCommandCancel = "$cancel";
internal const string CompilerCommandEnableCompiler = "$enable_compiler";
internal const string CompilerCommandDisableCompiler = "$disable_compiler";
internal const string CompilerCommandTriggerRecompilation = "$trigger_recompilation";
internal const string CompilerCommandEagerCompileMethods = "$eager_compile_methods";
internal const string CompilerCommandWaitUntilCompilationFinished = "$wait_until_compilation_finished";
internal const string CompilerCommandClearEagerCompilationQueues = "$clear_eager_compilation_queues";
internal const string CompilerCommandCancelEagerCompilation = "$cancel_eager_compilation";
internal const string CompilerCommandReset = "$reset";
internal const string CompilerCommandDomainReload = "$domain_reload";
internal const string CompilerCommandUpdateAssemblyFolders = "$update_assembly_folders";
internal const string CompilerCommandVersionNotification = "$version";
internal const string CompilerCommandSetProgressCallback = "$set_progress_callback";
internal const string CompilerCommandRequestClearJitCache = "$request_clear_jit_cache";
// 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;
#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
}
}
///
/// true if this option is the global options that affects menus
///
private bool IsGlobal { get; }
///
/// Gets a boolean indicating whether burst is enabled.
///
public bool IsEnabled
{
get => EnableBurstCompilation && !ForceDisableBurstCompilation;
}
///
/// Gets or sets a boolean to enable or disable compilation of burst jobs.
///
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;
if (changed && value)
{
MaybePreventChangingOption();
}
_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();
}
}
}
///
/// Gets or sets a boolean to force the compilation of all burst jobs synchronously.
///
///
/// This is only available at Editor time. Does not have an impact on player mode.
///
public bool EnableBurstCompileSynchronously
{
get => _enableBurstCompileSynchronously;
set
{
bool changed = _enableBurstCompileSynchronously != value;
_enableBurstCompileSynchronously = value;
if (changed) OnOptionsChanged();
}
}
///
/// Gets or sets a boolean to enable or disable safety checks.
///
///
/// This is only available at Editor time. Does not have an impact on player mode.
///
public bool EnableBurstSafetyChecks
{
get => _enableBurstSafetyChecks;
set
{
bool changed = _enableBurstSafetyChecks != value;
if (changed)
{
MaybePreventChangingOption();
}
_enableBurstSafetyChecks = value;
if (changed)
{
OnOptionsChanged();
MaybeTriggerRecompilation();
}
}
}
///
/// Gets or sets a boolean to force enable safety checks, irrespective of what
/// EnableBurstSafetyChecks is set to, or whether the job or function
/// has DisableSafetyChecks set.
///
///
/// This is only available at Editor time. Does not have an impact on player mode.
///
public bool ForceEnableBurstSafetyChecks
{
get => _forceEnableBurstSafetyChecks;
set
{
bool changed = _forceEnableBurstSafetyChecks != value;
if (changed)
{
MaybePreventChangingOption();
}
_forceEnableBurstSafetyChecks = value;
if (changed)
{
OnOptionsChanged();
MaybeTriggerRecompilation();
}
}
}
///
/// Enable debugging mode
///
public bool EnableBurstDebug
{
get => _enableBurstDebug;
set
{
bool changed = _enableBurstDebug != value;
if (changed)
{
MaybePreventChangingOption();
}
_enableBurstDebug = value;
if (changed)
{
OnOptionsChanged();
MaybeTriggerRecompilation();
}
}
}
///
/// This property is no longer used and will be removed in a future major release.
///
[Obsolete("This property is no longer used and will be removed in a future major release")]
public bool DisableOptimizations
{
get => false;
set
{
}
}
///
/// 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
///
[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, bool isForEagerCompilation = false)
{
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;
}
// If we're compiling for eager compilation, and this method has requested not to be eager-compiled... don't compile it.
if (isForEagerCompilation && (attribute.Options?.Contains(DoNotEagerCompile) ?? false))
{
return false;
}
return true;
}
private static BurstCompileAttribute GetBurstCompileAttribute(MemberInfo memberInfo)
{
var result = memberInfo.GetCustomAttribute();
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();
// Don't eager-compile tests that we expect to fail compilation.
var expectCompilerExceptionProperty = attributeType.GetProperty("ExpectCompilerException");
var expectCompilerException = (expectCompilerExceptionProperty != null)
? (bool)expectCompilerExceptionProperty.GetValue(a)
: false;
if (expectCompilerException)
{
options.Add(DoNotEagerCompile);
}
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);
}
///
/// Gets the options for the specified member. Returns false if the `[BurstCompile]` attribute was not found
///
/// false if the `[BurstCompile]` attribute was not found; otherwise true
internal bool TryGetOptions(MemberInfo member, bool isJit, out string flagsOut, bool isForEagerCompilation = false)
{
flagsOut = null;
BurstCompileAttribute attr;
if (!TryGetAttribute(member, out attr, isForEagerCompilation))
{
return false;
}
flagsOut = GetOptions(isJit, attr, isForEagerCompilation);
return true;
}
internal string GetOptions(bool isJit, BurstCompileAttribute attr = null, bool isForEagerCompilation = false)
{
// Add debug to Jit options instead of passing it here
// attr.Debug
var flagsBuilderOut = new StringBuilder();
if (isJit && !isForEagerCompilation && ((attr?.CompileSynchronously ?? false) || RequiresSynchronousCompilation))
{
AddOption(flagsBuilderOut, GetOption(OptionJitEnableSynchronousCompilation));
}
var shouldEnableSafetyChecks = EnableBurstSafetyChecks;
if (isJit && isForEagerCompilation)
{
// Eager compilation must always be asynchronous.
// - For synchronous jobs, we set the compilation priority to HighPriority.
// This has two effects:
// - These synchronous jobs will be compiled before asynchronous jobs.
// - We will block on these compilations when entering PlayMode.
// - For asynchronous jobs, we set the compilation priority to LowPriority.
// These jobs will be compiled after "normal" compilation requests
// for asynchronous jobs.
// Note that we ignore the global "compile synchronously" option here because:
// - If it's set when entering play mode, then we'll wait for all
// methods to be compiled anyway.
// - If it's not set when entering play mode, then we only want to wait
// for methods that explicitly have CompileSynchronously=true on their attributes.
var priority = (attr?.CompileSynchronously ?? false)
? CompilationPriority.HighPriority
: CompilationPriority.LowPriority;
AddOption(flagsBuilderOut, GetOption(OptionJitCompilationPriority, priority));
// Don't call `burst.initialize` when we're eager-compiling.
AddOption(flagsBuilderOut, GetOption(OptionJitSkipBurstInitialize));
}
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));
}
// [BurstCompile(DisableSafetyChecks = true)] takes precedence over EnableBurstSafetyChecks
if (attr.DisableSafetyChecks)
{
shouldEnableSafetyChecks = false;
}
if (attr.Options != null)
{
foreach (var option in attr.Options)
{
if (!String.IsNullOrEmpty(option))
{
AddOption(flagsBuilderOut, option);
}
}
}
}
// ForceEnableBurstSafetyChecks takes precedence over any other setting.
if (ForceEnableBurstSafetyChecks)
{
shouldEnableSafetyChecks = true;
}
// Fetch options from attribute
if (shouldEnableSafetyChecks)
{
AddOption(flagsBuilderOut, GetOption(OptionSafetyChecks));
}
else
{
AddOption(flagsBuilderOut, GetOption(OptionDisableSafetyChecks));
}
if (isJit && EnableBurstTimings)
{
AddOption(flagsBuilderOut, GetOption(OptionJitLogTimings));
}
if (EnableBurstDebug || (attr?.Debug ?? false))
{
AddOption(flagsBuilderOut, GetOption(OptionDebugMode));
}
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 && UNITY_2019_3_OR_NEWER
if (IsGlobal && IsEnabled && !IsInitializing)
{
UnityEditor.EditorUtility.DisplayProgressBar("Burst", "Waiting for compilation to finish", -1);
try
{
BurstCompiler.TriggerRecompilation();
}
finally
{
UnityEditor.EditorUtility.ClearProgressBar();
}
}
#endif
}
///
/// This method should be called before changing any option that requires
/// an Editor restart in versions older than 2019.3.
///
/// This is because Editors older than 2019.3 don't support recompilation
/// of already-compiled jobs.
///
private void MaybePreventChangingOption()
{
#if UNITY_EDITOR && !UNITY_2019_3_OR_NEWER
if (IsGlobal && !IsInitializing)
{
if (RequiresRestartUtility.CalledFromUI)
{
RequiresRestartUtility.RequiresRestart = true;
}
else
{
throw new InvalidOperationException("This option cannot be set programmatically in 2019.2 and older versions of the Editor");
}
}
#endif
}
#if !UNITY_DOTSPLAYER && !NET_DOTS
///
/// Static initializer based on command line arguments
///
static BurstCompilerOptions()
{
foreach (var arg in Environment.GetCommandLineArgs())
{
switch (arg)
{
case DisableCompilationArg:
ForceDisableBurstCompilation = true;
break;
case ForceSynchronousCompilationArg:
ForceBurstCompilationSynchronously = false;
break;
}
}
}
#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 = 6,
WASM = 7,
UWP = 8,
Lumin = 9,
Switch = 10,
Stadia = 11,
tvOS = 12,
GameCoreXboxOne = 13,
GameCoreXboxSeries = 14,
PS5 = 15,
}
// NOTE: This must be synchronized with Backend.TargetCpu
internal enum TargetCpu
{
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,
}
#endif
///
/// Flags used by to dump intermediate compiler results.
///
[Flags]
#if BURST_COMPILER_SHARED
public enum NativeDumpFlags
#else
internal enum NativeDumpFlags
#endif
{
///
/// Nothing is selected.
///
None = 0,
///
/// Dumps the IL of the method being compiled
///
IL = 1 << 0,
///
/// Dumps the reformated backend API Calls
///
Backend = 1 << 1,
///
/// Dumps the generated module without optimizations
///
IR = 1 << 2,
///
/// Dumps the generated backend code after optimizations (if enabled)
///
IROptimized = 1 << 3,
///
/// Dumps the generated ASM code
///
Asm = 1 << 4,
///
/// Generate the native code
///
Function = 1 << 5,
///
/// Dumps the result of analysis
///
Analysis = 1 << 6,
///
/// Dumps the diagnostics from optimisation
///
IRPassAnalysis = 1 << 7,
///
/// Dumps the IL before all transformation of the method being compiled
///
ILPre = 1 << 8,
///
/// Dumps all normal output.
///
All = IL | ILPre | IR | IROptimized | Asm | Function | Analysis | IRPassAnalysis
}
#if BURST_COMPILER_SHARED
public enum CompilationPriority
#else
internal enum CompilationPriority
#endif
{
HighPriority = 0,
StandardPriority = 1,
LowPriority = 2,
}
#if UNITY_EDITOR
///
/// 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.
///
internal static class RequiresRestartUtility
{
[ThreadStatic]
public static bool CalledFromUI;
[ThreadStatic]
public static bool RequiresRestart;
}
#endif
internal readonly struct EagerCompilationRequest
{
public EagerCompilationRequest(string encodedMethod, string options)
{
EncodedMethod = encodedMethod;
Options = options;
}
public readonly string EncodedMethod;
public readonly string Options;
}
}
#endif