UnityGame/Library/PackageCache/com.unity.burst/Runtime/Intrinsics/Common.cs

329 lines
14 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
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)]
[BurstRuntime.Preserve]
// 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;
}
}