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; } }