From e66d834512c4176682cec496165f5101be9e0727 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Wed, 29 Jan 2025 03:20:38 -0800 Subject: [PATCH 01/31] Implement 'ConditionalWeakTable.GetOrAdd' APIs (#111204) * Add '[EditorBrowsable(Never)]' to APIs * Add 'GetOrAdd' API * Add 'GetOrAdd' API * Add 'GetOrAdd' API * Update ref assembly * Add unit tests * Add XML docs for new APIs * Remove 'Atomically' to clarify docs * Convert uses to new APIs * Remove leftover unused method * Switch 'GetOrCreateComInterfaceForObject' to local type * Apply suggestions from code review * Lower threshold time for new tests --------- Co-authored-by: Jan Kotas --- .../DispenserThatReusesAsLongAsKeyIsAlive.cs | 9 +- .../InteropServices/ComWrappers.NativeAot.cs | 36 ++- .../ObjectiveCMarshal.NativeAot.cs | 2 +- .../Runtime/InteropServices/PInvokeMarshal.cs | 4 +- .../src/System/Threading/Monitor.NativeAot.cs | 4 +- .../Net/Sockets/IOControlKeepAlive.Windows.cs | 2 +- .../CompilerServices/ConditionalWeakTable.cs | 121 ++++++++-- .../Reflection/DispatchProxyGenerator.cs | 2 +- .../System.Runtime/ref/System.Runtime.cs | 6 + .../ConditionalWeakTableTests.cs | 207 ++++++++++++++++++ 10 files changed, 344 insertions(+), 49 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs index d0879b3cd0d9c2..18ba9d7880978d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Dispensers/DispenserThatReusesAsLongAsKeyIsAlive.cs @@ -14,23 +14,16 @@ internal sealed class DispenserThatReusesAsLongAsKeyIsAlive factory) { - _createValueCallback = CreateValue; _conditionalWeakTable = new ConditionalWeakTable(); _factory = factory; } public sealed override V GetOrAdd(K key) { - return _conditionalWeakTable.GetValue(key, _createValueCallback); - } - - private V CreateValue(K key) - { - return _factory(key); + return _conditionalWeakTable.GetOrAdd(key, _factory); } private readonly Func _factory; private readonly ConditionalWeakTable _conditionalWeakTable; - private readonly ConditionalWeakTable.CreateValueCallback _createValueCallback; } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs index 87eb31d58022fe..38105ec0984fdb 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs @@ -701,6 +701,13 @@ public void DisconnectTracker() } } + // Custom type instead of a value tuple to avoid rooting 'ITuple' and other value tuple stuff + private struct GetOrCreateComInterfaceForObjectParameters + { + public ComWrappers? This; + public CreateComInterfaceFlags Flags; + } + /// /// Create a COM representation of the supplied object that can be passed to a non-managed environment. /// @@ -716,18 +723,12 @@ public unsafe IntPtr GetOrCreateComInterfaceForObject(object instance, CreateCom { ArgumentNullException.ThrowIfNull(instance); - ManagedObjectWrapperHolder? managedObjectWrapper; - if (_managedObjectWrapperTable.TryGetValue(instance, out managedObjectWrapper)) + ManagedObjectWrapperHolder managedObjectWrapper = _managedObjectWrapperTable.GetOrAdd(instance, static (c, items) => { - managedObjectWrapper.AddRef(); - return managedObjectWrapper.ComIp; - } - - managedObjectWrapper = _managedObjectWrapperTable.GetValue(instance, (c) => - { - ManagedObjectWrapper* value = CreateManagedObjectWrapper(c, flags); + ManagedObjectWrapper* value = items.This!.CreateManagedObjectWrapper(c, items.Flags); return new ManagedObjectWrapperHolder(value, c); - }); + }, new GetOrCreateComInterfaceForObjectParameters { This = this, Flags = flags }); + managedObjectWrapper.AddRef(); return managedObjectWrapper.ComIp; } @@ -1069,15 +1070,11 @@ private void RegisterWrapperForObject(NativeObjectWrapper wrapper, object comPro Debug.Assert(wrapper.ProxyHandle.Target == comProxy); Debug.Assert(wrapper.IsUniqueInstance || _rcwCache.FindProxyForComInstance(wrapper.ExternalComObject) == comProxy); - if (s_nativeObjectWrapperTable.TryGetValue(comProxy, out NativeObjectWrapper? registeredWrapper) - && registeredWrapper != wrapper) - { - Debug.Assert(registeredWrapper.ExternalComObject != wrapper.ExternalComObject); - wrapper.Release(); - throw new NotSupportedException(); - } + // Add the input wrapper bound to the COM proxy, if there isn't one already. If another thread raced + // against this one and this lost, we'd get the wrapper added from that thread instead. + NativeObjectWrapper registeredWrapper = s_nativeObjectWrapperTable.GetOrAdd(comProxy, wrapper); - registeredWrapper = GetValueFromRcwTable(comProxy, wrapper); + // We lost the race, so we cannot register the incoming wrapper with the target object if (registeredWrapper != wrapper) { Debug.Assert(registeredWrapper.ExternalComObject != wrapper.ExternalComObject); @@ -1091,9 +1088,6 @@ private void RegisterWrapperForObject(NativeObjectWrapper wrapper, object comPro // TrackerObjectManager and we could end up missing a section of the object graph. // This cache deduplicates, so it is okay that the wrapper will be registered multiple times. AddWrapperToReferenceTrackerHandleCache(registeredWrapper); - - // Separate out into a local function to avoid the closure and delegate allocation unless we need it. - static NativeObjectWrapper GetValueFromRcwTable(object userObject, NativeObjectWrapper newWrapper) => s_nativeObjectWrapperTable.GetValue(userObject, _ => newWrapper); } private static void AddWrapperToReferenceTrackerHandleCache(NativeObjectWrapper wrapper) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs index ccb969f1728011..621c62d87db7d9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.NativeAot.cs @@ -136,7 +136,7 @@ private static IntPtr CreateReferenceTrackingHandleInternal( throw new InvalidOperationException(SR.InvalidOperation_ObjectiveCTypeNoFinalizer); } - var trackerInfo = s_objects.GetValue(obj, static o => new ObjcTrackingInformation()); + var trackerInfo = s_objects.GetOrAdd(obj, static o => new ObjcTrackingInformation()); trackerInfo.EnsureInitialized(obj); trackerInfo.GetTaggedMemory(out memInSizeT, out mem); return RuntimeImports.RhHandleAllocRefCounted(obj); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index 81c9482a557580..5ae29387480839 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -70,7 +70,7 @@ public static unsafe IntPtr GetFunctionPointerForDelegate(Delegate del) // // Marshalling a managed delegate created from managed code into a native function pointer // - return GetPInvokeDelegates().GetValue(del, s_AllocateThunk ??= AllocateThunk).Thunk; + return GetPInvokeDelegates().GetOrAdd(del, s_AllocateThunk ??= AllocateThunk).Thunk; } } @@ -78,7 +78,7 @@ public static unsafe IntPtr GetFunctionPointerForDelegate(Delegate del) /// Used to lookup whether a delegate already has thunk allocated for it /// private static ConditionalWeakTable s_pInvokeDelegates; - private static ConditionalWeakTable.CreateValueCallback s_AllocateThunk; + private static Func s_AllocateThunk; private static ConditionalWeakTable GetPInvokeDelegates() { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs index bed275c1ea995d..4c90bbaa42ad3e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Monitor.NativeAot.cs @@ -25,14 +25,14 @@ public static partial class Monitor #region Object->Lock/Condition mapping private static readonly ConditionalWeakTable s_conditionTable = new ConditionalWeakTable(); - private static readonly ConditionalWeakTable.CreateValueCallback s_createCondition = (o) => new Condition(ObjectHeader.GetLockObject(o)); + private static readonly Func s_createCondition = (o) => new Condition(ObjectHeader.GetLockObject(o)); private static Condition GetCondition(object obj) { Debug.Assert( !(obj is Condition), "Do not use Monitor.Pulse or Wait on a Condition instance; use the methods on Condition instead."); - return s_conditionTable.GetValue(obj, s_createCondition); + return s_conditionTable.GetOrAdd(obj, s_createCondition); } #endregion diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/IOControlKeepAlive.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/IOControlKeepAlive.Windows.cs index 76fd1ca3fe15da..5fff0bf763481a 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/IOControlKeepAlive.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/IOControlKeepAlive.Windows.cs @@ -61,7 +61,7 @@ public static SocketError Set(SafeSocketHandle handle, SocketOptionName optionNa public static SocketError Set(SafeSocketHandle handle, SocketOptionName optionName, int optionValueSeconds) { - IOControlKeepAlive ioControlKeepAlive = s_socketKeepAliveTable.GetValue(handle, (SafeSocketHandle handle) => new IOControlKeepAlive()); + IOControlKeepAlive ioControlKeepAlive = s_socketKeepAliveTable.GetOrAdd(handle, (SafeSocketHandle handle) => new IOControlKeepAlive()); if (optionName == SocketOptionName.TcpKeepAliveTime) { ioControlKeepAlive._timeMs = SecondsToMilliseconds(optionValueSeconds); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs index 1b5c0894966d3f..2318211cb7e9f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Numerics; @@ -188,36 +189,125 @@ public void Clear() } /// - /// Atomically searches for a specified key in the table and returns the corresponding value. - /// If the key does not exist in the table, the method invokes a callback method to create a - /// value that is bound to the specified key. + /// Searches for a specified key in the table and returns the corresponding value. If the key does + /// not exist in the table, the method adds the given value and binds it to the specified key. + /// + /// The key of the value to find. It cannot be . + /// The value to add and bind to , if one does not exist already. + /// The value bound to in the current instance, after the method completes. + /// is . + public TValue GetOrAdd(TKey key, TValue value) + { + // key is validated by TryGetValue + if (TryGetValue(key, out TValue? existingValue)) + { + return existingValue; + } + + return GetOrAddLocked(key, value); + } + + /// + /// Searches for a specified key in the table and returns the corresponding value. If the key does not exist + /// in the table, the method invokes the supplied factory to create a value that is bound to the specified key. + /// + /// The key of the value to find. It cannot be . + /// The callback that creates a value for key, if one does not exist already. It cannot be . + /// The value bound to in the current instance, after the method completes. + /// or are . + /// + /// If multiple threads try to initialize the same key, the table may invoke multiple times + /// with the same key. Exactly one of these calls will succeed and the returned value of that call will be the one added to + /// the table and returned by all the racing calls. This rule permits the + /// table to invoke outside the internal table lock, to prevent deadlocks. + /// + public TValue GetOrAdd(TKey key, Func valueFactory) + { + ArgumentNullException.ThrowIfNull(valueFactory); + + // key is validated by TryGetValue + if (TryGetValue(key, out TValue? existingValue)) + { + return existingValue; + } + + // create the value outside of the lock + TValue value = valueFactory(key); + + return GetOrAddLocked(key, value); + } + + /// + /// Searches for a specified key in the table and returns the corresponding value. If the key does not exist + /// in the table, the method invokes the supplied factory to create a value that is bound to the specified key. + /// + /// The type of the additional argument to use with the value factory. + /// The key of the value to find. It cannot be . + /// The callback that creates a value for key, if one does not exist already. It cannot be . + /// The additional argument to supply to upon invocation. + /// The value bound to in the current instance, after the method completes. + /// or are . + /// + /// If multiple threads try to initialize the same key, the table may invoke multiple times with the + /// same key. Exactly one of these calls will succeed and the returned value of that call will be the one added to the table and + /// returned by all the racing calls. This rule permits the + /// table to invoke outside the internal table lock, to prevent deadlocks. + /// + public TValue GetOrAdd(TKey key, Func valueFactory, TArg factoryArgument) + where TArg : allows ref struct + { + ArgumentNullException.ThrowIfNull(valueFactory); + + // key is validated by TryGetValue + if (TryGetValue(key, out TValue? existingValue)) + { + return existingValue; + } + + // create the value outside of the lock + TValue value = valueFactory(key, factoryArgument); + + return GetOrAddLocked(key, value); + } + + /// + /// Searches for a specified key in the table and returns the corresponding value. If the key does not exist + /// in the table, the method invokes a callback method to create a value that is bound to the specified key. /// /// key of the value to find. Cannot be null. /// callback that creates value for key. Cannot be null. /// /// + /// /// If multiple threads try to initialize the same key, the table may invoke createValueCallback /// multiple times with the same key. Exactly one of these calls will succeed and the returned /// value of that call will be the one added to the table and returned by all the racing GetValue() calls. /// This rule permits the table to invoke createValueCallback outside the internal table lock /// to prevent deadlocks. + /// + /// + /// Consider using (or one of its overloads) instead. + /// /// + [EditorBrowsable(EditorBrowsableState.Never)] public TValue GetValue(TKey key, CreateValueCallback createValueCallback) { ArgumentNullException.ThrowIfNull(createValueCallback); // key is validated by TryGetValue - return TryGetValue(key, out TValue? existingValue) ? - existingValue : - GetValueLocked(key, createValueCallback); + if (TryGetValue(key, out TValue? existingValue)) + { + return existingValue; + } + + // create the value outside of the lock + TValue value = createValueCallback(key); + + return GetOrAddLocked(key, value); } - private TValue GetValueLocked(TKey key, CreateValueCallback createValueCallback) + private TValue GetOrAddLocked(TKey key, TValue value) { - // If we got here, the key was not in the table. Invoke the callback (outside the lock) - // to generate the new value for the key. - TValue newValue = createValueCallback(key); - lock (_lock) { // Now that we've taken the lock, must recheck in case we lost a race to add the key. @@ -228,8 +318,8 @@ private TValue GetValueLocked(TKey key, CreateValueCallback createValueCallback) else { // Verified in-lock that we won the race to add the key. Add it now. - CreateEntry(key, newValue); - return newValue; + CreateEntry(key, value); + return value; } } } @@ -239,8 +329,13 @@ private TValue GetValueLocked(TKey key, CreateValueCallback createValueCallback) /// to create new instances as needed. If TValue does not have a default constructor, this will throw. /// /// key of the value to find. Cannot be null. + /// + /// Consider using (or one of its overloads) instead. + /// + [EditorBrowsable(EditorBrowsableState.Never)] public TValue GetOrCreateValue(TKey key) => GetValue(key, _ => Activator.CreateInstance()); + [EditorBrowsable(EditorBrowsableState.Never)] public delegate TValue CreateValueCallback(TKey key); /// Gets an enumerator for the table. diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs index 6153f7e0cfdbde..5181722c578c4e 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs +++ b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs @@ -71,7 +71,7 @@ internal static object CreateProxyInstance( AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(baseType.Assembly); Debug.Assert(alc != null); - ProxyAssembly proxyAssembly = s_alcProxyAssemblyMap.GetValue(alc, static x => new ProxyAssembly(x)); + ProxyAssembly proxyAssembly = s_alcProxyAssemblyMap.GetOrAdd(alc, static x => new ProxyAssembly(x)); GeneratedTypeInfo proxiedType = proxyAssembly.GetProxyType(baseType, interfaceType, interfaceParameter, proxyParameter); return Activator.CreateInstance(proxiedType.GeneratedType, new object[] { proxiedType.MethodInfos })!; } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index f16290e603a976..5f8847cf2f4518 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -13276,13 +13276,19 @@ public ConditionalWeakTable() { } public void Add(TKey key, TValue value) { } public void AddOrUpdate(TKey key, TValue value) { } public void Clear() { } + public TValue GetOrAdd(TKey key, TValue value) { throw null; } + public TValue GetOrAdd(TKey key, System.Func valueFactory) { throw null; } + public TValue GetOrAdd(TKey key, System.Func valueFactory, TArg factoryArgument) where TArg : allows ref struct { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public TValue GetOrCreateValue(TKey key) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public TValue GetValue(TKey key, System.Runtime.CompilerServices.ConditionalWeakTable.CreateValueCallback createValueCallback) { throw null; } public bool Remove(TKey key) { throw null; } System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } public bool TryAdd(TKey key, TValue value) { throw null; } public bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public delegate TValue CreateValueCallback(TKey key); } public readonly partial struct ConfiguredAsyncDisposable diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/ConditionalWeakTableTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/ConditionalWeakTableTests.cs index c535ab341dd1b9..c4f70e4ab37687 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/ConditionalWeakTableTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/ConditionalWeakTableTests.cs @@ -22,6 +22,11 @@ public static void InvalidArgs_Throws() AssertExtensions.Throws("key", () => cwt.Add(null, new object())); // null key AssertExtensions.Throws("key", () => cwt.TryGetValue(null, out ignored)); // null key AssertExtensions.Throws("key", () => cwt.Remove(null)); // null key + AssertExtensions.Throws("key", () => cwt.GetOrAdd(null, new object())); // null key + AssertExtensions.Throws("key", () => cwt.GetOrAdd(null, k => new object())); // null key + AssertExtensions.Throws("key", () => cwt.GetOrAdd(null, (k, a) => new object(), 42)); // null key + AssertExtensions.Throws("valueFactory", () => cwt.GetOrAdd(new object(), null)); // null factory + AssertExtensions.Throws("valueFactory", () => cwt.GetOrAdd(new object(), null, 42)); // null factory AssertExtensions.Throws("createValueCallback", () => cwt.GetValue(new object(), null)); // null delegate object key = new object(); @@ -62,6 +67,9 @@ public static void Add(int numObjects, bool tryAdd) Assert.Same(values[i], value); Assert.Same(value, cwt.GetOrCreateValue(keys[i])); Assert.Same(value, cwt.GetValue(keys[i], _ => new object())); + Assert.Same(value, cwt.GetOrAdd(keys[i], new object())); + Assert.Same(value, cwt.GetOrAdd(keys[i], k => new object())); + Assert.Same(value, cwt.GetOrAdd(keys[i], (k, a) => new object(), 42)); } return Tuple.Create(cwt, keys.Select(k => new WeakReference(k)).ToArray(), values.Select(v => new WeakReference(v)).ToArray()); @@ -86,6 +94,9 @@ public static void TryAdd_ConditionallyAdds() object value1 = new object(); object value2 = new object(); + object value3 = new object(); + object value4 = new object(); + object value5 = new object(); object found; object key1 = new object(); @@ -102,8 +113,30 @@ public static void TryAdd_ConditionallyAdds() Assert.Same(value1, found); Assert.Equal(2, cwt.Count()); + object key3 = new object(); + Assert.Same(value1, cwt.GetOrAdd(key1, new object())); + Assert.Same(value3, cwt.GetOrAdd(key3, value3)); + Assert.Same(value3, cwt.GetOrAdd(key3, new object())); + Assert.True(cwt.Remove(key3)); + Assert.Same(value4, cwt.GetOrAdd(key3, value4)); + + object key4 = new object(); + Assert.Same(value4, cwt.GetOrAdd(key4, k => value4)); + Assert.Same(value4, cwt.GetOrAdd(key4, k => new object())); + Assert.True(cwt.Remove(key4)); + Assert.Same(value5, cwt.GetOrAdd(key4, k => value5)); + + object key5 = new object(); + Assert.Same(value5, cwt.GetOrAdd(key5, (k, a) => a, value5)); + Assert.Same(value5, cwt.GetOrAdd(key5, (k, a) => a, new object())); + Assert.True(cwt.Remove(key5)); + Assert.Same(value4, cwt.GetOrAdd(key5, (k, a) => a, value4)); + GC.KeepAlive(key1); GC.KeepAlive(key2); + GC.KeepAlive(key3); + GC.KeepAlive(key4); + GC.KeepAlive(key5); } [Theory] @@ -185,6 +218,66 @@ public static void Concurrent_Add_Read_Remove_DifferentObjects() }); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Add_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + + // Here we use a lower threshold to reduce test time (same below). + // This applies to all the new 'GetOrAdd' tests in this file. + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + cwt.Add(key, value); + Assert.Same(value, cwt.GetOrAdd(key, new object())); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_Add_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + cwt.Add(key, value); + Assert.Same(value, cwt.GetOrAdd(key, _ => new object())); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_WithArg_Add_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + cwt.Add(key, value); + Assert.Same(value, cwt.GetOrAdd(key, (k, a) => a, new object())); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public static void Concurrent_GetValue_Read_Remove_DifferentObjects() { @@ -203,6 +296,60 @@ public static void Concurrent_GetValue_Read_Remove_DifferentObjects() }); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + Assert.Same(value, cwt.GetOrAdd(key, value)); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + Assert.Same(value, cwt.GetOrAdd(key, _ => value)); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_WithArg_Read_Remove_DifferentObjects() + { + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + object key = new object(); + object value = new object(); + Assert.Same(value, cwt.GetOrAdd(key, (k, a) => a, value)); + Assert.True(cwt.Remove(key)); + Assert.False(cwt.Remove(key)); + } + }); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public static void Concurrent_GetValue_Read_Remove_SameObject() { @@ -221,6 +368,60 @@ public static void Concurrent_GetValue_Read_Remove_SameObject() }); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Read_Remove_SameObject() + { + object key = new object(); + object value = new object(); + + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + Assert.Same(value, cwt.GetOrAdd(key, value)); + cwt.Remove(key); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_Read_Remove_SameObject() + { + object key = new object(); + object value = new object(); + + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + Assert.Same(value, cwt.GetOrAdd(key, _ => value)); + cwt.Remove(key); + } + }); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public static void Concurrent_GetOrAdd_Factory_WithArg_Read_Remove_SameObject() + { + object key = new object(); + object value = new object(); + + var cwt = new ConditionalWeakTable(); + DateTime end = DateTime.UtcNow + TimeSpan.FromSeconds(0.10); + Parallel.For(0, Environment.ProcessorCount, i => + { + while (DateTime.UtcNow < end) + { + Assert.Same(value, cwt.GetOrAdd(key, (k, a) => a, value)); + cwt.Remove(key); + } + }); + } + [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] static WeakReference GetWeakCondTabRef(out ConditionalWeakTable cwt_out, out object key_out) { @@ -369,6 +570,9 @@ public static void AddOrUpdateDataTest() Assert.Equal("value1", value); Assert.Equal(value, cwt.GetOrCreateValue(key)); Assert.Equal(value, cwt.GetValue(key, k => "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, k => "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, (k, a) => a, "value1")); Assert.Throws(() => cwt.AddOrUpdate(null, "value2")); @@ -377,6 +581,9 @@ public static void AddOrUpdateDataTest() Assert.Equal("value2", value); Assert.Equal(value, cwt.GetOrCreateValue(key)); Assert.Equal(value, cwt.GetValue(key, k => "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, k => "value1")); + Assert.Equal(value, cwt.GetOrAdd(key, (k, a) => a, "value1")); } [Fact] From bfa02766bdde4657ddd1f734e274d43e6ba3c036 Mon Sep 17 00:00:00 2001 From: Jesper Meyer Date: Wed, 29 Jan 2025 12:21:05 +0100 Subject: [PATCH 02/31] System.Text.Json: Fix polymorphic state bug when using ReferenceHandler.IgnoreCycles (#111808) * Fix issue #111790 Clear the polymorphic state after ignoring a reference to prevent subsequent object writes from inheriting it. * Copy tests to cover preserve references * Update metadata * Add classes to referencehandlertest.ignorecycles --------- Co-authored-by: Eirik Tsarpalis --- .../JsonConverter.MetadataHandling.cs | 7 +++ .../ReferenceHandlerTests.IgnoreCycles.cs | 42 ++++++++++++++++++ .../ReferenceHandlerTests.Serialize.cs | 44 +++++++++++++++++++ .../ReferenceHandlerTests.IgnoreCycles.cs | 10 +++++ .../Serialization/ReferenceHandlerTests.cs | 10 +++++ 5 files changed, 113 insertions(+) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs index 8f542612b90e4e..5fad7fd1e9a791 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.MetadataHandling.cs @@ -148,6 +148,13 @@ internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object v if (resolver.ContainsReferenceForCycleDetection(value)) { writer.WriteNullValue(); + + if (polymorphicConverter is not null) + { + // Clear out any polymorphic state. + state.PolymorphicTypeDiscriminator = null; + state.PolymorphicTypeResolver = null; + } return true; } diff --git a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs index 0affb96da401e4..fd092427890a00 100644 --- a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs +++ b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs @@ -429,6 +429,48 @@ public async Task IgnoreCycles_BoxedValueShouldNotBeIgnored() await Test_Serialize_And_SerializeAsync_Contains(root, expectedSubstring: @"""DayOfBirth"":15", expectedTimes: 2, s_optionsIgnoreCycles); } + [Fact] + public async Task IgnoreCycles_DerivedType_InArray() + { + var worker = new OfficeWorker + { + Office = new Office + { + Dummy = new() + } + }; + + worker.Office.Staff = [worker, new RemoteWorker()]; + + await Test_Serialize_And_SerializeAsync(worker, """{"Office":{"Staff":[null,{"$type":"remote"}],"Dummy":{}}}""", s_optionsIgnoreCycles); + + worker.Office.Staff = [worker]; + + await Test_Serialize_And_SerializeAsync(worker, """{"Office":{"Staff":[null],"Dummy":{}}}""", s_optionsIgnoreCycles); + } + + [JsonDerivedType(typeof(OfficeWorker), "office")] + [JsonDerivedType(typeof(RemoteWorker), "remote")] + public abstract class EmployeeLocation + { + } + + public class OfficeWorker : EmployeeLocation + { + public Office Office { get; set; } + } + + public class RemoteWorker : EmployeeLocation + { + } + + public class Office + { + public EmployeeLocation[] Staff { get; set; } + + public EmptyClass Dummy { get; set; } + } + [Fact] public async Task CycleDetectionStatePersistsAcrossContinuations() { diff --git a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs index dd166409de2699..8cc7cb1084f4b7 100644 --- a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs +++ b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs @@ -224,5 +224,49 @@ public async Task CustomHashCode() // otherwise objects would not be correctly identified when searching for them in the dictionary. Assert.Same(listCopy[0], listCopy[1]); } + + [Fact] + public async Task Preserve_DerivedType_InArray() + { + var worker = new OfficeWorker + { + Office = new Office + { + Dummy = new() + } + }; + + worker.Office.Staff = [worker, new RemoteWorker()]; + + string json = await Serializer.SerializeWrapper(worker, s_serializerOptionsPreserve); + Assert.Equal("""{"$id":"1","Office":{"$id":"2","Staff":[{"$ref":"1"},{"$id":"3","$type":"remote"}],"Dummy":{"$id":"4"}}}""", json); + + worker.Office.Staff = [worker]; + + json = await Serializer.SerializeWrapper(worker, s_serializerOptionsPreserve); + Assert.Equal("""{"$id":"1","Office":{"$id":"2","Staff":[{"$ref":"1"}],"Dummy":{"$id":"3"}}}""", json); + } + + [JsonDerivedType(typeof(OfficeWorker), "office")] + [JsonDerivedType(typeof(RemoteWorker), "remote")] + public abstract class EmployeeLocation + { + } + + public class OfficeWorker : EmployeeLocation + { + public Office Office { get; set; } + } + + public class RemoteWorker : EmployeeLocation + { + } + + public class Office + { + public EmployeeLocation[] Staff { get; set; } + + public EmptyClass Dummy { get; set; } + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs index 8c073e1e5e30ee..3f9f56a26340ab 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.IgnoreCycles.cs @@ -89,6 +89,11 @@ public ReferenceHandlerTests_IgnoreCycles_Metadata(JsonSerializerWrapper seriali [JsonSerializable(typeof(TreeNode>))] [JsonSerializable(typeof(TreeNode))] [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(EmployeeLocation))] + [JsonSerializable(typeof(EmployeeLocation[]))] + [JsonSerializable(typeof(OfficeWorker))] + [JsonSerializable(typeof(Office))] + [JsonSerializable(typeof(RemoteWorker))] internal sealed partial class ReferenceHandlerTests_IgnoreCyclesContext_Metadata : JsonSerializerContext { } @@ -172,6 +177,11 @@ public ReferenceHandlerTests_IgnoreCycles_Default(JsonSerializerWrapper serializ [JsonSerializable(typeof(TreeNode>))] [JsonSerializable(typeof(TreeNode))] [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(EmployeeLocation))] + [JsonSerializable(typeof(EmployeeLocation[]))] + [JsonSerializable(typeof(OfficeWorker))] + [JsonSerializable(typeof(Office))] + [JsonSerializable(typeof(RemoteWorker))] internal sealed partial class ReferenceHandlerTests_IgnoreCyclesContext_Default : JsonSerializerContext { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.cs index 03c1ede64586f0..36cb8c42f4efda 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ReferenceHandlerTests.cs @@ -139,6 +139,11 @@ public ReferenceHandlerTests_Metadata(JsonSerializerWrapper serializer) [JsonSerializable(typeof(ClassWithConflictingIdProperty))] [JsonSerializable(typeof(ClassWithIgnoredConflictingProperty))] [JsonSerializable(typeof(ClassWithExtensionDataConflictingProperty))] + [JsonSerializable(typeof(EmployeeLocation))] + [JsonSerializable(typeof(EmployeeLocation[]))] + [JsonSerializable(typeof(OfficeWorker))] + [JsonSerializable(typeof(Office))] + [JsonSerializable(typeof(RemoteWorker))] internal sealed partial class ReferenceHandlerTestsContext_Metadata : JsonSerializerContext { } @@ -281,6 +286,11 @@ public ReferenceHandlerTests_Default(JsonSerializerWrapper serializer) [JsonSerializable(typeof(ClassWithConflictingIdProperty))] [JsonSerializable(typeof(ClassWithIgnoredConflictingProperty))] [JsonSerializable(typeof(ClassWithExtensionDataConflictingProperty))] + [JsonSerializable(typeof(EmployeeLocation))] + [JsonSerializable(typeof(EmployeeLocation[]))] + [JsonSerializable(typeof(OfficeWorker))] + [JsonSerializable(typeof(Office))] + [JsonSerializable(typeof(RemoteWorker))] internal sealed partial class ReferenceHandlerTestsContext_Default : JsonSerializerContext { } From bcc0e894256b22736be3ffac876c7f21cffe070a Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 29 Jan 2025 15:18:24 +0000 Subject: [PATCH 03/31] arm64: Add tests for add(s), and(s), sub(s), cmp, cmn, eor, neg & orr (#111796) * arm64: Add tests for add(s), and(s), sub(s), cmp, cmn, eor, neg & orr * Add swap tests with operands in opposite order --- src/tests/JIT/opt/InstructionCombining/Add.cs | 215 ++++++++++++++++++ .../JIT/opt/InstructionCombining/Add.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/And.cs | 215 ++++++++++++++++++ .../JIT/opt/InstructionCombining/And.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Cmn.cs | 138 +++++++++++ .../JIT/opt/InstructionCombining/Cmn.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Cmp.cs | 138 +++++++++++ .../JIT/opt/InstructionCombining/Cmp.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Eor.cs | 109 +++++++++ .../JIT/opt/InstructionCombining/Eor.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Neg.cs | 98 ++++++++ .../JIT/opt/InstructionCombining/Neg.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Orr.cs | 109 +++++++++ .../JIT/opt/InstructionCombining/Orr.csproj | 17 ++ src/tests/JIT/opt/InstructionCombining/Sub.cs | 187 +++++++++++++++ .../JIT/opt/InstructionCombining/Sub.csproj | 17 ++ 16 files changed, 1345 insertions(+) create mode 100644 src/tests/JIT/opt/InstructionCombining/Add.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Add.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/And.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/And.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Cmn.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Cmn.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Cmp.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Cmp.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Eor.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Eor.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Neg.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Neg.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Orr.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Orr.csproj create mode 100644 src/tests/JIT/opt/InstructionCombining/Sub.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Sub.csproj diff --git a/src/tests/JIT/opt/InstructionCombining/Add.cs b/src/tests/JIT/opt/InstructionCombining/Add.cs new file mode 100644 index 00000000000000..0ac13412879c94 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Add.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestAdd +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckAdd() + { + bool fail = false; + + if (Add(1, 2) != 3) + { + fail = true; + } + + if (AddLSL(5, 5) != 85) + { + fail = true; + } + + if (AddLSLSwap(5, 5) != 85) + { + fail = true; + } + + if (AddLSR(1, 0x20000000) != 2) + { + fail = true; + } + + if (AddASR(-2, 0x4000) != -1) + { + fail = true; + } + + if (AddLargeShift(0x100000, 1) != 0x900000) + { + fail = true; + } + + if (AddLargeShift64Bit(0xAB, 0x19a0000000000) != 0x178) + { + fail = true; + } + + if (Adds(-5, 5) != 1) + { + fail = true; + } + + if (AddsLSL(-0x78000, 0xF) != 1) + { + fail = true; + } + + if (AddsLSLSwap(-0x78000, 0xF) != 1) + { + fail = true; + } + + if (AddsLSR(0, 0x3c0) != 1) + { + fail = true; + } + + if (AddsASR(-1, 0x800) != 1) + { + fail = true; + } + + if (AddsLargeShift(-0xFF, 0x1fe0) != 1) + { + fail = true; + } + + if (AddsLargeShift64Bit(-0x40000000000, 1) != 1) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Add(int a, int b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a + b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddLSL(int a, int b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return a + (b<<4); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return (b<<4) + a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint AddLSR(uint a, uint b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #29 + return a + (b>>29); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddASR(int a, int b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #14 + return a + (b>>14); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddLargeShift(int a, int b) + { + //ARM64-FULL-LINE: add {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #23 + return a + (b<<183); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long AddLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: add {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, ASR #41 + return a + (b>>169); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Adds(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + if (a + b == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddsLSL(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #15 + if (a + (b<<15) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddsLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #15 + if ((b<<15) + a == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddsLSR(uint a, uint b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #6 + if (a + (b>>6) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddsASR(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #11 + if (a + (b>>11) == 0) { + return 1; + } + return -1; + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AddsLargeShift(int a, int b) + { + //ARM64-FULL-LINE: adds {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #5 + if (a + (b>>133) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long AddsLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: adds {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSL #42 + if (a + (b<<106) == 0) { + return 1; + } + return -1; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Add.csproj b/src/tests/JIT/opt/InstructionCombining/Add.csproj new file mode 100644 index 00000000000000..1328d31335bd09 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Add.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/And.cs b/src/tests/JIT/opt/InstructionCombining/And.cs new file mode 100644 index 00000000000000..9a16c1938d45d8 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/And.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestAnd +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckAnd() + { + bool fail = false; + + if (And(3, 2) != 2) + { + fail = true; + } + + if (AndLSL(255, 1) != 16) + { + fail = true; + } + + if (AndLSLSwap(255, 1) != 16) + { + fail = true; + } + + if (AndLSR(255, 0x10000000) != 8) + { + fail = true; + } + + if (AndASR(-5, 0x3C00) != 0xB) + { + fail = true; + } + + if (AndLargeShift(9, 1) != 8) + { + fail = true; + } + + if (AndLargeShift64Bit(0xF000000000, 7) != 0xE000000000) + { + fail = true; + } + + if (Ands(4, 4) != 1) + { + fail = true; + } + + if (AndsLSL(8, 2) != 1) + { + fail = true; + } + + if (AndsLSLSwap(8, 2) != 1) + { + fail = true; + } + + if (AndsLSR(3, 0xa0000) != 1) + { + fail = true; + } + + if (AndsASR(6, 0x6000) != 1) + { + fail = true; + } + + if (AndsLargeShift(0x80000000, 1) != 1) + { + fail = true; + } + + if (AndsLargeShift64Bit(0xD, 0x34000000000) != 1) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int And(int a, int b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a & b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndLSL(int a, int b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return a & (b<<4); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return (b<<4) & a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint AndLSR(uint a, uint b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #25 + return a & (b>>25); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndASR(int a, int b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #10 + return a & (b>>10); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndLargeShift(int a, int b) + { + //ARM64-FULL-LINE: and {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #3 + return a & (b<<67); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long AndLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: and {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSL #37 + return a & (b<<101); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Ands(int a, int b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}} + if ((a & b) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsLSL(int a, int b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if ((a & (b<<2)) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if (((b<<2) & a) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsLSR(uint a, uint b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}}, LSR #17 + if ((a & (b>>17)) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsASR(int a, int b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}}, ASR #12 + if ((a & (b>>12)) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsLargeShift(uint a, uint b) + { + //ARM64-FULL-LINE: tst {{w[0-9]+}}, {{w[0-9]+}}, LSL #31 + if ((a & (b<<255)) != 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int AndsLargeShift64Bit(ulong a, ulong b) + { + //ARM64-FULL-LINE: lsr {{x[0-9]+}}, {{x[0-9]+}}, #38 + //ARM64-FULL-LINE: tst {{x[0-9]+}}, {{x[0-9]+}} + if ((a & (b>>230)) != 0) { + return 1; + } + return -1; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/And.csproj b/src/tests/JIT/opt/InstructionCombining/And.csproj new file mode 100644 index 00000000000000..8e7b7770b02247 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/And.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Cmn.cs b/src/tests/JIT/opt/InstructionCombining/Cmn.cs new file mode 100644 index 00000000000000..f8e05ed9125a0d --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Cmn.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestCompareNegative +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckCompareNegative() + { + bool fail = false; + + if (!Cmn(-4, 4)) + { + fail = true; + } + + if (!CmnLSL(-16, 4)) + { + fail = true; + } + + if (!CmnLSLSwap(-16, 4)) + { + fail = true; + } + + if (!CmnLSR(0xFFFFFFFC, 0x10)) + { + fail = true; + } + + if (!CmnASR(-0xA, 0x2800)) + { + fail = true; + } + + if (!CmnLargeShift(-0x18, 0x3)) + { + fail = true; + } + + if (!CmnLargeShift64Bit(-0x300000000000000, 0x6)) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Cmn(int a, int b) + { + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}} + if (a == -b) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnLSL(int a, int b) + { + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if (a == -(b<<2)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if (-(b<<2) == a) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnLSR(uint a, uint b) + { + //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #2 + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}} + if (a == (uint)-(b>>2)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnASR(int a, int b) + { + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}}, ASR #10 + if (a == -(b>>10)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnLargeShift(int a, int b) + { + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}}, LSL #3 + if (a == -(b<<35)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmnLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: cmn {{x[0-9]+}}, {{x[0-9]+}}, LSL #55 + if (a == -(b<<247)) + { + return true; + } + return false; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Cmn.csproj b/src/tests/JIT/opt/InstructionCombining/Cmn.csproj new file mode 100644 index 00000000000000..f92126ec42c194 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Cmn.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Cmp.cs b/src/tests/JIT/opt/InstructionCombining/Cmp.cs new file mode 100644 index 00000000000000..0ce7de0faee544 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Cmp.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestCompare +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckCompare() + { + bool fail = false; + + if (!Cmp(12, 12)) + { + fail = true; + } + + if (!CmpLSL(12, 3)) + { + fail = true; + } + + if (!CmpLSLSwap(12, 3)) + { + fail = true; + } + + if (!CmpLSR(5, 0xa00)) + { + fail = true; + } + + if (!CmpASR(7, 0x380)) + { + fail = true; + } + + if (!CmpLargeShift(0x500000, 0xA)) + { + fail = true; + } + + if (!CmpLargeShift64Bit(0x580000000000000, 0xB)) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Cmp(int a, int b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}} + if (a == b) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpLSL(int a, int b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if (a == (b<<2)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + if ((b<<2) == a) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpLSR(uint a, uint b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}}, LSR #9 + if (a == (b>>9)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpASR(int a, int b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}}, ASR #7 + if (a == (b>>7)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpLargeShift(int a, int b) + { + //ARM64-FULL-LINE: cmp {{w[0-9]+}}, {{w[0-9]+}}, LSL #19 + if (a == (b<<115)) + { + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CmpLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: lsl {{x[0-9]+}}, {{x[0-9]+}}, #55 + //ARM64-FULL-LINE: cmp {{x[0-9]+}}, {{x[0-9]+}} + if (a == (b<<119)) + { + return true; + } + return false; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Cmp.csproj b/src/tests/JIT/opt/InstructionCombining/Cmp.csproj new file mode 100644 index 00000000000000..121723d2791f87 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Cmp.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Eor.cs b/src/tests/JIT/opt/InstructionCombining/Eor.cs new file mode 100644 index 00000000000000..f62c53a52268c1 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Eor.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestEor +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckEor() + { + bool fail = false; + + if (Eor(5, 3) != 6) + { + fail = true; + } + + if (EorLSL(32, 3) != 16) + { + fail = true; + } + + if (EorLSLSwap(32, 3) != 16) + { + fail = true; + } + + if (EorLSR(0xBA, 0xABCDE) != 0x11) + { + fail = true; + } + + if (EorASR(0x8282, 0x1DA00000) != 0x82B9) + { + fail = true; + } + + if (EorLargeShift(0xDCBA, 0x1F) != 0xc3ba) + { + fail = true; + } + + if (EorLargeShift64Bit(0x2468, 0x26ae123456789ABC) != 0x373F) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Eor(int a, int b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a ^ b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int EorLSL(int a, int b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return a ^ (b<<4); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int EorLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #4 + return (b<<4) ^ a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint EorLSR(uint a, uint b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #12 + return a ^ (b>>12); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int EorASR(int a, int b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #23 + return a ^ (b>>23); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int EorLargeShift(int a, int b) + { + //ARM64-FULL-LINE: eor {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #8 + return a ^ (b<<136); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong EorLargeShift64Bit(ulong a, ulong b) + { + //ARM64-FULL-LINE: eor {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSR #49 + return a ^ (b>>177); + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Eor.csproj b/src/tests/JIT/opt/InstructionCombining/Eor.csproj new file mode 100644 index 00000000000000..dd4c42699f2e68 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Eor.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Neg.cs b/src/tests/JIT/opt/InstructionCombining/Neg.cs new file mode 100644 index 00000000000000..cf2850eab045a0 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Neg.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestNeg +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckNeg() + { + bool fail = false; + + if (Neg(3) != -3) + { + fail = true; + } + + if (NegLSL(4) != -16) + { + fail = true; + } + + if (NegLSR(0x300000) != 0xFFFFFFFD) + { + fail = true; + } + + if (NegASR(0xA000) != -5) + { + fail = true; + } + + if (NegLargeShift(0xC) != -0x180000) + { + fail = true; + } + + if (NegLargeShift64Bit(0xD) != 0x6000000000000000) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Neg(int a) + { + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}} + return -a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int NegLSL(int a) + { + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}}, LSL #2 + return -(a<<2); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint NegLSR(uint a) + { + //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #20 + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}} + return (uint)-(a>>20); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int NegASR(int a) + { + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}}, ASR #13 + return -(a>>13); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int NegLargeShift(int a) + { + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}}, LSL #17 + return -(a<<81); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long NegLargeShift64Bit(long a) + { + //ARM64-FULL-LINE: neg {{x[0-9]+}}, {{x[0-9]+}}, LSL #61 + return -(a<<189); + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Neg.csproj b/src/tests/JIT/opt/InstructionCombining/Neg.csproj new file mode 100644 index 00000000000000..5593e2e3ec2db3 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Neg.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Orr.cs b/src/tests/JIT/opt/InstructionCombining/Orr.cs new file mode 100644 index 00000000000000..204b1c85d1eab2 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Orr.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestOrr +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckOrr() + { + bool fail = false; + + if (Orr(2, 4) != 6) + { + fail = true; + } + + if (OrrLSL(0x180, 5) != 0x1C0) + { + fail = true; + } + + if (OrrLSLSwap(0x180, 5) != 0x1C0) + { + fail = true; + } + + if (OrrLSR(0x7, 0x1234) != 0x27) + { + fail = true; + } + + if (OrrASR(0x13, 0xA98765) != 0x3B) + { + fail = true; + } + + if (OrrLargeShift(0x1A40, 0x8E) != 0x1BC0) + { + fail = true; + } + + if (OrrLargeShift64Bit(0x2468, 0xDEF1234567) != 0x246F) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Orr(int a, int b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a | b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int OrrLSL(int a, int b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #6 + return a | (b<<6); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int OrrLSLSwap(int a, int b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #6 + return (b<<6) | a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint OrrLSR(uint a, uint b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #7 + return a | (b>>7); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int OrrASR(int a, int b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #18 + return a | (b>>18); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int OrrLargeShift(int a, int b) + { + //ARM64-FULL-LINE: orr {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #5 + return a | (b<<101); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong OrrLargeShift64Bit(ulong a, ulong b) + { + //ARM64-FULL-LINE: orr {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSR #33 + return a | (b>>289); + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Orr.csproj b/src/tests/JIT/opt/InstructionCombining/Orr.csproj new file mode 100644 index 00000000000000..07c6535e553e0a --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Orr.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + diff --git a/src/tests/JIT/opt/InstructionCombining/Sub.cs b/src/tests/JIT/opt/InstructionCombining/Sub.cs new file mode 100644 index 00000000000000..105666b2ff6194 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Sub.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestSub +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckSub() + { + bool fail = false; + + if (Sub(5, 2) != 3) + { + fail = true; + } + + if (SubLSL(100, 2) != 84) + { + fail = true; + } + + if (SubLSR(10, 64) != 6) + { + fail = true; + } + + if (SubASR(10, 320) != 5) + { + fail = true; + } + + if (SubLargeShift(0x40000F, 1) != 0xF) + { + fail = true; + } + + if (SubLargeShift64Bit(0x40000000000ACE, 1) != 0xACE) + { + fail = true; + } + + if (Subs(15, 15) != 1) + { + fail = true; + } + + if (SubsLSL(14, 7) != 1) + { + fail = true; + } + + if (SubsLSR(1, 0x80000000) != 1) + { + fail = true; + } + + if (SubsASR(27, 0x1B00000) != 1) + { + fail = true; + } + + if (SubsLargeShift(4, 2) != 1) + { + fail = true; + } + + if (SubsLargeShift64Bit(0x300000000, 3) != 1) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Sub(int a, int b) + { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + return a - b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubLSL(int a, int b) + { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #3 + return a - (b<<3); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint SubLSR(uint a, uint b) + { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #4 + return a - (b>>4); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubASR(int a, int b) + { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #6 + return a - (b>>6); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubLargeShift(int a, int b) + { + //ARM64-FULL-LINE: sub {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #22 + return a - (b<<118); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long SubLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: sub {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSL #54 + return a - (b<<118); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Subs(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}} + if (a - b == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubsLSL(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #1 + if (a - (b<<1) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubsLSR(uint a, uint b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSR #31 + if (a - (b>>31) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubsASR(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, ASR #20 + if (a - (b>>20) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubsLargeShift(int a, int b) + { + //ARM64-FULL-LINE: subs {{w[0-9]+}}, {{w[0-9]+}}, {{w[0-9]+}}, LSL #1 + if (a - (b<<33) == 0) { + return 1; + } + return -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int SubsLargeShift64Bit(long a, long b) + { + //ARM64-FULL-LINE: subs {{x[0-9]+}}, {{x[0-9]+}}, {{x[0-9]+}}, LSL #32 + if (a - (b<<96) == 0) { + return 1; + } + return -1; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Sub.csproj b/src/tests/JIT/opt/InstructionCombining/Sub.csproj new file mode 100644 index 00000000000000..fdea1deaa97d69 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Sub.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + From 3e32e51532ac941cb0a1dd04dcb78e2278ca74a1 Mon Sep 17 00:00:00 2001 From: Jonathan Davies Date: Wed, 29 Jan 2025 15:55:40 +0000 Subject: [PATCH 04/31] arm64: Add support for bitwise NOT (#111904) * Contributes to #68028 --- src/coreclr/jit/codegenarm64.cpp | 6 +- src/coreclr/jit/lower.cpp | 5 + src/coreclr/jit/lower.h | 1 + src/coreclr/jit/lowerarmarch.cpp | 27 +++++- src/tests/JIT/opt/InstructionCombining/Mvn.cs | 97 +++++++++++++++++++ .../JIT/opt/InstructionCombining/Mvn.csproj | 17 ++++ 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 src/tests/JIT/opt/InstructionCombining/Mvn.cs create mode 100644 src/tests/JIT/opt/InstructionCombining/Mvn.csproj diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 9c215b0ec69918..8811349b8cdbb3 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -3490,13 +3490,15 @@ void CodeGen::genCodeForNegNot(GenTree* tree) GenTree* operand = tree->gtGetOp1(); // The src must be a register. - if (tree->OperIs(GT_NEG) && operand->isContained()) + if (tree->OperIs(GT_NEG, GT_NOT) && operand->isContained()) { genTreeOps oper = operand->OperGet(); switch (oper) { case GT_MUL: { + assert(tree->OperIs(GT_NEG)); + ins = INS_mneg; GenTree* op1 = tree->gtGetOp1(); GenTree* a = op1->gtGetOp1(); @@ -3510,7 +3512,7 @@ void CodeGen::genCodeForNegNot(GenTree* tree) case GT_RSH: case GT_RSZ: { - assert(ins == INS_neg || ins == INS_negs); + assert(ins == INS_neg || ins == INS_negs || ins == INS_mvn); assert(operand->gtGetOp2()->IsCnsIntOrI()); assert(operand->gtGetOp2()->isContained()); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 724dd17082a4e3..484bf1454e5fd3 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -511,6 +511,11 @@ GenTree* Lowering::LowerNode(GenTree* node) } #endif break; + case GT_NOT: +#ifdef TARGET_ARM64 + ContainCheckNot(node->AsOp()); +#endif + break; case GT_SELECT: return LowerSelect(node->AsConditional()); diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 659870c844cd73..7fa3aca9e98511 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -94,6 +94,7 @@ class Lowering final : public Phase insCflags TruthifyingFlags(GenCondition cond); void ContainCheckConditionalCompare(GenTreeCCMP* ccmp); void ContainCheckNeg(GenTreeOp* neg); + void ContainCheckNot(GenTreeOp* notOp); void TryLowerCnsIntCselToCinc(GenTreeOp* select, GenTree* cond); void TryLowerCselToCSOp(GenTreeOp* select, GenTree* cond); bool TryLowerAddSubToMulLongOp(GenTreeOp* op, GenTree** next); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 471ec849686e92..852d912a133e78 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -304,7 +304,7 @@ bool Lowering::IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childN } } - if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && parentNode->OperIs(GT_AND_NOT)) + if (childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && parentNode->OperIs(GT_NOT, GT_AND_NOT)) { return true; } @@ -3290,6 +3290,31 @@ void Lowering::ContainCheckNeg(GenTreeOp* neg) } } +//------------------------------------------------------------------------ +// ContainCheckNot : determine whether the source of a not should be contained. +// +// Arguments: +// notOp - pointer to the node +// +void Lowering::ContainCheckNot(GenTreeOp* notOp) +{ + if (notOp->isContained()) + return; + + if (!varTypeIsIntegral(notOp)) + return; + + if ((notOp->gtFlags & GTF_SET_FLAGS)) + return; + + GenTree* childNode = notOp->gtGetOp1(); + if (comp->opts.OptimizationEnabled() && childNode->OperIs(GT_LSH, GT_RSH, GT_RSZ) && + IsContainableUnaryOrBinaryOp(notOp, childNode)) + { + MakeSrcContained(notOp, childNode); + } +} + //---------------------------------------------------------------------------------------------- // TryLowerCselToCSOp: Try converting SELECT/SELECTCC to SELECT_?/SELECT_?CC. Conversion is possible only if // one of the operands of the select node is one of GT_NEG, GT_NOT or GT_ADD. diff --git a/src/tests/JIT/opt/InstructionCombining/Mvn.cs b/src/tests/JIT/opt/InstructionCombining/Mvn.cs new file mode 100644 index 00000000000000..74957a091e63af --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Mvn.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +namespace TestMvn +{ + public class Program + { + [MethodImpl(MethodImplOptions.NoInlining)] + [Fact] + public static int CheckMvn() + { + bool fail = false; + + if (Mvn(5) != 0xFFFFFFFA) + { + fail = true; + } + + if (MvnLSL(10) != 0xFFFAFFFF) + { + fail = true; + } + + if (MvnLSR(0x76543210) != 0xFFFFFF89) + { + fail = true; + } + + if (MvnASR(0xACE1234) != -0x5670A) + { + fail = true; + } + + if (MvnLargeShift(0x1A1A) != 0x5FFFFFFf) + { + fail = true; + } + + if (MvnLargeShift64Bit(0x2B3C2B3C2B3C2B3C) != 0xFFFFFFFFD4C3D4C3) + { + fail = true; + } + + if (fail) + { + return 101; + } + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint Mvn(uint a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}} + return ~a; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint MvnLSL(uint a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}}, LSL #15 + return ~(a<<15); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint MvnLSR(uint a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}}, LSR #24 + return ~(a>>24); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int MvnASR(int a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}}, ASR #9 + return ~(a>>9); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint MvnLargeShift(uint a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}}, LSL #28 + return ~(a<<60); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong MvnLargeShift64Bit(ulong a) + { + //ARM64-FULL-LINE: mvn {{x[0-9]+}}, {{x[0-9]+}}, LSR #32 + return ~(a>>160); + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/Mvn.csproj b/src/tests/JIT/opt/InstructionCombining/Mvn.csproj new file mode 100644 index 00000000000000..4ad4ea2e208934 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/Mvn.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + + From c5921ed898702e8628af0ce7b1943a063fd76cd1 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 29 Jan 2025 08:09:29 -0800 Subject: [PATCH 05/31] JIT: Remove OSR quirk in `fgOptimizeUncondBranchToSimpleCond` (#111936) Looks like a relic from when loop recognition was lexical. --- src/coreclr/jit/fgopt.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 6187274c70332f..244d8bcd9efc1f 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -2213,27 +2213,6 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* // At this point we know target is BBJ_COND. assert(target->KindIs(BBJ_COND)); - // Bail out if OSR, as we can have unusual flow into loops. If one - // of target's successors is also a backedge target, this optimization - // may mess up loop recognition by creating too many non-loop preds. - // - if (opts.IsOSR()) - { - if (target->GetFalseTarget()->HasFlag(BBF_BACKWARD_JUMP_TARGET)) - { - JITDUMP("Deferring: " FMT_BB " --> " FMT_BB "; latter looks like loop top\n", target->bbNum, - target->GetFalseTarget()->bbNum); - return false; - } - - if (target->GetTrueTarget()->HasFlag(BBF_BACKWARD_JUMP_TARGET)) - { - JITDUMP("Deferring: " FMT_BB " --> " FMT_BB "; latter looks like loop top\n", target->bbNum, - target->GetTrueTarget()->bbNum); - return false; - } - } - // See if this block assigns constant or other interesting tree to that same local. // if (!fgBlockEndFavorsTailDuplication(block, lclNum)) From d410898949f19681587bb308e6c50190350f3d81 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 29 Jan 2025 08:10:41 -0800 Subject: [PATCH 06/31] JIT: Skip `fgComputeMissingBlockWeights` when we have profile data (#111873) Part of #107749. Follow-up to #111764. fgComputeMissingBlockWeights doesn't modify anything when we have profile data, and fgCalledCount can be derived from the entry block's weight quite simply. --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/fgprofile.cpp | 119 +++++----------------------------- src/coreclr/jit/morph.cpp | 2 +- 3 files changed, 17 insertions(+), 107 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 442dd8f17a5a24..4a639c7e639f9e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6310,8 +6310,7 @@ class Compiler void fgPrintEdgeWeights(); #endif PhaseStatus fgComputeBlockWeights(); - bool fgComputeMissingBlockWeights(weight_t* returnWeight); - bool fgComputeCalledCount(weight_t returnWeight); + bool fgComputeMissingBlockWeights(); bool fgReorderBlocks(bool useProfile); void fgDoReversePostOrderLayout(); diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 2a21e61f0d3a09..63634dc2edeb54 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -4210,10 +4210,7 @@ bool Compiler::fgIncorporateEdgeCounts() // PhaseStatus Compiler::fgComputeBlockWeights() { - const bool usingProfileWeights = fgIsUsingProfileWeights(); - bool madeChanges = false; - fgModified = false; - fgCalledCount = BB_UNITY_WEIGHT; + fgModified = false; #if DEBUG if (verbose) @@ -4223,40 +4220,38 @@ PhaseStatus Compiler::fgComputeBlockWeights() } #endif // DEBUG - weight_t returnWeight = BB_UNITY_WEIGHT; - - madeChanges |= fgComputeMissingBlockWeights(&returnWeight); - - if (usingProfileWeights) + if (fgIsUsingProfileWeights()) { - madeChanges |= fgComputeCalledCount(returnWeight); - } - else - { - JITDUMP(" -- no profile data, so using default called count\n"); + // Compute fgCalledCount by subtracting any non-entry flow into fgFirstBB from its weight + fgCalledCount = fgFirstBB->bbWeight; + for (FlowEdge* const predEdge : fgFirstBB->PredEdges()) + { + fgCalledCount = max(BB_ZERO_WEIGHT, fgCalledCount - predEdge->getLikelyWeight()); + } + + JITDUMP("We are using the profile weights and fgCalledCount is " FMT_WT "\n", fgCalledCount); + return PhaseStatus::MODIFIED_NOTHING; } - return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; + JITDUMP(" -- no profile data, so using default called count\n"); + fgCalledCount = BB_UNITY_WEIGHT; + return fgComputeMissingBlockWeights() ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } //------------------------------------------------------------- // fgComputeMissingBlockWeights: determine weights for blocks // that were not profiled and do not yet have weights. // -// Arguments -// returnWeight [out] - sum of weights for all return and throw blocks -// // Returns: // true if any changes made // -bool Compiler::fgComputeMissingBlockWeights(weight_t* returnWeight) +bool Compiler::fgComputeMissingBlockWeights() { BasicBlock* bSrc; BasicBlock* bDst; unsigned iterations = 0; bool changed; bool modified = false; - weight_t weight; // If we have any blocks that did not have profile derived weight // we will try to fix their weight up here @@ -4265,7 +4260,6 @@ bool Compiler::fgComputeMissingBlockWeights(weight_t* returnWeight) do // while (changed) { changed = false; - weight = 0; iterations++; for (bDst = fgFirstBB; bDst != nullptr; bDst = bDst->Next()) @@ -4376,14 +4370,6 @@ bool Compiler::fgComputeMissingBlockWeights(weight_t* returnWeight) bDst->bbSetRunRarely(); } } - - // Sum up the weights of all of the return blocks and throw blocks - // This is used when we have a back-edge into block 1 - // - if (bDst->hasProfileWeight() && bDst->KindIs(BBJ_RETURN, BBJ_THROW)) - { - weight += bDst->bbWeight; - } } } // Generally when we synthesize profile estimates we do it in a way where this algorithm will converge @@ -4400,84 +4386,9 @@ bool Compiler::fgComputeMissingBlockWeights(weight_t* returnWeight) } #endif - *returnWeight = weight; - return modified; } -//------------------------------------------------------------- -// fgComputeCalledCount: when profile information is in use, -// compute fgCalledCount -// -// Argument: -// returnWeight - sum of weights for all return and throw blocks -// -// Returns: -// true if any changes were made -// -bool Compiler::fgComputeCalledCount(weight_t returnWeight) -{ - // When we are not using profile data we have already setup fgCalledCount - // only set it here if we are using profile data - assert(fgIsUsingProfileWeights()); - bool madeChanges = false; - - BasicBlock* firstILBlock = fgFirstBB; // The first block for IL code (i.e. for the IL code at offset 0) - - // OSR methods can have complex entry flow, and so - // for OSR we ensure fgFirstBB has plausible profile data. - // - if (!opts.IsOSR()) - { - // Skip past any/all BBF_INTERNAL blocks that may have been added before the first real IL block. - // - while (firstILBlock->HasFlag(BBF_INTERNAL)) - { - firstILBlock = firstILBlock->Next(); - } - } - - // The 'firstILBlock' is now expected to have a profile-derived weight - assert(firstILBlock->hasProfileWeight()); - - // If the first block only has one ref then we use its weight for fgCalledCount. - // Otherwise we have backedges into the first block, so instead we use the sum - // of the return block weights for fgCalledCount. - // - // If the profile data has a 0 for the returnWeight - // (i.e. the function never returns because it always throws) - // then just use the first block weight rather than 0. - // - if ((firstILBlock->countOfInEdges() == 1) || (returnWeight == BB_ZERO_WEIGHT)) - { - fgCalledCount = firstILBlock->bbWeight; - } - else - { - fgCalledCount = returnWeight; - } - - // If we allocated a scratch block as the first BB then we need - // to set its profile-derived weight to be fgCalledCount - if (fgFirstBB->HasFlag(BBF_INTERNAL)) - { - fgFirstBB->setBBProfileWeight(fgCalledCount); - madeChanges = true; - JITDUMP("fgComputeCalledCount: Modified method entry weight. Data %s inconsistent.\n", - fgPgoConsistent ? "is now" : "was already"); - fgPgoConsistent = false; - } - -#if DEBUG - if (verbose) - { - printf("We are using the Profile Weights and fgCalledCount is " FMT_WT "\n", fgCalledCount); - } -#endif - - return madeChanges; -} - //------------------------------------------------------------------------ // fgProfileWeightsEqual: check if two profile weights are equal // (or nearly so) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 31785e2e052e49..a787fece19fb6d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13533,7 +13533,7 @@ PhaseStatus Compiler::fgMorphBlocks() if (!fgProfileWeightsConsistent(incomingWeight, fgEntryBB->bbWeight)) { JITDUMP("OSR: Original method entry " FMT_BB " has inconsistent weight. Data %s inconsistent.\n", - fgPgoConsistent ? "is now" : "was already"); + fgEntryBB->bbNum, fgPgoConsistent ? "is now" : "was already"); fgPgoConsistent = false; } } From f71255e9f563cde3214f746eef0285a934803c18 Mon Sep 17 00:00:00 2001 From: Ivan Povazan <55002338+ivanpovazan@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:33:33 +0100 Subject: [PATCH 07/31] [android] Internal Android sample build improvements and CoreCLR support (#111545) * Move default AOT properties to a common props file * Use AndroidBuild.targets in AndroidSampleApp * Adding a comment on usage of UseAotDataFile opt * Relocate MonoAOTCompiler.props and ship it as part of LibraryBuilder.nupkg * Add functional test for Android library mode * We need the comment as we are referring to OS versions in the AOT compiler props * Disable the library mode tests * Do not force UseMonoRuntime in AndroidBuild.props * Avoid using CoreCLR properties when building mono runtime tests * Revert "Avoid using CoreCLR properties when building mono runtime tests" This reverts commit 156cc0e8bdc200fa6a9b92e0c5c202975cc3143b. * workaround for runtime tests which imports coreCLR Directory.Build.props and forces UseMonoRuntime=false * Adapt build integration targets to work with CoreCLR * Use RuntimeHeaders everywhere * Link issues for TODOs --- Directory.Build.props | 2 +- src/libraries/tests.proj | 8 + .../msbuild/android/build/AndroidBuild.props | 25 ++- .../android/build/AndroidBuild.targets | 58 +++---- src/mono/msbuild/apple/build/AppleBuild.props | 3 + .../msbuild/apple/build/AppleBuild.targets | 4 +- .../msbuild/common/LibraryBuilder.targets | 2 +- .../msbuild/common}/MonoAOTCompiler.props | 22 +-- ...oft.NET.Runtime.LibraryBuilder.Sdk.pkgproj | 1 + .../sample/Android/AndroidSampleApp.csproj | 144 ++---------------- src/mono/sample/Android/Makefile | 2 +- src/mono/sample/mbr/apple/AppleDelta.csproj | 2 +- .../AotCompilerTask/MonoAOTCompiler.csproj | 5 - ..._Emulator.LibraryMode_Aot_Llvm.Test.csproj | 25 +++ .../ILLink.Descriptors.xml | 7 + .../LibraryMode_AOT_LLVM/Program.cs | 20 +++ 16 files changed, 136 insertions(+), 194 deletions(-) rename src/{tasks/AotCompilerTask => mono/msbuild/common}/MonoAOTCompiler.props (82%) create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Android.Device_Emulator.LibraryMode_Aot_Llvm.Test.csproj create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/ILLink.Descriptors.xml create mode 100644 src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Program.cs diff --git a/Directory.Build.props b/Directory.Build.props index 7b78bd6efc0f6b..5eeb3ed7f86822 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -63,7 +63,7 @@ - src/mono/msbuild/apple/build/AppleBuild.targets - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-x64.xml - src/installer/pkg/sfx/bundle/shared-framework-distribution-template-arm64.xml - - src/tasks/AotCompilerTask/MonoAOTCompiler.props + - src/mono/msbuild/common/MonoAOTCompiler.props - src/tasks/AppleAppBuilder/Xcode.cs - src/tasks/MobileBuildTasks/Apple/AppleProject.cs - https://github.com/dotnet/sdk repo > src/Installer/redist-installer/targets/GeneratePKG.targets diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 76f22763eac181..a71adb42e82b3e 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -256,6 +256,14 @@ + + + + + + + + diff --git a/src/mono/msbuild/android/build/AndroidBuild.props b/src/mono/msbuild/android/build/AndroidBuild.props index 9263ef8a7c82d2..de95c1d581efbb 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.props +++ b/src/mono/msbuild/android/build/AndroidBuild.props @@ -1,18 +1,21 @@ $(TargetOS)-$(TargetArchitecture.ToLowerInvariant()) - true - true + + false + true + true <_HostOS Condition="$([MSBuild]::IsOSPlatform('Windows'))">windows <_HostOS Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx <_HostOS Condition="'$(_HostOS)' == ''">linux - <_IsLibraryMode Condition="'$(UseNativeAOTRuntime)' != 'true' and '$(NativeLib)' != ''">true - <_ReadRuntimeComponentsManifestTargetName Condition="'$(UseNativeAOTRuntime)' != 'true'">_MonoReadAvailableComponentsManifest + <_IsLibraryMode Condition="'$(UseMonoRuntime)' == 'true' and '$(UseNativeAOTRuntime)' != 'true' and '$(NativeLib)' != ''">true + <_ReadRuntimeComponentsManifestTargetName Condition="'$(UseMonoRuntime)' == 'true' and '$(UseNativeAOTRuntime)' != 'true'">_MonoReadAvailableComponentsManifest Publish - + $(_ReadRuntimeComponentsManifestTargetName); _InitializeCommonProperties; _BeforeAndroidBuild; @@ -23,11 +26,19 @@ _AndroidGenerateAppBundle; _AfterAndroidBuild + + _InitializeCommonProperties; + _BeforeAndroidBuild; + _AndroidResolveReferences; + _AndroidGenerateAppBundle; + _AfterAndroidBuild + <_CommonTargetsDir Condition="'$(_CommonTargetsDir)' == ''">$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), '..', '..', 'common')) - - + + + \ No newline at end of file diff --git a/src/mono/msbuild/android/build/AndroidBuild.targets b/src/mono/msbuild/android/build/AndroidBuild.targets index 6774939f2fba4e..b5c2a588962631 100644 --- a/src/mono/msbuild/android/build/AndroidBuild.targets +++ b/src/mono/msbuild/android/build/AndroidBuild.targets @@ -7,9 +7,6 @@ false - @@ -24,6 +21,8 @@ <_MobileIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'mobile')) $(PlatformTarget) + $(AssemblyName) + False @@ -38,10 +37,11 @@ $([MSBuild]::NormalizeDirectory('$(OutDir)', 'Bundle')) $(AndroidBundleDir) - <_MonoHeaderPath>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0')) + $(CoreClrProjectRoot)hosts\inc - + @@ -94,7 +94,7 @@ <_AOTMode Condition="'$(UseMonoJustInterp)' != 'true'">Normal <_AOTMode Condition="'$(UseMonoJustInterp)' == 'true'">JustInterp - <_AOTMode Condition="'$(ForceFullAOT)' == 'true'">Full + <_AOTMode Condition="'$(ForceAOT)' == 'true' and '$(AOTWithLibraryFiles)' != 'true'">Full <_AotOutputType>AsmOnly @@ -112,33 +112,11 @@ <_AotOutputType>ObjectFile - - <_Triple Condition="'$(TargetArchitecture)' == 'arm'">armv7-linux-gnueabi - <_Triple Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android - <_Triple Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android - <_Triple Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android - - - - <_AsOptions>-target $(_Triple) -c -x assembler - <_LdName>clang - <_LdOptions>-fuse-ld=lld - <_AsName>clang - - + - - - - - - - - - @@ -150,11 +128,6 @@ 21 - - <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) - <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) - - <_AotInputAssemblies Include="@(_AssembliesToBundleInternal)" Condition="'%(_AssembliesToBundleInternal._InternalForceInterpret)' != 'true'"> @@ -246,8 +219,7 @@ - @@ -255,7 +227,15 @@ <_NativeDependencies Include="$(LibraryOutputPath)" /> + + + <_RuntimeFlavor>Mono + <_RuntimeFlavor Condition="'$(UseMonoRuntime)' == 'false' and '$(UseNativeAOTRuntime)' != 'true'">CoreCLR + + + StripDebugSymbols="$(StripDebugSymbols)"> diff --git a/src/mono/msbuild/apple/build/AppleBuild.props b/src/mono/msbuild/apple/build/AppleBuild.props index 6625f122c63efb..c1e8952f835e76 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.props +++ b/src/mono/msbuild/apple/build/AppleBuild.props @@ -12,6 +12,8 @@ true $(TargetOS)-$(TargetArchitecture.ToLowerInvariant()) + true true @@ -46,4 +48,5 @@ + \ No newline at end of file diff --git a/src/mono/msbuild/apple/build/AppleBuild.targets b/src/mono/msbuild/apple/build/AppleBuild.targets index 0aafe5b5612d09..d14a4c7b79a069 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.targets +++ b/src/mono/msbuild/apple/build/AppleBuild.targets @@ -54,7 +54,7 @@ $([MSBuild]::NormalizeDirectory('$(OutDir)', 'Bundle')) $(AppleBundleDir) - <_MonoHeaderPath Condition="'$(UseMonoRuntime)' != 'false' and '$(UseNativeAOTRuntime)' != 'true'">$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include', 'mono-2.0')) <_AotModuleTablePath>$(AppleBundleDir)\modules.m $(AssemblyName) @@ -324,7 +324,7 @@ InvariantGlobalization="$(InvariantGlobalization)" IsLibraryMode="$(_IsLibraryMode)" MainLibraryFileName="$(MainLibraryFileName)" - MonoRuntimeHeaders="$(_MonoHeaderPath)" + MonoRuntimeHeaders="$(RuntimeHeaders)" NativeMainSource="$(NativeMainSource)" NativeDependencies="@(NativeDependencies)" Optimized="$(Optimized)" diff --git a/src/mono/msbuild/common/LibraryBuilder.targets b/src/mono/msbuild/common/LibraryBuilder.targets index 9ea4a4562ee1e9..bb93a3b8ca54f0 100644 --- a/src/mono/msbuild/common/LibraryBuilder.targets +++ b/src/mono/msbuild/common/LibraryBuilder.targets @@ -33,7 +33,7 @@ ExtraLinkerArguments="@(_ExtraLinkerArgs)" ExtraSources="@(_ExtraLibrarySources)" IsSharedLibrary="$(_IsSharedLibrary)" - MonoRuntimeHeaders="$(_MonoHeaderPath)" + MonoRuntimeHeaders="$(RuntimeHeaders)" Name="$(AssemblyName)" OutputDirectory="$(BundleDir)" RuntimeIdentifier="$(RuntimeIdentifier)" diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.props b/src/mono/msbuild/common/MonoAOTCompiler.props similarity index 82% rename from src/tasks/AotCompilerTask/MonoAOTCompiler.props rename to src/mono/msbuild/common/MonoAOTCompiler.props index 1fc8e8f41adf50..d717c8e2df3a44 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.props +++ b/src/mono/msbuild/common/MonoAOTCompiler.props @@ -4,8 +4,6 @@ - - @@ -15,12 +13,6 @@ - - - - - - @@ -28,7 +20,9 @@ - + + + @@ -36,4 +30,14 @@ + + <_Triple Condition="'$(TargetArchitecture)' == 'arm'">armv7-linux-gnueabi + <_Triple Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android + <_Triple Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android + <_Triple Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android + <_AsOptions>-target $(_Triple) -c -x assembler + <_LdName>clang + <_LdOptions>-fuse-ld=lld + <_AsName>clang + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.LibraryBuilder.Sdk/Microsoft.NET.Runtime.LibraryBuilder.Sdk.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.LibraryBuilder.Sdk/Microsoft.NET.Runtime.LibraryBuilder.Sdk.pkgproj index 8d9a5f20f6e83d..587f7590b31235 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.LibraryBuilder.Sdk/Microsoft.NET.Runtime.LibraryBuilder.Sdk.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Runtime.LibraryBuilder.Sdk/Microsoft.NET.Runtime.LibraryBuilder.Sdk.pkgproj @@ -17,6 +17,7 @@ + diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj index 43e92d34c84818..8917de08365c0e 100644 --- a/src/mono/sample/Android/AndroidSampleApp.csproj +++ b/src/mono/sample/Android/AndroidSampleApp.csproj @@ -2,151 +2,39 @@ Mono Exe - false $(NetCoreAppCurrent) android-$(TargetArchitecture) true true Link false + $(ForceAOT) + True + HelloAndroid + $(AssemblyName).dll + true + + <_UseAotDataFile Condition="'$(RunAOTCompilation)' == 'true'">false - - - - - - - <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile - - - - - False - True - $(PublishDir)apk\ - - - - - @(MonoAOTCompilerDefaultAotArguments, ';') - @(MonoAOTCompilerDefaultProcessArguments, ';') - - - - - - - - - - - - - - - - <_AotOutputType>Library - <_AotLibraryFormat>So - <_AotMode Condition="'$(AotMode)' == ''">Normal - - - - <_AotOutputType>AsmOnly - <_AotModulesTablePath>$(ApkDir)\modules.c - <_AotMode Condition="'$(AotMode)' == ''">Full - - - - 21 - + + - - <_AsPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_AsPrefixPath)')) - <_ToolPrefixPath>$([MSBuild]::EnsureTrailingSlash('$(_ToolPrefixPath)')) - - - - <_Triple Condition="'$(TargetArchitecture)' == 'arm'">armv7-linux-gnueabi - <_Triple Condition="'$(TargetArchitecture)' == 'arm64'">aarch64-linux-android - <_Triple Condition="'$(TargetArchitecture)' == 'x86'">i686-linux-android - <_Triple Condition="'$(TargetArchitecture)' == 'x64'">x86_64-linux-android - - - - <_AsOptions>-target $(_Triple) -c -x assembler - <_LdName>clang - <_LdOptions>-fuse-ld=lld - <_AsName>clang - - - - - - - - $(MicrosoftNetCoreAppRuntimePackDir)runtimes\android-$(TargetArchitecture)\native\include\mono-2.0 - $(CoreClrProjectRoot)hosts\inc - - - - - - - - - - - + + - - - - + AfterTargets="_AfterAndroidBuild" + DependsOnTargets="$(AndroidBuildDependsOn)" > diff --git a/src/mono/sample/Android/Makefile b/src/mono/sample/Android/Makefile index 82539298194b20..f0be463045a7c6 100644 --- a/src/mono/sample/Android/Makefile +++ b/src/mono/sample/Android/Makefile @@ -44,7 +44,7 @@ run: appbuilder /p:ForceAOT=$(AOT) \ /p:AOTWithLibraryFiles=$(AOT_WITH_LIBRARY_FILES) \ /p:MonoForceInterpreter=$(INTERP) \ - /p:UseLLVM=$(USE_LLVM) \ + /p:MonoEnableLLVM=$(USE_LLVM) \ /p:RunActivity=false \ '/p:DiagnosticPorts="$(DIAGNOSTIC_PORTS)"' \ /p:RuntimeFlavor=$(RUNTIME_FLAVOR) \ diff --git a/src/mono/sample/mbr/apple/AppleDelta.csproj b/src/mono/sample/mbr/apple/AppleDelta.csproj index 716fd9b61c64fe..12706ebedeb0fc 100644 --- a/src/mono/sample/mbr/apple/AppleDelta.csproj +++ b/src/mono/sample/mbr/apple/AppleDelta.csproj @@ -27,7 +27,7 @@ - - + diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj index 35e3f5892c3d40..2a5fce2048862e 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.csproj @@ -23,11 +23,6 @@ - - - PreserveNewest - - diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Android.Device_Emulator.LibraryMode_Aot_Llvm.Test.csproj b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Android.Device_Emulator.LibraryMode_Aot_Llvm.Test.csproj new file mode 100644 index 00000000000000..7a80fe0814e636 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Android.Device_Emulator.LibraryMode_Aot_Llvm.Test.csproj @@ -0,0 +1,25 @@ + + + Exe + false + true + true + $(NetCoreAppCurrent) + Android.Device_Emulator.LibraryMode_Aot_Llvm.Test.dll + 42 + true + true + true + shared + true + + + + + + + + + + + diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/ILLink.Descriptors.xml b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/ILLink.Descriptors.xml new file mode 100644 index 00000000000000..96491444910b04 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/ILLink.Descriptors.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Program.cs b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Program.cs new file mode 100644 index 00000000000000..916d1f1aa03b80 --- /dev/null +++ b/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM/Program.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +public static class Program +{ + [UnmanagedCallersOnly(EntryPoint = nameof(SayHello))] + public static void SayHello() + { + Console.WriteLine("Called from native! Hello!"); + } + + public static int Main() + { + Console.WriteLine("Hello, Android!"); // logcat + return 42; + } +} From 87e9f1d94f94f7e9b38da74fd93ea856b0ca6d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Wed, 29 Jan 2025 09:09:35 -0800 Subject: [PATCH 08/31] Add workflow to prevent merging a PR when the `NO-MERGE` label is applied (#111940) * Add workflow to prevent merging a PR when the `NO-MERGE` label is applied. * Fix backtick bug in check-service-labels echo too. --- .github/workflows/check-no-merge-label.yml | 25 ++++++++++++++++++++++ .github/workflows/check-service-labels.yml | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check-no-merge-label.yml diff --git a/.github/workflows/check-no-merge-label.yml b/.github/workflows/check-no-merge-label.yml new file mode 100644 index 00000000000000..1c01c2f7324175 --- /dev/null +++ b/.github/workflows/check-no-merge-label.yml @@ -0,0 +1,25 @@ +name: check-no-merge-label + +permissions: + pull-requests: read + +on: + pull_request: + types: [opened, edited, reopened, labeled, unlabeled, synchronize] + branches: + - 'main' + - 'release/**' + +jobs: + check-labels: + runs-on: ubuntu-latest + steps: + - name: Check 'NO-MERGE' label + run: | + echo "Merging permission is disabled when the 'NO-MERGE' label is applied." + if [ "${{ contains(github.event.pull_request.labels.*.name, 'NO-MERGE') }}" = "false" ]; then + exit 0 + else + echo "::error:: The 'NO-MERGE' label was applied to the PR. Merging is disabled." + exit 1 + fi diff --git a/.github/workflows/check-service-labels.yml b/.github/workflows/check-service-labels.yml index 5261cc165ee128..2d85e4d278a393 100644 --- a/.github/workflows/check-service-labels.yml +++ b/.github/workflows/check-service-labels.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Check 'Servicing-approved' label run: | - echo "Merging permission is enabled for servicing PRs when the `Servicing-approved` label is applied." + echo "Merging permission is enabled for servicing PRs when the 'Servicing-approved' label is applied." if [ "${{ contains(github.event.pull_request.labels.*.name, 'Servicing-approved') }}" = "true" ]; then exit 0 else From 6b47b537be038ecad0e82c4a478cecc0d52bdcdd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 29 Jan 2025 10:41:56 -0800 Subject: [PATCH 09/31] Rewrite how we swap assemblies out as part of NativeAOT to specifically check that they're coming from the runtime pack instead of checking file version numbers and using that as a heuristic for the same thing. (#111944) --- ...mputeManagedAssembliesToCompileToNative.cs | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs index 6d278e4b9597a8..e4c5332acaa21a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs +++ b/src/coreclr/tools/aot/ILCompiler.Build.Tasks/ComputeManagedAssembliesToCompileToNative.cs @@ -120,6 +120,8 @@ public override bool Execute() { // In the case of disk-based assemblies, this holds the file path string itemSpec = taskItem.ItemSpec; + string assemblyFileName = Path.GetFileName(itemSpec); + bool isFromRuntimePack = taskItem.GetMetadata("NuGetPackageId")?.StartsWith("Microsoft.NETCore.App.Runtime.", StringComparison.OrdinalIgnoreCase) == true; // Skip the native apphost (whose name ends up colliding with the native output binary) and supporting libraries if (itemSpec.EndsWith(DotNetAppHostExecutableName, StringComparison.OrdinalIgnoreCase) || itemSpec.Contains(DotNetHostFxrLibraryName) || itemSpec.Contains(DotNetHostPolicyLibraryName)) @@ -128,55 +130,39 @@ public override bool Execute() continue; } - // Prototype aid - remove the native CoreCLR runtime pieces from the publish folder - if (itemSpec.IndexOf("microsoft.netcore.app", StringComparison.OrdinalIgnoreCase) != -1 && (itemSpec.Contains("\\native\\") || itemSpec.Contains("/native/"))) + if (isFromRuntimePack && taskItem.GetMetadata("AssetType")?.Equals("native", StringComparison.OrdinalIgnoreCase) == true) { + // Skip the native components of the runtime pack, we don't need them for NativeAOT. assembliesToSkipPublish.Add(taskItem); continue; } - var assemblyFileName = Path.GetFileName(itemSpec); - - if (assemblyFileName == "WindowsBase.dll") - { - // There are two instances of WindowsBase.dll, one small one, in the NativeAOT framework - // and real one in WindowsDesktop SDK. We want to make sure that if both are present, - // we will use the one from WindowsDesktop SDK, and not from NativeAOT framework. - foreach (ITaskItem taskItemToSkip in FrameworkAssemblies) - { - if (Path.GetFileName(taskItemToSkip.ItemSpec) == assemblyFileName) - { - assembliesToSkipPublish.Add(taskItemToSkip); - break; - } - } - - assembliesToSkipPublish.Add(taskItem); - list.Add(taskItem); - continue; - } - // Remove any assemblies whose implementation we want to come from NativeAOT's package. // Currently that's System.Private.* SDK assemblies and a bunch of framework assemblies. if (nativeAotFrameworkAssembliesToUse.TryGetValue(assemblyFileName, out ITaskItem frameworkItem)) { - if (GetFileVersion(itemSpec).CompareTo(GetFileVersion(frameworkItem.ItemSpec)) > 0) + // If the assembly is part of the Microsoft.NETCore.App.Runtime runtime pack, we want to swap it with the corresponding package from the NativeAOT SDK. + // Otherwise we want to use the assembly the user has referenced. + if (!isFromRuntimePack) { - if (assemblyFileName == "System.Private.CoreLib.dll") - { - Log.LogError($"Overriding System.Private.CoreLib.dll with a newer version is not supported. Attempted to use {itemSpec} instead of {frameworkItem.ItemSpec}."); - } - else - { - // Allow OOB references with higher version to take precedence over the framework assemblies. - list.Add(taskItem); - } + // The assembly was overridden by an OOB package through standard .NET SDK conflict resolution. + // Use that version instead of swapping to the NativeAOT one. + list.Add(taskItem); + } + else if (assemblyFileName == "System.Private.CoreLib.dll" && GetFileVersion(itemSpec).CompareTo(GetFileVersion(frameworkItem.ItemSpec)) > 0) + { + // Validate that we aren't trying to use an older NativeAOT package against a newer non-NativeAOT runtime pack. + // That's not supported. + Log.LogError($"Overriding System.Private.CoreLib.dll with a newer version is not supported. Attempted to use {itemSpec} instead of {frameworkItem.ItemSpec}."); } assembliesToSkipPublish.Add(taskItem); continue; } + // For all other files, check if they are managed assemblies. + // If they're managed, skip publishing them and categorize them correctly as inputs to ILC. + // If they're not managed assemblies, then they're native dependencies. Allow them to be published. try { using (FileStream moduleStream = File.OpenRead(itemSpec)) From d551abb68de9ed473a505a84fd60244ba11bca74 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Wed, 29 Jan 2025 11:05:56 -0800 Subject: [PATCH 10/31] Invoke crossgen exe when building R2RTest (#111881) Given the way crossgen is built in-tree, we no longer have crossgen.dll, but rather the actual crossgen executable. Should fix https://github.com/dotnet/runtime/issues/111878 --- eng/pipelines/common/xplat-setup.yml | 4 ++++ .../coreclr/templates/crossgen2-comparison-build-job.yml | 5 +---- src/tests/build.proj | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 8bedc3fbce652c..fda4c66b4a791f 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -71,6 +71,8 @@ jobs: value: zip - name: tarCompression value: '' + - name: exeExt + value: '.exe' - name: scriptExt value: '.cmd' - name: dir @@ -91,6 +93,8 @@ jobs: value: tar - name: tarCompression value: gz + - name: exeExt + value: '' - name: scriptExt value: '.sh' - name: dir diff --git a/eng/pipelines/coreclr/templates/crossgen2-comparison-build-job.yml b/eng/pipelines/coreclr/templates/crossgen2-comparison-build-job.yml index bf598beec35d35..01f9ed4cfb5309 100644 --- a/eng/pipelines/coreclr/templates/crossgen2-comparison-build-job.yml +++ b/eng/pipelines/coreclr/templates/crossgen2-comparison-build-job.yml @@ -69,10 +69,7 @@ jobs: - name: target_crossgen2_os value: osx - name: crossgen2location - value: $(productDirectory)$(dir)$(targetFlavor)$(dir)crossgen2$(dir)crossgen2.dll - - ${{ if ne(parameters.archType, 'x64') }}: - - name: crossgen2location - value: $(productDirectory)$(dir)$(targetFlavor)$(dir)x64$(dir)crossgen2$(dir)crossgen2.dll + value: $(binDirectory)$(dir)crossgen2_inbuild$(dir)$(archType)$(dir)$(buildConfigUpper)$(dir)crossgen2.dll - name: librariesProductDllDir value: $(Build.SourcesDirectory)$(dir)artifacts$(dir)bin$(dir)runtime$(dir)net10.0-$(osGroup)$(osSubgroup)-$(buildConfig)-$(archType) diff --git a/src/tests/build.proj b/src/tests/build.proj index 74be7ea380ded2..c57cff8db050ba 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -688,8 +688,7 @@ $(CrossgenCmd) --crossgen2-parallelism 1 $(CrossgenCmd) --verify-type-and-field-layout - $(CrossgenCmd) --crossgen2-path "$(__BinDir)\$(BuildArchitecture)\crossgen2\crossgen2" - $(CrossgenCmd) --crossgen2-path "$(__BinDir)\$(BuildArchitecture)\crossgen2\crossgen2.exe" + $(CrossgenCmd) --crossgen2-path "$(__BinDir)\$(BuildArchitecture)\crossgen2\crossgen2$(ExeSuffix)" From 29d286b2c063abb8a426439a5fab517bf02fa582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Wed, 29 Jan 2025 21:02:11 +0100 Subject: [PATCH 11/31] Fix mistakes in net9 WorkloadManifest.json (#111950) * Fix mistakes in net9 and current WorkloadManifest.json I noticed these while working on the workload manifest move to the sdk repo. We were missing the alias-to for the templates and missed the net9 in the package id for microsoft-net-runtime-macos-net9. * Revert the template changes --- .../WorkloadManifest.json.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net9.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net9.Manifest/WorkloadManifest.json.in index 0897896dd5ad9f..ac1d63b87404de 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net9.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.net9.Manifest/WorkloadManifest.json.in @@ -115,8 +115,8 @@ "packs": [ "Microsoft.NETCore.App.Runtime.Mono.net9.osx-arm64", "Microsoft.NETCore.App.Runtime.Mono.net9.osx-x64", - "Microsoft.NETCore.App.Runtime.osx-arm64", - "Microsoft.NETCore.App.Runtime.osx-x64" + "Microsoft.NETCore.App.Runtime.net9.osx-arm64", + "Microsoft.NETCore.App.Runtime.net9.osx-x64" ], "extends": [ "microsoft-net-runtime-mono-tooling-net9" ], "platforms": [ "osx-arm64", "osx-x64" ] From 1b71136cdaf9a6ab3d01149bac31459fa91ea4a6 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 29 Jan 2025 12:56:49 -0800 Subject: [PATCH 12/31] Cleanup membarrier portability (#111943) Fixes #111776 --- src/coreclr/gc/unix/config.gc.h.in | 1 + src/coreclr/gc/unix/configure.cmake | 1 + src/coreclr/gc/unix/gcenv.unix.cpp | 46 ++++++++++---------------- src/coreclr/pal/src/config.h.in | 2 +- src/coreclr/pal/src/configure.cmake | 1 + src/coreclr/pal/src/thread/process.cpp | 40 ++++++++-------------- 6 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/coreclr/gc/unix/config.gc.h.in b/src/coreclr/gc/unix/config.gc.h.in index 01cb767798fbcd..dfc38aea6b8470 100644 --- a/src/coreclr/gc/unix/config.gc.h.in +++ b/src/coreclr/gc/unix/config.gc.h.in @@ -6,6 +6,7 @@ #cmakedefine01 HAVE_SYS_TIME_H #cmakedefine01 HAVE_SYS_MMAN_H +#cmakedefine01 HAVE_SYS_MEMBARRIER_H #cmakedefine01 HAVE_PTHREAD_THREADID_NP #cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP #cmakedefine01 HAVE_VM_FLAGS_SUPERPAGE_SIZE_ANY diff --git a/src/coreclr/gc/unix/configure.cmake b/src/coreclr/gc/unix/configure.cmake index c3b301f58938f0..8d33b81a32f727 100644 --- a/src/coreclr/gc/unix/configure.cmake +++ b/src/coreclr/gc/unix/configure.cmake @@ -11,6 +11,7 @@ include(CheckLibraryExists) check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(sys/mman.h HAVE_SYS_MMAN_H) check_include_files(pthread_np.h HAVE_PTHREAD_NP_H) +check_include_files(sys/membarrier.h HAVE_SYS_MEMBARRIER_H) check_function_exists(vm_allocate HAVE_VM_ALLOCATE) check_function_exists(sysctlbyname HAVE_SYSCTLBYNAME) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index 37ce5943a20ff8..43588c66eb015a 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -29,6 +29,14 @@ #include #endif +#ifdef __linux__ +#include +#include +#define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) +#elif HAVE_SYS_MEMBARRIER_H +#include +#endif + #include #undef min @@ -94,10 +102,6 @@ extern "C" #include #endif // __HAIKU__ -#ifdef __linux__ -#include // __NR_membarrier -#endif - #if HAVE_PTHREAD_NP_H #include #endif @@ -132,29 +136,9 @@ typedef cpuset_t cpu_set_t; // The cached total number of CPUs that can be used in the OS. static uint32_t g_totalCpuCount = 0; -// -// Helper membarrier function -// -#ifdef __NR_membarrier -# define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) -#else -# define membarrier(...) -ENOSYS -#endif - -enum membarrier_cmd -{ - MEMBARRIER_CMD_QUERY = 0, - MEMBARRIER_CMD_GLOBAL = (1 << 0), - MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1), - MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2), - MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3), - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4), - MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 5), - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6) -}; - bool CanFlushUsingMembarrier() { +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H #ifdef TARGET_ANDROID // Avoid calling membarrier on older Android versions where membarrier @@ -169,15 +153,16 @@ bool CanFlushUsingMembarrier() // Starting with Linux kernel 4.14, process memory barriers can be generated // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. - int mask = membarrier(MEMBARRIER_CMD_QUERY, 0); + int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); if (mask >= 0 && mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED && // Register intent to use the private expedited command. - membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0) + membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0) { return true; } +#endif return false; } @@ -423,12 +408,15 @@ bool GCToOSInterface::CanGetCurrentProcessorNumber() // Flush write buffers of processors that are executing threads of the current process void GCToOSInterface::FlushProcessWriteBuffers() { +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H if (s_flushUsingMemBarrier) { - int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0); + int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); assert(status == 0 && "Failed to flush using membarrier"); } - else if (g_helperPage != 0) + else +#endif + if (g_helperPage != 0) { int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex); assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock"); diff --git a/src/coreclr/pal/src/config.h.in b/src/coreclr/pal/src/config.h.in index a3c83373d6d955..a47712a855fe46 100644 --- a/src/coreclr/pal/src/config.h.in +++ b/src/coreclr/pal/src/config.h.in @@ -13,12 +13,12 @@ #cmakedefine01 HAVE_CRT_EXTERNS_H #cmakedefine01 HAVE_SYS_TIME_H #cmakedefine01 HAVE_PTHREAD_NP_H +#cmakedefine01 HAVE_SYS_MEMBARRIER_H #cmakedefine01 HAVE_SYS_LWP_H #cmakedefine01 HAVE_LWP_H #cmakedefine01 HAVE_RUNETYPE_H #cmakedefine01 HAVE_GNU_LIBNAMES_H #cmakedefine01 HAVE_PRCTL_H -#cmakedefine01 HAVE_PTHREAD_NP_H #cmakedefine01 HAVE_AUXV_HWCAP_H #cmakedefine01 HAVE_SYS_PTRACE_H #cmakedefine01 HAVE_SYS_UCONTEXT_H diff --git a/src/coreclr/pal/src/configure.cmake b/src/coreclr/pal/src/configure.cmake index bc4f3258b44b3a..4ba3ab6f53ce0d 100644 --- a/src/coreclr/pal/src/configure.cmake +++ b/src/coreclr/pal/src/configure.cmake @@ -40,6 +40,7 @@ check_include_files(procfs.h HAVE_PROCFS_H) check_include_files(crt_externs.h HAVE_CRT_EXTERNS_H) check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(pthread_np.h HAVE_PTHREAD_NP_H) +check_include_files(sys/membarrier.h HAVE_SYS_MEMBARRIER_H) check_include_files(sys/lwp.h HAVE_SYS_LWP_H) check_include_files(lwp.h HAVE_LWP_H) check_include_files(runetype.h HAVE_RUNETYPE_H) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index b50838750c478f..20c09eb357dd2e 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -64,7 +64,11 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include #ifdef __linux__ -#include // __NR_membarrier +#include +#include +#define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) +#elif HAVE_SYS_MEMBARRIER_H +#include #endif #ifdef __APPLE__ @@ -125,27 +129,6 @@ CObjectType CorUnix::otProcess( CObjectType::NoOwner ); -// -// Helper membarrier function -// -#ifdef __NR_membarrier -# define membarrier(...) syscall(__NR_membarrier, __VA_ARGS__) -#else -# define membarrier(...) -ENOSYS -#endif - -enum membarrier_cmd -{ - MEMBARRIER_CMD_QUERY = 0, - MEMBARRIER_CMD_GLOBAL = (1 << 0), - MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1), - MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2), - MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3), - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4), - MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 5), - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6) -}; - // // Tracks if the OS supports FlushProcessWriteBuffers using membarrier // @@ -2581,19 +2564,21 @@ InitializeFlushProcessWriteBuffers() _ASSERTE(s_helperPage == 0); _ASSERTE(s_flushUsingMemBarrier == 0); +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H // Starting with Linux kernel 4.14, process memory barriers can be generated // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. - int mask = membarrier(MEMBARRIER_CMD_QUERY, 0); + int mask = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); if (mask >= 0 && mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED) { // Register intent to use the private expedited command. - if (membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0) + if (membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0) == 0) { s_flushUsingMemBarrier = TRUE; return TRUE; } } +#endif #ifdef TARGET_APPLE return TRUE; @@ -2649,12 +2634,15 @@ VOID PALAPI FlushProcessWriteBuffers() { +#if defined(__linux__) || HAVE_SYS_MEMBARRIER_H if (s_flushUsingMemBarrier) { - int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0); + int status = membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0); FATAL_ASSERT(status == 0, "Failed to flush using membarrier"); } - else if (s_helperPage != 0) + else +#endif + if (s_helperPage != 0) { int status = pthread_mutex_lock(&flushProcessWriteBuffersMutex); FATAL_ASSERT(status == 0, "Failed to lock the flushProcessWriteBuffersMutex lock"); From be91a3666f36167e698026fb2bb10abcbc807f68 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Wed, 29 Jan 2025 22:19:14 +0000 Subject: [PATCH 13/31] Avoid `Unsafe.As` for `Memory` and `ReadOnlyMemory` conversion (#111023) * Use `Unsafe.BitCast`to cast `Memory` and `ReadOnlyMemory` * remove use of `Unsafe` --- .../System.Private.CoreLib/src/System/Memory.cs | 5 +---- .../System.Private.CoreLib/src/System/ReadOnlyMemory.cs | 9 +++------ .../src/System/Runtime/InteropServices/MemoryMarshal.cs | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Memory.cs b/src/libraries/System.Private.CoreLib/src/System/Memory.cs index 5ec18c4cb842ec..a0da08ba9a8038 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Memory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Memory.cs @@ -20,9 +20,6 @@ namespace System [DebuggerDisplay("{ToString(),raw}")] public readonly struct Memory : IEquatable> { - // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, - // as code uses Unsafe.As to cast between them. - // The highest order bit of _index is used to discern whether _object is a pre-pinned array. // (_index < 0) => _object is a pre-pinned array, so Pin() will not allocate a new GCHandle // (else) => Pin() needs to allocate a new GCHandle to pin the object. @@ -187,7 +184,7 @@ internal Memory(object? obj, int start, int length) /// Defines an implicit conversion of a to a /// public static implicit operator ReadOnlyMemory(Memory memory) => - Unsafe.As, ReadOnlyMemory>(ref memory); + new ReadOnlyMemory(memory._object, memory._index, memory._length); /// /// Returns an empty diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs index ee2059ef0170aa..61bd9314bd26ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs @@ -20,15 +20,12 @@ namespace System [DebuggerDisplay("{ToString(),raw}")] public readonly struct ReadOnlyMemory : IEquatable> { - // NOTE: With the current implementation, Memory and ReadOnlyMemory must have the same layout, - // as code uses Unsafe.As to cast between them. - // The highest order bit of _index is used to discern whether _object is a pre-pinned array. // (_index < 0) => _object is a pre-pinned array, so Pin() will not allocate a new GCHandle // (else) => Pin() needs to allocate a new GCHandle to pin the object. - private readonly object? _object; - private readonly int _index; - private readonly int _length; + internal readonly object? _object; + internal readonly int _index; + internal readonly int _length; internal const int RemoveFlagsBitMask = 0x7FFFFFFF; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs index d849a0d58c75ed..0fc0642c52978b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs @@ -71,7 +71,7 @@ ref Unsafe.As(ref GetReference(span)), /// as but only used for reading to store a . /// public static Memory AsMemory(ReadOnlyMemory memory) => - Unsafe.As, Memory>(ref memory); + new Memory(memory._object, memory._index, memory._length); /// /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element From 7de730ad244e58626840e3617ca41349e5cf3720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:22:10 -0800 Subject: [PATCH 14/31] Remove zlib dependencies from Docker containers (#111939) --- .devcontainer/Dockerfile | 1 - .devcontainer/android/Dockerfile | 2 -- .devcontainer/wasm-multiThreaded/Dockerfile | 1 - .devcontainer/wasm/Dockerfile | 1 - 4 files changed, 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b0b3a1020e618f..987034cae61e02 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -24,6 +24,5 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ liblttng-ust-dev \ libssl-dev \ libkrb5-dev \ - zlib1g-dev \ ninja-build \ tzdata diff --git a/.devcontainer/android/Dockerfile b/.devcontainer/android/Dockerfile index 092e291fc6290d..bdbc7d68f258cb 100644 --- a/.devcontainer/android/Dockerfile +++ b/.devcontainer/android/Dockerfile @@ -21,9 +21,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ liblttng-ust-dev \ libssl-dev \ libkrb5-dev \ - zlib1g-dev \ ninja-build \ - zlib1g-dev \ ninja-build \ openjdk-17-jdk \ pulseaudio diff --git a/.devcontainer/wasm-multiThreaded/Dockerfile b/.devcontainer/wasm-multiThreaded/Dockerfile index 70fc2380fdd098..ed0ee4f35f5d73 100644 --- a/.devcontainer/wasm-multiThreaded/Dockerfile +++ b/.devcontainer/wasm-multiThreaded/Dockerfile @@ -24,7 +24,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ liblttng-ust-dev \ libssl-dev \ libkrb5-dev \ - zlib1g-dev \ ninja-build SHELL ["/bin/bash", "-c"] diff --git a/.devcontainer/wasm/Dockerfile b/.devcontainer/wasm/Dockerfile index 63335387f3cd1e..3950e19fcf1de4 100644 --- a/.devcontainer/wasm/Dockerfile +++ b/.devcontainer/wasm/Dockerfile @@ -23,7 +23,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ liblttng-ust-dev \ libssl-dev \ libkrb5-dev \ - zlib1g-dev \ ninja-build SHELL ["/bin/bash", "-c"] From 3688b59a4c8efebf6d13307d555be0f3ef063b40 Mon Sep 17 00:00:00 2001 From: Linus Hamlin <78953007+lilinus@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:28:45 +0100 Subject: [PATCH 15/31] Use IRootFunctions in Tensor.StdDev (#110641) * Use IRootFunctions in Tensor.StdDev * Try fix api compat issue * Replace Pow+Sum with SumOfSquares * Drop IPowerFunctions constraint * Try fix compatability supression * Fix StdDev stride issue * Add regression test * fix test * Use FlattenedLength in StdDev * Try add byref to api compat supressions * Try fix type constraint --------- Co-authored-by: Tanner Gooding --- .../ref/System.Numerics.Tensors.netcore.cs | 2 +- .../src/CompatibilitySuppressions.xml | 14 +++++++ .../Tensors/netcore/TensorExtensions.cs | 38 ++++++++++--------- .../tests/TensorTests.cs | 9 +++++ 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs index d3c31753c42ba8..9475c3390558f5 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.netcore.cs @@ -487,7 +487,7 @@ public static void ResizeTo(scoped in System.Numerics.Tensors.Tensor tenso public static ref readonly System.Numerics.Tensors.TensorSpan StackAlongDimension(scoped System.ReadOnlySpan> tensors, in System.Numerics.Tensors.TensorSpan destination, int dimension) { throw null; } public static System.Numerics.Tensors.Tensor Stack(params scoped System.ReadOnlySpan> tensors) { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Stack(scoped in System.ReadOnlySpan> tensors, in System.Numerics.Tensors.TensorSpan destination) { throw null; } - public static T StdDev(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IFloatingPoint, System.Numerics.IPowerFunctions, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity { throw null; } + public static T StdDev(in System.Numerics.Tensors.ReadOnlyTensorSpan x) where T : System.Numerics.IFloatingPoint, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IRootFunctions { throw null; } public static System.Numerics.Tensors.Tensor Subtract(in System.Numerics.Tensors.ReadOnlyTensorSpan x, in System.Numerics.Tensors.ReadOnlyTensorSpan y) where T : System.Numerics.ISubtractionOperators { throw null; } public static ref readonly System.Numerics.Tensors.TensorSpan Subtract(scoped in System.Numerics.Tensors.ReadOnlyTensorSpan x, scoped in System.Numerics.Tensors.ReadOnlyTensorSpan y, in System.Numerics.Tensors.TensorSpan destination) where T : System.Numerics.ISubtractionOperators { throw null; } public static System.Numerics.Tensors.Tensor Subtract(in System.Numerics.Tensors.ReadOnlyTensorSpan x, T y) where T : System.Numerics.ISubtractionOperators { throw null; } diff --git a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml index ae2ea3d401a7b1..d05601550864ea 100644 --- a/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Numerics.Tensors/src/CompatibilitySuppressions.xml @@ -253,4 +253,18 @@ lib/net9.0/System.Numerics.Tensors.dll true + + CP0021 + M:System.Numerics.Tensors.Tensor.StdDev``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@)``0:T:System.Numerics.IRootFunctions{``0} + lib/net8.0/System.Numerics.Tensors.dll + lib/net8.0/System.Numerics.Tensors.dll + true + + + CP0021 + M:System.Numerics.Tensors.Tensor.StdDev``1(System.Numerics.Tensors.ReadOnlyTensorSpan{``0}@)``0:T:System.Numerics.IRootFunctions{``0} + lib/net9.0/System.Numerics.Tensors.dll + lib/net9.0/System.Numerics.Tensors.dll + true + \ No newline at end of file diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs index 5ba164337bfc91..84c933226fb2a1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorExtensions.cs @@ -3512,26 +3512,15 @@ public static ref readonly TensorSpan StackAlongDimension(scoped ReadOnlyS /// The to take the standard deviation of. /// representing the standard deviation. public static T StdDev(in ReadOnlyTensorSpan x) - where T : IFloatingPoint, IPowerFunctions, IAdditionOperators, IAdditiveIdentity + where T : IFloatingPoint, IAdditionOperators, IAdditiveIdentity, IRootFunctions { T mean = Average(x); - Span span = MemoryMarshal.CreateSpan(ref x._reference, (int)x._shape._memoryLength); - Span output = new T[x.FlattenedLength]; - TensorPrimitives.Subtract(span, mean, output); - TensorPrimitives.Abs(output, output); - TensorPrimitives.Pow((ReadOnlySpan)output, T.CreateChecked(2), output); - T sum = TensorPrimitives.Sum((ReadOnlySpan)output); - T variance = sum / T.CreateChecked(x._shape._memoryLength); - - if (typeof(T) == typeof(float)) - { - return T.CreateChecked(MathF.Sqrt(float.CreateChecked(variance))); - } - if (typeof(T) == typeof(double)) - { - return T.CreateChecked(Math.Sqrt(double.CreateChecked(variance))); - } - return T.Pow(variance, T.CreateChecked(0.5)); + Tensor temp = CreateUninitialized(x.Lengths); + Subtract(x, mean, temp); + Abs(temp, temp); + T sum = SumOfSquares(temp); + T variance = sum / T.CreateChecked(x.FlattenedLength); + return T.Sqrt(variance); } #endregion @@ -6664,6 +6653,19 @@ public static T Sum(scoped in ReadOnlyTensorSpan x) } #endregion + #region SumOfSquares + /// + /// Sums the squared elements of the specified tensor. + /// + /// Tensor to sum squares of + /// + internal static T SumOfSquares(scoped in ReadOnlyTensorSpan x) + where T : IAdditionOperators, IAdditiveIdentity, IMultiplyOperators + { + return TensorPrimitivesHelperSpanInTOut(x, TensorPrimitives.SumOfSquares); + } + #endregion + #region Tan /// Computes the element-wise tangent of the value in the specified tensor. /// The to take the sin of. diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 165779fe329a61..607d09788db1ce 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -1124,6 +1124,15 @@ public static void TensorStdDevTests() Tensor t0 = Tensor.Create((Enumerable.Range(0, 4).Select(i => (float)i)), [2, 2]); Assert.Equal(StdDev([0, 1, 2, 3]), Tensor.StdDev(t0), .1); + + // Test that non-contiguous calculations work + Tensor fourByFour = Tensor.Create([4, 4]); + fourByFour[[0, 0]] = 1f; + fourByFour[[0, 1]] = 1f; + fourByFour[[1, 0]] = 1f; + fourByFour[[1, 1]] = 1f; + ReadOnlyTensorSpan upperLeft = fourByFour.AsReadOnlyTensorSpan().Slice([0..2, 0..2]); + Assert.Equal(0f, Tensor.StdDev(upperLeft)); } public static float StdDev(float[] values) From f552567173b706812d11579f8059a14a748dab81 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 29 Jan 2025 14:52:43 -0800 Subject: [PATCH 16/31] JIT: fix try region cloning when try is nested in a handler (#111975) We were not placing the new EH region in the table properly, and not correctly updating the handler indexes of cloned blocks. Add some test cases where we have cloneable loops with EH inside of finallies. Fixes some issues that came up testing #111473 --- src/coreclr/jit/fgehopt.cpp | 26 ++- src/tests/JIT/opt/Cloning/loops_with_eh.cs | 204 ++++++++++++++++++++- 2 files changed, 215 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/fgehopt.cpp b/src/coreclr/jit/fgehopt.cpp index d9710994cf152b..940077dc6d2d62 100644 --- a/src/coreclr/jit/fgehopt.cpp +++ b/src/coreclr/jit/fgehopt.cpp @@ -2761,14 +2761,20 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info, // this is cheaper than any other insertion point, as no existing regions get renumbered. // unsigned insertBeforeIndex = enclosingTryIndex; - if (insertBeforeIndex == EHblkDsc::NO_ENCLOSING_INDEX) + if ((enclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) && (enclosingHndIndex == EHblkDsc::NO_ENCLOSING_INDEX)) { - JITDUMP("Cloned EH clauses will go at the end of the EH table\n"); + JITDUMP("No enclosing EH region; cloned EH clauses will go at the end of the EH table\n"); insertBeforeIndex = compHndBBtabCount; } + else if ((enclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) || (enclosingHndIndex < enclosingTryIndex)) + { + JITDUMP("Cloned EH clauses will go before enclosing handler region EH#%02u\n", enclosingHndIndex); + insertBeforeIndex = enclosingHndIndex; + } else { - JITDUMP("Cloned EH clauses will go before enclosing region EH#%02u\n", enclosingTryIndex); + JITDUMP("Cloned EH clauses will go before enclosing try region EH#%02u\n", enclosingTryIndex); + assert(insertBeforeIndex == enclosingTryIndex); } // Once we call fgTryAddEHTableEntries with deferCloning = false, @@ -2989,7 +2995,7 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info, const unsigned originalTryIndex = block->getTryIndex(); unsigned cloneTryIndex = originalTryIndex; - if (originalTryIndex <= outermostTryIndex) + if (originalTryIndex < enclosingTryIndex) { cloneTryIndex += indexShift; } @@ -3003,11 +3009,15 @@ BasicBlock* Compiler::fgCloneTryRegion(BasicBlock* tryEntry, CloneTryInfo& info, if (block->hasHndIndex()) { const unsigned originalHndIndex = block->getHndIndex(); + unsigned cloneHndIndex = originalHndIndex; + + if (originalHndIndex < enclosingHndIndex) + { + cloneHndIndex += indexShift; + } - // if (originalHndIndex == - const unsigned cloneHndIndex = originalHndIndex + indexShift; - EHblkDsc* const originalEbd = ehGetDsc(originalHndIndex); - EHblkDsc* const clonedEbd = ehGetDsc(cloneHndIndex); + EHblkDsc* const originalEbd = ehGetDsc(originalHndIndex); + EHblkDsc* const clonedEbd = ehGetDsc(cloneHndIndex); newBlock->setHndIndex(cloneHndIndex); updateBlockReferences(cloneHndIndex); diff --git a/src/tests/JIT/opt/Cloning/loops_with_eh.cs b/src/tests/JIT/opt/Cloning/loops_with_eh.cs index ca0328e78bdaf4..13b161f6558440 100644 --- a/src/tests/JIT/opt/Cloning/loops_with_eh.cs +++ b/src/tests/JIT/opt/Cloning/loops_with_eh.cs @@ -5,7 +5,6 @@ using System.Runtime.CompilerServices; using Xunit; - // Cheat codes // // L - loop @@ -17,6 +16,7 @@ // m - multiple try exits (TF will remain a try finally) // g - giant finally (TF will remain try finally) // p - regions are serial, not nested +// TFi - try finally with what follows in the finally // // x: we currently cannot clone loops where the try is the first thing // as the header and preheader are different regions @@ -26,6 +26,9 @@ public class LoopsWithEH static int[] data; static int n; + [MethodImpl(MethodImplOptions.NoInlining)] + static void SideEffect() { } + static LoopsWithEH() { data = new int[100]; @@ -178,7 +181,7 @@ public static int Sum_LxTCC(int[] data, int n) { return -1; } - catch(Exception) + catch (Exception) { return -2; } @@ -201,7 +204,7 @@ public static int Sum_LxTCcC(int[] data, int n) } catch (IndexOutOfRangeException) { - sum +=1; + sum += 1; } catch (Exception) { @@ -536,7 +539,7 @@ public static int Sum_TCLxTfC(int[] data, int n) } } } - catch (Exception) + catch (Exception) { return -1; } @@ -642,7 +645,7 @@ public static int Sum_TCLxTF(int[] data, int n) { sum += data[i]; } - finally + finally { sum += 1; } @@ -807,7 +810,7 @@ public static int Sum_LxTFTF(int[] data, int n) [Fact] public static int Test_LxTFxTF() => Sum_LxTFTF(data, n) - 110; - + public static int Sum_TFLxTF(int[] data, int n) { int sum = 0; @@ -896,7 +899,7 @@ public static int Sum_TCTFLxTF(int[] data, int n) sum += 1; } } - catch(Exception) + catch (Exception) { return -1; } @@ -937,5 +940,192 @@ public static int Sum_TFTCLxTF(int[] data, int n) } return sum; } + + [Fact] + public static int Test_TFiL() => Sum_TFiL(data, n) - 91; + + public static int Sum_TFiL(int[] data, int n) + { + int sum = 0; + try + { + SideEffect(); + } + finally + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += data[i]; + } + } + + return sum; + } + + [Fact] + public static int Test_TFiLxTF() => Sum_TFiLxTF(data, n) - 131; + + public static int Sum_TFiLxTF(int[] data, int n) + { + int sum = 0; + try + { + SideEffect(); + } + finally + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += 1; + try + { + sum += data[i]; + } + finally + { + sum += 1; + } + } + } + + return sum; + } + + [Fact] + public static int Test_TFiLxTCc() => Sum_TFiLxTCc(data, n) - 111; + + public static int Sum_TFiLxTCc(int[] data, int n) + { + int sum = 0; + try + { + SideEffect(); + } + finally + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += 1; + try + { + sum += data[i]; + } + catch (Exception) + { + sum += 1; + } + } + } + + return sum; + } + + [Fact] + public static int Test_TFiLxTC() => Sum_TFiLxTC(data, n) - 112; + + public static int Sum_TFiLxTC(int[] data, int n) + { + int sum = 0; + try + { + SideEffect(); + } + finally + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += 1; + try + { + sum += data[i]; + } + catch (Exception) + { + goto after_loop; + } + } + + after_loop: + sum += 1; + + } + + return sum; + } + + [Fact] + public static int Test_TFTFiLxTC() => Sum_TFTFiLxTC(data, n) - 113; + + public static int Sum_TFTFiLxTC(int[] data, int n) + { + int sum = 0; + try + { + try + { + SideEffect(); + } + finally + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += 1; + try + { + sum += data[i]; + } + catch (Exception) + { + goto after_loop; + } + } + + after_loop: + sum += 1; + } + } + finally + { + sum += 1; + } + + return sum; + } + + + [Fact] + public static int Test_TFiTFxL() => Sum_TFiTFxL(data, n) - 92; + + public static int Sum_TFiTFxL(int[] data, int n) + { + int sum = 0; + + try + { + SideEffect(); + } + finally + { + try + { + sum += 1; + for (int i = 0; i < n; i++) + { + sum += data[i]; + } + } + finally + { + sum += 1; + } + } + + return sum; + } } From f3d90b2f622691253225ddcd61b8ae8a087955ae Mon Sep 17 00:00:00 2001 From: Hamish Arblaster Date: Thu, 30 Jan 2025 11:05:17 +1100 Subject: [PATCH 17/31] Add remark to `ConditionalSelect` (#111945) * Update Vector files * Update Vector.cs * Address Feedback --- .../System.Private.CoreLib/src/System/Numerics/Vector.cs | 3 +++ .../src/System/Runtime/Intrinsics/ISimdVector_2.cs | 1 + .../src/System/Runtime/Intrinsics/Vector128.cs | 1 + .../src/System/Runtime/Intrinsics/Vector256.cs | 1 + .../src/System/Runtime/Intrinsics/Vector512.cs | 1 + .../src/System/Runtime/Intrinsics/Vector64.cs | 1 + 6 files changed, 8 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs index 57a71792bc5f0f..744efd825eecce 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector.cs @@ -349,6 +349,7 @@ public static Vector ClampNative(Vector value, Vector min, Vector /// The vector that is selected when the corresponding bit in is zero. /// The type of the elements in the vector. /// A vector whose bits come from or based on the value of . + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) => (left & condition) | AndNot(right, condition); @@ -358,6 +359,7 @@ public static Vector ClampNative(Vector value, Vector min, Vector /// The vector that is selected when the corresponding bit in is one. /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) => ConditionalSelect(condition.As(), left, right); @@ -366,6 +368,7 @@ public static Vector ClampNative(Vector value, Vector min, Vector /// The vector that is selected when the corresponding bit in is one. /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] public static Vector ConditionalSelect(Vector condition, Vector left, Vector right) => ConditionalSelect(condition.As(), left, right); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/ISimdVector_2.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/ISimdVector_2.cs index bec27899e9f6b5..b00c7701adaba4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/ISimdVector_2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/ISimdVector_2.cs @@ -166,6 +166,7 @@ internal unsafe interface ISimdVector /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . /// The type of the elements in the vector () is not supported. + /// The returned vector is equivalent to ? : on a per-bit basis. static virtual TSelf ConditionalSelect(TSelf condition, TSelf left, TSelf right) => (left & condition) | (right & ~condition); /// Copies the per-element sign of a vector to the per-element sign of another vector. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 6831123b931848..11c0a7e113dda0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -346,6 +346,7 @@ public static Vector128 ClampNative(Vector128 value, Vector128 min, /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . /// The type of , , and () is not supported. + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 ConditionalSelect(Vector128 condition, Vector128 left, Vector128 right) => (left & condition) | AndNot(right, condition); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs index 36093c16b86d94..56cb30e7bf4bea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256.cs @@ -384,6 +384,7 @@ public static Vector256 ClampNative(Vector256 value, Vector256 min, /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . /// The type of , , and () is not supported. + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 ConditionalSelect(Vector256 condition, Vector256 left, Vector256 right) => (left & condition) | AndNot(right, condition); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs index 6877dae70af508..2cdfba21cd61b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs @@ -384,6 +384,7 @@ public static Vector512 ClampNative(Vector512 value, Vector512 min, /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . /// The type of , , and () is not supported. + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 ConditionalSelect(Vector512 condition, Vector512 left, Vector512 right) => (left & condition) | AndNot(right, condition); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs index 4ddbe5eb4831b6..d7d17e8599933c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64.cs @@ -352,6 +352,7 @@ public static Vector64 ClampNative(Vector64 value, Vector64 min, Vec /// The vector that is selected when the corresponding bit in is zero. /// A vector whose bits come from or based on the value of . /// The type of , , and () is not supported. + /// The returned vector is equivalent to ? : on a per-bit basis. [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector64 ConditionalSelect(Vector64 condition, Vector64 left, Vector64 right) => (left & condition) | AndNot(right, condition); From 7693b6d0202281dc38dcfb9562e9064fcd13bc61 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 30 Jan 2025 02:17:42 +0100 Subject: [PATCH 18/31] Fix failed assertion 'FPbased == FPbased2' (#111787) * Move assert to prevent incorrect trigger when lvaFrameAddress optimization merged SP+n and FP-m stores into a single pair-wise store * Better fix --- src/coreclr/jit/emitarm64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 3fd34318d9ec11..10d344b22c02fa 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -12311,7 +12311,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) // If there are 2 GC vars in this instrDesc, get the 2nd variable // that should be tracked. - adr2 = emitComp->lvaFrameAddress(varNum2, &FPbased2, true); + adr2 = emitComp->lvaFrameAddress(varNum2, &FPbased2, FPbased); ofs2Dist = EA_SIZE_IN_BYTES(size); #ifdef DEBUG assert(FPbased == FPbased2); From adf123c23ee42262515c36562b09fa0b19959599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 30 Jan 2025 09:36:04 +0100 Subject: [PATCH 19/31] Replace OPTIMIZE_FOR_SIZE with feature switch (#111743) --- .../TestUtilities/System/PlatformDetection.cs | 13 +++- .../tests/ZipArchive/zip_LargeFiles.cs | 4 +- .../tests/LargeFilesTests.Net.cs | 2 +- .../System.Linq/src/System.Linq.csproj | 59 ++++++++----------- .../System.Linq/src/System/Linq/AnyAll.cs | 4 +- .../System.Linq/src/System/Linq/Count.cs | 8 +-- .../System.Linq/src/System/Linq/ElementAt.cs | 8 +-- .../System.Linq/src/System/Linq/Enumerable.cs | 4 ++ .../System.Linq/src/System/Linq/First.cs | 4 +- .../System.Linq/src/System/Linq/Iterator.cs | 8 +-- .../System.Linq/src/System/Linq/Last.cs | 4 +- .../src/System/Linq/Skip.SizeOpt.cs | 2 +- .../src/System/Linq/Skip.SpeedOpt.cs | 2 +- .../System.Linq/src/System/Linq/Skip.cs | 6 +- .../src/System/Linq/Take.SizeOpt.cs | 4 +- .../src/System/Linq/Take.SpeedOpt.cs | 4 +- .../System.Linq/src/System/Linq/Take.cs | 23 +++++--- .../src/System/Linq/ToCollection.cs | 8 +-- .../System.Linq/tests/ConcatTests.cs | 2 +- src/libraries/System.Linq/tests/CountTests.cs | 11 ++-- .../System.Linq/tests/OrderedSubsetting.cs | 2 +- src/libraries/System.Linq/tests/RangeTests.cs | 6 +- .../System.Linq/tests/RepeatTests.cs | 2 +- .../System.Linq/tests/SelectManyTests.cs | 2 +- src/libraries/System.Linq/tests/TakeTests.cs | 4 +- .../WasmFeatures.props | 1 + 26 files changed, 92 insertions(+), 105 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 68d878fa1bacfd..fb6d6ddb0e990e 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -142,8 +142,17 @@ public static int SlowRuntimeTimeoutModifier public static bool IsStartingProcessesSupported => !IsiOS && !IstvOS; - public static bool IsSpeedOptimized => !IsSizeOptimized; - public static bool IsSizeOptimized => IsBrowser || IsWasi || IsAndroid || IsAppleMobile; + public static bool IsLinqSpeedOptimized => !IsLinqSizeOptimized; + public static bool IsLinqSizeOptimized => s_linqIsSizeOptimized.Value; + private static readonly Lazy s_linqIsSizeOptimized = new Lazy(ComputeIsLinqSizeOptimized); + private static bool ComputeIsLinqSizeOptimized() + { +#if NET + return (bool)typeof(Enumerable).GetMethod("get_IsSizeOptimized", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, Array.Empty()); +#else + return false; +#endif + } public static bool IsBrowserDomSupported => IsEnvironmentVariableTrue("IsBrowserDomSupported"); public static bool IsBrowserDomSupportedOrNotBrowser => IsNotBrowser || IsBrowserDomSupported; diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs index b4623e653af84d..dbb91fc5523de0 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_LargeFiles.cs @@ -9,7 +9,7 @@ namespace System.IO.Compression.Tests; [Collection(nameof(DisableParallelization))] public class zip_LargeFiles : ZipFileTestBase { - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes [OuterLoop("It requires almost 12 GB of free disk space")] public static void UnzipOver4GBZipFile() { @@ -49,7 +49,7 @@ private static void FillWithHardToCompressData(byte[] buffer) Random.Shared.NextBytes(buffer); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes [OuterLoop("It requires 5~6 GB of free disk space and a lot of CPU time for compressed tests")] [InlineData(false)] [InlineData(true)] diff --git a/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs index c073c09b8f92f7..ad93cf44172ee5 100644 --- a/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs +++ b/src/libraries/System.IO.Packaging/tests/LargeFilesTests.Net.cs @@ -15,7 +15,7 @@ private static void FillWithHardToCompressData(byte[] buffer) Random.Shared.NextBytes(buffer); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMobile), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes [InlineData(false)] [InlineData(true)] [OuterLoop("It requires 5~6 GB of free disk space and a lot of CPU time for compressed tests")] diff --git a/src/libraries/System.Linq/src/System.Linq.csproj b/src/libraries/System.Linq/src/System.Linq.csproj index 6bea440207f3a2..cbbec2028cf8d7 100644 --- a/src/libraries/System.Linq/src/System.Linq.csproj +++ b/src/libraries/System.Linq/src/System.Linq.csproj @@ -1,98 +1,85 @@ - $(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-wasi;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos + $(NetCoreAppCurrent) false - - - $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) - true - $(DefineConstants);OPTIMIZE_FOR_SIZE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Linq/src/System/Linq/AnyAll.cs b/src/libraries/System.Linq/src/System/Linq/AnyAll.cs index b236c6a6f1ac64..2e10e25db1c5a3 100644 --- a/src/libraries/System.Linq/src/System/Linq/AnyAll.cs +++ b/src/libraries/System.Linq/src/System/Linq/AnyAll.cs @@ -20,8 +20,7 @@ public static bool Any(this IEnumerable source) return gc.Count != 0; } -#if !OPTIMIZE_FOR_SIZE - if (source is Iterator iterator) + if (!IsSizeOptimized && source is Iterator iterator) { int count = iterator.GetCount(onlyIfCheap: true); if (count >= 0) @@ -32,7 +31,6 @@ public static bool Any(this IEnumerable source) iterator.TryGetFirst(out bool found); return found; } -#endif if (source is ICollection ngc) { diff --git a/src/libraries/System.Linq/src/System/Linq/Count.cs b/src/libraries/System.Linq/src/System/Linq/Count.cs index 85bb624800d9d3..6a12a11cbe163d 100644 --- a/src/libraries/System.Linq/src/System/Linq/Count.cs +++ b/src/libraries/System.Linq/src/System/Linq/Count.cs @@ -20,12 +20,10 @@ public static int Count(this IEnumerable source) return collectionoft.Count; } -#if !OPTIMIZE_FOR_SIZE - if (source is Iterator iterator) + if (!IsSizeOptimized && source is Iterator iterator) { return iterator.GetCount(onlyIfCheap: false); } -#endif if (source is ICollection collection) { @@ -115,8 +113,7 @@ public static bool TryGetNonEnumeratedCount(this IEnumerable s return true; } -#if !OPTIMIZE_FOR_SIZE - if (source is Iterator iterator) + if (!IsSizeOptimized && source is Iterator iterator) { int c = iterator.GetCount(onlyIfCheap: true); if (c >= 0) @@ -125,7 +122,6 @@ public static bool TryGetNonEnumeratedCount(this IEnumerable s return true; } } -#endif if (source is ICollection collection) { diff --git a/src/libraries/System.Linq/src/System/Linq/ElementAt.cs b/src/libraries/System.Linq/src/System/Linq/ElementAt.cs index 97b87f9eba9999..26c69366fa9f3b 100644 --- a/src/libraries/System.Linq/src/System/Linq/ElementAt.cs +++ b/src/libraries/System.Linq/src/System/Linq/ElementAt.cs @@ -23,9 +23,7 @@ public static TSource ElementAt(this IEnumerable source, int i bool found; TSource? element = -#if !OPTIMIZE_FOR_SIZE - source is Iterator iterator ? iterator.TryGetElementAt(index, out found) : -#endif + !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetElementAt(index, out found) : TryGetElementAtNonIterator(source, index, out found); if (!found) @@ -123,9 +121,7 @@ public static TSource ElementAt(this IEnumerable source, Index } return -#if !OPTIMIZE_FOR_SIZE - source is Iterator iterator ? iterator.TryGetElementAt(index, out found) : -#endif + !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetElementAt(index, out found) : TryGetElementAtNonIterator(source, index, out found); } diff --git a/src/libraries/System.Linq/src/System/Linq/Enumerable.cs b/src/libraries/System.Linq/src/System/Linq/Enumerable.cs index fbdf9a12a28b65..8e5571ddda534e 100644 --- a/src/libraries/System.Linq/src/System/Linq/Enumerable.cs +++ b/src/libraries/System.Linq/src/System/Linq/Enumerable.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -9,6 +10,9 @@ namespace System.Linq { public static partial class Enumerable { + [FeatureSwitchDefinition("System.Linq.Enumerable.IsSizeOptimized")] + internal static bool IsSizeOptimized { get; } = AppContext.TryGetSwitch("System.Linq.Enumerable.IsSizeOptimized", out bool isEnabled) ? isEnabled : false; + public static IEnumerable AsEnumerable(this IEnumerable source) => source; /// Returns an empty . diff --git a/src/libraries/System.Linq/src/System/Linq/First.cs b/src/libraries/System.Linq/src/System/Linq/First.cs index fc97996d480dd7..814943c94438d8 100644 --- a/src/libraries/System.Linq/src/System/Linq/First.cs +++ b/src/libraries/System.Linq/src/System/Linq/First.cs @@ -70,9 +70,7 @@ public static TSource FirstOrDefault(this IEnumerable source, } return -#if !OPTIMIZE_FOR_SIZE - source is Iterator iterator ? iterator.TryGetFirst(out found) : -#endif + !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetFirst(out found) : TryGetFirstNonIterator(source, out found); } diff --git a/src/libraries/System.Linq/src/System/Linq/Iterator.cs b/src/libraries/System.Linq/src/System/Linq/Iterator.cs index 8d5982eb0b9b49..00da01cb25cb80 100644 --- a/src/libraries/System.Linq/src/System/Linq/Iterator.cs +++ b/src/libraries/System.Linq/src/System/Linq/Iterator.cs @@ -88,11 +88,9 @@ public Iterator GetEnumerator() /// The type of the mapped items. /// The selector used to map each item. public virtual IEnumerable Select(Func selector) => -#if OPTIMIZE_FOR_SIZE - new IEnumerableSelectIterator(this, selector); -#else - new IteratorSelectIterator(this, selector); -#endif + !IsSizeOptimized + ? new IteratorSelectIterator(this, selector) + : new IEnumerableSelectIterator(this, selector); /// diff --git a/src/libraries/System.Linq/src/System/Linq/Last.cs b/src/libraries/System.Linq/src/System/Linq/Last.cs index 9de082deffa7a5..ca48475259d8e5 100644 --- a/src/libraries/System.Linq/src/System/Linq/Last.cs +++ b/src/libraries/System.Linq/src/System/Linq/Last.cs @@ -69,9 +69,7 @@ public static TSource LastOrDefault(this IEnumerable source, F } return -#if !OPTIMIZE_FOR_SIZE - source is Iterator iterator ? iterator.TryGetLast(out found) : -#endif + !IsSizeOptimized && source is Iterator iterator ? iterator.TryGetLast(out found) : TryGetLastNonIterator(source, out found); } diff --git a/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs index 3704526903ece1..13e6642ee1fc02 100644 --- a/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs @@ -7,7 +7,7 @@ namespace System.Linq { public static partial class Enumerable { - private static IEnumerable SkipIterator(IEnumerable source, int count) + private static IEnumerable SizeOptimizedSkipIterator(IEnumerable source, int count) { using IEnumerator e = source.GetEnumerator(); while (count > 0 && e.MoveNext()) count--; diff --git a/src/libraries/System.Linq/src/System/Linq/Skip.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Skip.SpeedOpt.cs index 74ff73a068242c..4de9afbbbfb47b 100644 --- a/src/libraries/System.Linq/src/System/Linq/Skip.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Skip.SpeedOpt.cs @@ -7,7 +7,7 @@ namespace System.Linq { public static partial class Enumerable { - private static IEnumerable SkipIterator(IEnumerable source, int count) => + private static IEnumerable SpeedOptimizedSkipIterator(IEnumerable source, int count) => source is IList sourceList ? (IEnumerable)new IListSkipTakeIterator(sourceList, count, int.MaxValue) : new IEnumerableSkipTakeIterator(source, count, -1); diff --git a/src/libraries/System.Linq/src/System/Linq/Skip.cs b/src/libraries/System.Linq/src/System/Linq/Skip.cs index 6b565670fe6903..ac8252a07c6d0c 100644 --- a/src/libraries/System.Linq/src/System/Linq/Skip.cs +++ b/src/libraries/System.Linq/src/System/Linq/Skip.cs @@ -30,14 +30,12 @@ public static IEnumerable Skip(this IEnumerable sourc count = 0; } -#if !OPTIMIZE_FOR_SIZE - else if (source is Iterator iterator) + else if (!IsSizeOptimized && source is Iterator iterator) { return iterator.Skip(count) ?? Empty(); } -#endif - return SkipIterator(source, count); + return IsSizeOptimized ? SizeOptimizedSkipIterator(source, count) : SpeedOptimizedSkipIterator(source, count); } public static IEnumerable SkipWhile(this IEnumerable source, Func predicate) diff --git a/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs index f61b01ee57773a..6f2bd0d9b0fa6b 100644 --- a/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs @@ -8,7 +8,7 @@ namespace System.Linq { public static partial class Enumerable { - private static IEnumerable TakeIterator(IEnumerable source, int count) + private static IEnumerable SizeOptimizedTakeIterator(IEnumerable source, int count) { Debug.Assert(count > 0); @@ -19,7 +19,7 @@ private static IEnumerable TakeIterator(IEnumerable s } } - private static IEnumerable TakeRangeIterator(IEnumerable source, int startIndex, int endIndex) + private static IEnumerable SizeOptimizedTakeRangeIterator(IEnumerable source, int startIndex, int endIndex) { Debug.Assert(source is not null); Debug.Assert(startIndex >= 0 && startIndex < endIndex); diff --git a/src/libraries/System.Linq/src/System/Linq/Take.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Take.SpeedOpt.cs index 81025ee21d0b4b..b5ec96ab3a0f12 100644 --- a/src/libraries/System.Linq/src/System/Linq/Take.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Take.SpeedOpt.cs @@ -8,7 +8,7 @@ namespace System.Linq { public static partial class Enumerable { - private static IEnumerable TakeIterator(IEnumerable source, int count) + private static IEnumerable SpeedOptimizedTakeIterator(IEnumerable source, int count) { Debug.Assert(source is not null && !IsEmptyArray(source)); Debug.Assert(count > 0); @@ -19,7 +19,7 @@ private static IEnumerable TakeIterator(IEnumerable s new IEnumerableSkipTakeIterator(source, 0, count - 1); } - private static IEnumerable TakeRangeIterator(IEnumerable source, int startIndex, int endIndex) + private static IEnumerable SpeedOptimizedTakeRangeIterator(IEnumerable source, int startIndex, int endIndex) { Debug.Assert(source is not null && !IsEmptyArray(source)); Debug.Assert(startIndex >= 0 && startIndex < endIndex); diff --git a/src/libraries/System.Linq/src/System/Linq/Take.cs b/src/libraries/System.Linq/src/System/Linq/Take.cs index 65feb8a3eb08b8..9df5fbc8a2bec8 100644 --- a/src/libraries/System.Linq/src/System/Linq/Take.cs +++ b/src/libraries/System.Linq/src/System/Linq/Take.cs @@ -15,9 +15,12 @@ public static IEnumerable Take(this IEnumerable sourc ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } - return count <= 0 || IsEmptyArray(source) ? - [] : - TakeIterator(source, count); + if (count <= 0 || IsEmptyArray(source)) + { + return []; + } + + return IsSizeOptimized ? SizeOptimizedTakeIterator(source, count) : SpeedOptimizedTakeIterator(source, count); } /// Returns a specified range of contiguous elements from a sequence. @@ -60,9 +63,12 @@ public static IEnumerable Take(this IEnumerable sourc } else if (!isEndIndexFromEnd) { - return startIndex >= endIndex ? - [] : - TakeRangeIterator(source, startIndex, endIndex); + if (startIndex >= endIndex) + { + return []; + } + + return IsSizeOptimized ? SizeOptimizedTakeRangeIterator(source, startIndex, endIndex) : SpeedOptimizedTakeRangeIterator(source, startIndex, endIndex); } return TakeRangeFromEndIterator(source, isStartIndexFromEnd, startIndex, isEndIndexFromEnd, endIndex); @@ -88,7 +94,10 @@ private static IEnumerable TakeRangeFromEndIterator(IEnumerabl if (startIndex < endIndex) { - foreach (TSource element in TakeRangeIterator(source, startIndex, endIndex)) + IEnumerable rangeIterator = IsSizeOptimized + ? SizeOptimizedTakeRangeIterator(source, startIndex, endIndex) + : SpeedOptimizedTakeRangeIterator(source, startIndex, endIndex); + foreach (TSource element in rangeIterator) { yield return element; } diff --git a/src/libraries/System.Linq/src/System/Linq/ToCollection.cs b/src/libraries/System.Linq/src/System/Linq/ToCollection.cs index 05e18b2382c8c6..c6654b6e4fff1b 100644 --- a/src/libraries/System.Linq/src/System/Linq/ToCollection.cs +++ b/src/libraries/System.Linq/src/System/Linq/ToCollection.cs @@ -11,12 +11,10 @@ public static partial class Enumerable { public static TSource[] ToArray(this IEnumerable source) { -#if !OPTIMIZE_FOR_SIZE - if (source is Iterator iterator) + if (!IsSizeOptimized && source is Iterator iterator) { return iterator.ToArray(); } -#endif if (source is ICollection collection) { @@ -64,12 +62,10 @@ public static List ToList(this IEnumerable source) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } -#if !OPTIMIZE_FOR_SIZE - if (source is Iterator iterator) + if (!IsSizeOptimized && source is Iterator iterator) { return iterator.ToList(); } -#endif return new List(source); } diff --git a/src/libraries/System.Linq/tests/ConcatTests.cs b/src/libraries/System.Linq/tests/ConcatTests.cs index 9ec1b5edf05867..53273b21d57e23 100644 --- a/src/libraries/System.Linq/tests/ConcatTests.cs +++ b/src/libraries/System.Linq/tests/ConcatTests.cs @@ -320,7 +320,7 @@ public static IEnumerable ManyConcatsData() yield return [Enumerable.Range(0, 500).Select(i => Enumerable.Repeat(i, 1)).Reverse()]; } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void CountOfConcatIteratorShouldThrowExceptionOnIntegerOverflow() { var supposedlyLargeCollection = new DelegateBasedCollection { CountWorker = () => int.MaxValue }; diff --git a/src/libraries/System.Linq/tests/CountTests.cs b/src/libraries/System.Linq/tests/CountTests.cs index 33f19e47433d87..ecb87f924d6e54 100644 --- a/src/libraries/System.Linq/tests/CountTests.cs +++ b/src/libraries/System.Linq/tests/CountTests.cs @@ -178,10 +178,11 @@ public static IEnumerable NonEnumeratedCount_SupportedEnumerables() yield return WrapArgs(0, Enumerable.Empty()); - if (PlatformDetection.IsSpeedOptimized) + yield return WrapArgs(100, Enumerable.Range(1, 100)); + yield return WrapArgs(80, Enumerable.Repeat(1, 80)); + + if (PlatformDetection.IsLinqSpeedOptimized) { - yield return WrapArgs(100, Enumerable.Range(1, 100)); - yield return WrapArgs(80, Enumerable.Repeat(1, 80)); yield return WrapArgs(50, Enumerable.Range(1, 50).Select(x => x + 1)); yield return WrapArgs(4, new int[] { 1, 2, 3, 4 }.Select(x => x + 1)); yield return WrapArgs(50, Enumerable.Range(1, 50).Select(x => x + 1).Select(x => x - 1)); @@ -200,10 +201,8 @@ public static IEnumerable NonEnumeratedCount_UnsupportedEnumerables() yield return WrapArgs(new Stack([1, 2, 3, 4]).Select(x => x + 1)); yield return WrapArgs(Enumerable.Range(1, 100).Distinct()); - if (!PlatformDetection.IsSpeedOptimized) + if (!PlatformDetection.IsLinqSpeedOptimized) { - yield return WrapArgs(Enumerable.Range(1, 100)); - yield return WrapArgs(Enumerable.Repeat(1, 80)); yield return WrapArgs(Enumerable.Range(1, 50).Select(x => x + 1)); yield return WrapArgs(new int[] { 1, 2, 3, 4 }.Select(x => x + 1)); yield return WrapArgs(Enumerable.Range(1, 50).Select(x => x + 1).Select(x => x - 1)); diff --git a/src/libraries/System.Linq/tests/OrderedSubsetting.cs b/src/libraries/System.Linq/tests/OrderedSubsetting.cs index be6ae710101842..5804ac1d4229e7 100644 --- a/src/libraries/System.Linq/tests/OrderedSubsetting.cs +++ b/src/libraries/System.Linq/tests/OrderedSubsetting.cs @@ -224,7 +224,7 @@ public void TakeAndSkip() Assert.Equal(Enumerable.Range(10, 1), ordered.Take(11).Skip(10)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void TakeAndSkip_DoesntIterateRangeUnlessNecessary() { Assert.Empty(Enumerable.Range(0, int.MaxValue).Take(int.MaxValue).OrderBy(i => i).Skip(int.MaxValue - 4).Skip(15)); diff --git a/src/libraries/System.Linq/tests/RangeTests.cs b/src/libraries/System.Linq/tests/RangeTests.cs index 4b143d51d8ce48..79a41f80fcca62 100644 --- a/src/libraries/System.Linq/tests/RangeTests.cs +++ b/src/libraries/System.Linq/tests/RangeTests.cs @@ -210,19 +210,19 @@ public void FirstOrDefault() Assert.Equal(-100, Enumerable.Range(-100, int.MaxValue).FirstOrDefault()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void Last() { Assert.Equal(1000000056, Enumerable.Range(57, 1000000000).Last()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void LastOrDefault() { Assert.Equal(int.MaxValue - 101, Enumerable.Range(-100, int.MaxValue).LastOrDefault()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void IListImplementationIsValid() { Validate(Enumerable.Range(42, 10), [42, 43, 44, 45, 46, 47, 48, 49, 50, 51]); diff --git a/src/libraries/System.Linq/tests/RepeatTests.cs b/src/libraries/System.Linq/tests/RepeatTests.cs index 7ae3370660e0ec..e63a93c046fdfb 100644 --- a/src/libraries/System.Linq/tests/RepeatTests.cs +++ b/src/libraries/System.Linq/tests/RepeatTests.cs @@ -234,7 +234,7 @@ public void Count() Assert.Equal(42, Enumerable.Repeat("Test", 42).Count()); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void ICollectionImplementationIsValid() { Validate(Enumerable.Repeat(42, 10), [42, 42, 42, 42, 42, 42, 42, 42, 42, 42]); diff --git a/src/libraries/System.Linq/tests/SelectManyTests.cs b/src/libraries/System.Linq/tests/SelectManyTests.cs index 55e0e35a193d55..297b00e8aa0828 100644 --- a/src/libraries/System.Linq/tests/SelectManyTests.cs +++ b/src/libraries/System.Linq/tests/SelectManyTests.cs @@ -470,7 +470,7 @@ public static IEnumerable DisposeAfterEnumerationData() return lengths.SelectMany(l => lengths, (l1, l2) => new object[] { l1, l2 }); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] [InlineData(new[] { int.MaxValue, 1 })] [InlineData(new[] { 2, int.MaxValue - 1 })] [InlineData(new[] { 123, 456, int.MaxValue - 100000, 123456 })] diff --git a/src/libraries/System.Linq/tests/TakeTests.cs b/src/libraries/System.Linq/tests/TakeTests.cs index b19b9c17d65163..299dfb24b776a0 100644 --- a/src/libraries/System.Linq/tests/TakeTests.cs +++ b/src/libraries/System.Linq/tests/TakeTests.cs @@ -1079,7 +1079,7 @@ public void RepeatEnumeratingNotList() Assert.Equal(taken5, taken5); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] [InlineData(1000)] [InlineData(1000000)] [InlineData(int.MaxValue)] @@ -2033,7 +2033,7 @@ public void EmptySource_DoNotThrowException_EnumerablePartition() Assert.Empty(EnumerablePartitionOrEmpty(source).Take(^6..^7)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsLinqSpeedOptimized))] public void SkipTakeOnIListIsIList() { IList list = new ReadOnlyCollection(Enumerable.Range(0, 100).ToList()); diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props index c429f998d9befa..289b8c503a39fc 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WasmFeatures.props @@ -3,6 +3,7 @@ false false false + true true false false From 7afd807da79b99c2d1b4b2e03b5e147836178fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 30 Jan 2025 14:07:47 +0100 Subject: [PATCH 20/31] Combined dependency update (#111852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/arcade build 20250111.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.25058.4 -> To Version 10.0.0-beta.25061.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20250107.4 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 10.0.0-alpha.0.24627.1 -> To Version 10.0.0-alpha.0.25057.4 * Update dependencies from https://github.com/dotnet/cecil build 20250106.4 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.5-alpha.24627.1 -> To Version 0.11.5-alpha.25056.4 * Update dependencies from https://github.com/dotnet/sdk build 20250112.16 Microsoft.DotNet.ApiCompat.Task From Version 10.0.100-alpha.1.24622.2 -> To Version 10.0.100-alpha.1.25062.16 * Update dependencies from https://github.com/dotnet/icu build 20250113.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 10.0.0-alpha.1.24627.1 -> To Version 10.0.0-alpha.1.25063.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20250113.1 Microsoft.DotNet.CilStrip.Sources , Microsoft.NET.HostModel.TestData , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 10.0.0-beta.25060.2 -> To Version 10.0.0-beta.25063.1 * Update dependencies from https://github.com/dotnet/arcade build 20250117.3 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.25058.4 -> To Version 10.0.0-beta.25067.3 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20250113.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 10.0.0-alpha.0.24627.1 -> To Version 10.0.0-alpha.0.25063.1 * Update dependencies from https://github.com/dotnet/runtime build 20250118.1 Microsoft.SourceBuild.Intermediate.runtime.linux-x64 , Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Reflection.Metadata , System.Reflection.MetadataLoadContext , System.Text.Json From Version 10.0.0-alpha.1.25057.24 -> To Version 10.0.0-alpha.1.25068.1 * Update dependencies from https://github.com/dotnet/icu build 20250119.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 10.0.0-alpha.1.24627.1 -> To Version 10.0.0-alpha.1.25069.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20250120.2 Microsoft.DotNet.CilStrip.Sources , Microsoft.NET.HostModel.TestData , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 10.0.0-beta.25060.2 -> To Version 10.0.0-beta.25070.2 * Update dependencies from https://github.com/dotnet/sdk build 20250120.10 Microsoft.DotNet.ApiCompat.Task From Version 10.0.100-alpha.1.24622.2 -> To Version 10.0.100-alpha.1.25070.10 * Update dependencies from https://github.com/dotnet/command-line-api build 20250121.2 Microsoft.SourceBuild.Intermediate.command-line-api , System.CommandLine From Version 0.1.552801 -> To Version 0.1.607102 * Update dependencies from https://github.com/dotnet/command-line-api build 20250122.1 Microsoft.SourceBuild.Intermediate.command-line-api , System.CommandLine From Version 0.1.552801 -> To Version 0.1.607201 * Revert System.CommandLine update * Update dependencies from https://github.com/dotnet/sdk build 20250123.10 Microsoft.DotNet.ApiCompat.Task From Version 10.0.100-alpha.1.24622.2 -> To Version 10.0.100-alpha.1.25073.10 * Remove dependency on sdk SB intermediate * allow prebuilt for Microsoft.DotNet.ApiCompat.Task * Update dependencies from https://github.com/dotnet/icu build 20250124.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 10.0.0-alpha.1.24627.1 -> To Version 10.0.0-preview.2.25074.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20250120.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 10.0.0-alpha.0.24627.1 -> To Version 10.0.0-alpha.0.25070.1 * Update dependencies from https://github.com/dotnet/arcade build 20250126.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.25058.4 -> To Version 10.0.0-beta.25076.1 * Update dependencies from https://github.com/dotnet/cecil build 20250119.2 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.5-alpha.24627.1 -> To Version 0.11.5-alpha.25069.2 * Update dependencies from https://github.com/dotnet/source-build-externals build 20250121.1 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 10.0.562301 -> To Version 10.0.607101 * Update SDK to 10.0.100-alpha.1.25077.2 (to fix a deps.json regression) * Update Microsoft.DotNet.ApiCompat.Task version * Update aspnetcore versions --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing Co-authored-by: Viktor Hofer Co-authored-by: Matt Thalman Co-authored-by: Alexander Köplinger --- eng/SourceBuildPrebuiltBaseline.xml | 1 + eng/Version.Details.xml | 206 +++++++++--------- eng/Versions.props | 82 +++---- eng/common/internal/Tools.csproj | 10 - eng/common/tools.ps1 | 4 +- eng/common/tools.sh | 4 +- global.json | 12 +- .../sample/wasm/blazor-frame/blazor.csproj | 4 +- .../App/BlazorBasicTestApp.csproj | 4 +- .../RazorClassLibrary.csproj | 2 +- .../AspNetCoreServer/AspNetCoreServer.csproj | 2 +- .../BlazorClient/BlazorClient.csproj | 4 +- .../WasmOnAspNetCore/Shared/Shared.csproj | 2 +- 13 files changed, 161 insertions(+), 176 deletions(-) diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 4e5b0e471c1565..69be84119be4d9 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -16,6 +16,7 @@ + - + https://github.com/dotnet/cecil - 9e8bd520939ddfee686261267a1646c1b113d9e1 + 2d5c8fb9aa8bd4c7fc085a73520061075c601655 @@ -77,146 +77,146 @@ - + https://github.com/dotnet/source-build-externals - 591e522d15c8c9ffad7c7c1df1ae6a3d392717b4 + 0c377e9585d2aeae504ff1d6529ccb1abef36172 - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf https://github.com/dotnet/llvm-project @@ -302,39 +302,39 @@ https://github.com/dotnet/llvm-project 317598aea216019b6164f599859c738f69595c60 - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c - + https://github.com/dotnet/runtime - 5c6d1b3f7b63a3150ce6c737aeb4af03b3cce621 + 29013d8ae50f5bc35427a9155234ccebfa5e227c https://github.com/dotnet/xharness @@ -348,9 +348,9 @@ https://github.com/dotnet/xharness 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 - + https://github.com/dotnet/arcade - e7cb34898a1b610eb2a22591a2178da6f1fb7e3c + fe17712bedb95a55d5af00c5191772c81393745f https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -368,13 +368,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 93bf80f30db2e15a7d62c22ff80fecf3518519b1 - + https://github.com/dotnet/hotreload-utils - 7d2f352486b2e39a7829fc7fefa7d6cf825deff5 + a8ba820e852306e5098dce560629cd98e0eb8a4a - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf https://github.com/dotnet/roslyn @@ -402,15 +402,9 @@ 557c46c532788c16881dbe1b9bd3d938c2ed22e0 - + https://github.com/dotnet/sdk - 13330d5ded0b2b2bcd6459d6a410aa6220b11040 - - - - https://github.com/dotnet/sdk - 13330d5ded0b2b2bcd6459d6a410aa6220b11040 - + 23e2ba847d79562b972dbf54eca3f87c3044d925 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -470,9 +464,9 @@ https://github.com/dotnet/node 703264f70f553a06adfb330378c96f56b7583273 - + https://github.com/dotnet/runtime-assets - 6082ed1bb2cfd2d394cdc0ec613c88f3754041f7 + fc476e8f2d685eb7cadf6342393a0af2708f4dbf diff --git a/eng/Versions.props b/eng/Versions.props index 31865578e84bd8..f60fe49d8f102c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -83,33 +83,33 @@ 0.2.0 - 10.0.100-alpha.1.24622.2 + 10.0.100-alpha.1.25077.2 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 2.9.2-beta.25058.4 - 10.0.0-beta.25058.4 - 2.9.2-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 - 10.0.0-beta.25058.4 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 2.9.2-beta.25076.1 + 10.0.0-beta.25076.1 + 2.9.2-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 + 10.0.0-beta.25076.1 1.4.0 6.0.0-preview.1.102 - 10.0.0-alpha.1.25057.24 + 10.0.0-alpha.1.25068.1 6.0.0 - 10.0.0-alpha.1.25057.24 + 10.0.0-alpha.1.25068.1 6.0.0 6.0.0 @@ -123,46 +123,46 @@ 5.0.0 4.6.0 4.6.0 - 10.0.0-alpha.1.25057.24 - 10.0.0-alpha.1.25057.24 + 10.0.0-alpha.1.25068.1 + 10.0.0-alpha.1.25068.1 6.0.0 5.0.0 5.0.0 5.0.0 7.0.0 - 10.0.0-alpha.1.25057.24 + 10.0.0-alpha.1.25068.1 6.1.0 7.0.0 4.6.0 4.5.0 - 10.0.0-alpha.1.25057.24 + 10.0.0-alpha.1.25068.1 8.0.0 8.0.4 8.0.0 8.0.0 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 - 10.0.0-beta.25060.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 + 10.0.0-beta.25070.2 10.0.0-prerelease.24610.1 10.0.0-prerelease.24610.1 10.0.0-prerelease.24610.1 - 10.0.0-alpha.0.24627.1 + 10.0.0-alpha.0.25070.1 1.0.0-prerelease.25067.2 1.0.0-prerelease.25067.2 @@ -219,9 +219,9 @@ 9.0.0-preview-20241010.1 - 0.11.5-alpha.24627.1 + 0.11.5-alpha.25069.2 - 10.0.0-alpha.1.24627.1 + 10.0.0-preview.2.25074.1 2.4.3 9.0.0-alpha.1.24167.3 diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index 32f79dfb3402c0..feaa6d20812d8f 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -15,16 +15,6 @@ - - - - https://devdiv.pkgs.visualstudio.com/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json; - - - $(RestoreSources); - https://devdiv.pkgs.visualstudio.com/_packaging/VS/nuget/v3/index.json; - - diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 04b02f4fd3cc86..80f9130b15087c 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -42,7 +42,7 @@ [bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true } # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1 +# default URL: https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1 [string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' } # True to use global NuGet cache instead of restoring packages to repository-local directory. @@ -262,7 +262,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) { if (!(Test-Path $installScript)) { Create-Directory $dotnetRoot $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit - $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" + $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1" Retry({ Write-Host "GET $uri" diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 40485a0f59de16..df203b5178421d 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -54,7 +54,7 @@ warn_as_error=${warn_as_error:-true} use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh +# default URL: https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'} # True to use global NuGet cache instead of restoring packages to repository-local directory. @@ -295,7 +295,7 @@ function with_retries { function GetDotNetInstallScript { local root=$1 local install_script="$root/dotnet-install.sh" - local install_script_url="https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" + local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh" if [[ ! -a "$install_script" ]]; then mkdir -p "$root" diff --git a/global.json b/global.json index 20e56cc5204710..d459e1b3777469 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,18 @@ { "sdk": { - "version": "10.0.100-alpha.1.24610.7", + "version": "10.0.100-alpha.1.25077.2", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "10.0.100-alpha.1.24610.7" + "dotnet": "10.0.100-alpha.1.25077.2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25058.4", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25058.4", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25058.4", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25076.1", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25076.1", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25076.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "10.0.0-alpha.1.25057.24" + "Microsoft.NET.Sdk.IL": "10.0.0-alpha.1.25068.1" } } diff --git a/src/mono/sample/wasm/blazor-frame/blazor.csproj b/src/mono/sample/wasm/blazor-frame/blazor.csproj index 20ff42598cd6a8..86e8e03e032fb9 100644 --- a/src/mono/sample/wasm/blazor-frame/blazor.csproj +++ b/src/mono/sample/wasm/blazor-frame/blazor.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/src/mono/wasm/testassets/BlazorBasicTestApp/App/BlazorBasicTestApp.csproj b/src/mono/wasm/testassets/BlazorBasicTestApp/App/BlazorBasicTestApp.csproj index d9067726d9bba1..107122f796b64d 100644 --- a/src/mono/wasm/testassets/BlazorBasicTestApp/App/BlazorBasicTestApp.csproj +++ b/src/mono/wasm/testassets/BlazorBasicTestApp/App/BlazorBasicTestApp.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary/RazorClassLibrary.csproj b/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary/RazorClassLibrary.csproj index 9ab5da455e2f3a..7d785ff3dc4f8a 100644 --- a/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary/RazorClassLibrary.csproj +++ b/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary/RazorClassLibrary.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/AspNetCoreServer.csproj b/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/AspNetCoreServer.csproj index e873b8660db5ba..7b1b4ec3d34d98 100644 --- a/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/AspNetCoreServer.csproj +++ b/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/AspNetCoreServer.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/BlazorClient.csproj b/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/BlazorClient.csproj index 6ca0a0f488b8dc..cdf111a973cf9f 100644 --- a/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/BlazorClient.csproj +++ b/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/BlazorClient.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/src/mono/wasm/testassets/WasmOnAspNetCore/Shared/Shared.csproj b/src/mono/wasm/testassets/WasmOnAspNetCore/Shared/Shared.csproj index 83e2a274b02d05..8c2bce70138411 100644 --- a/src/mono/wasm/testassets/WasmOnAspNetCore/Shared/Shared.csproj +++ b/src/mono/wasm/testassets/WasmOnAspNetCore/Shared/Shared.csproj @@ -11,6 +11,6 @@ - + From c96be21a86ca9911f90a2f915a576d3959bc207e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Jan 2025 08:00:21 -0800 Subject: [PATCH 21/31] Try changing the ICustomQueryInterface implementation to always return NotHandled instead of Failed to defer back to the ComWrappers impl. (#111978) --- src/tests/Interop/COM/ComWrappers/API/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Interop/COM/ComWrappers/API/Program.cs b/src/tests/Interop/COM/ComWrappers/API/Program.cs index 3ed01e8604ad3e..8204ce75f6a903 100644 --- a/src/tests/Interop/COM/ComWrappers/API/Program.cs +++ b/src/tests/Interop/COM/ComWrappers/API/Program.cs @@ -1121,7 +1121,7 @@ CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out } } - return CustomQueryInterfaceResult.Failed; + return CustomQueryInterfaceResult.NotHandled; } } } From 1497af6ab56195b376803e4d99ca9fab434b9acc Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:04:43 +0100 Subject: [PATCH 22/31] Update dependencies from https://github.com/dotnet/arcade build 20250129.2 (#111996) Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.25076.1 -> To Version 10.0.0-beta.25079.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 84 ++++++++++++++++++++--------------------- eng/Versions.props | 32 ++++++++-------- global.json | 6 +-- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7eeaac61613d3b..bee4b873ebd71d 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -84,87 +84,87 @@ - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b https://github.com/dotnet/runtime-assets @@ -348,9 +348,9 @@ https://github.com/dotnet/xharness 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 - + https://github.com/dotnet/arcade - fe17712bedb95a55d5af00c5191772c81393745f + 3bb46f96cc988a80a414f45394f8a9ce54b47d3b https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index f60fe49d8f102c..43466d1e7b8521 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -85,22 +85,22 @@ 10.0.100-alpha.1.25077.2 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 2.9.2-beta.25076.1 - 10.0.0-beta.25076.1 - 2.9.2-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 - 10.0.0-beta.25076.1 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 2.9.2-beta.25079.2 + 10.0.0-beta.25079.2 + 2.9.2-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 + 10.0.0-beta.25079.2 1.4.0 diff --git a/global.json b/global.json index d459e1b3777469..41f301cd8a0f98 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "10.0.100-alpha.1.25077.2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25076.1", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25076.1", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25076.1", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25079.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25079.2", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25079.2", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "10.0.0-alpha.1.25068.1" From 99b373a86fcf33ede2dd7b572478ad9890547808 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:05:18 +0100 Subject: [PATCH 23/31] [main] Update dependencies from dotnet/roslyn (#111946) * Update dependencies from https://github.com/dotnet/roslyn build 20250128.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.14.0-1.25077.5 -> To Version 4.14.0-2.25078.5 * Update dependencies from https://github.com/dotnet/roslyn build 20250129.2 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.14.0-1.25077.5 -> To Version 4.14.0-2.25079.2 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index bee4b873ebd71d..c41bc071cf432a 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -376,17 +376,17 @@ https://github.com/dotnet/runtime-assets fc476e8f2d685eb7cadf6342393a0af2708f4dbf - + https://github.com/dotnet/roslyn - 557c46c532788c16881dbe1b9bd3d938c2ed22e0 + 250065a15ef94895ef87f3e09b313b987375b5b1 - + https://github.com/dotnet/roslyn - 557c46c532788c16881dbe1b9bd3d938c2ed22e0 + 250065a15ef94895ef87f3e09b313b987375b5b1 - + https://github.com/dotnet/roslyn - 557c46c532788c16881dbe1b9bd3d938c2ed22e0 + 250065a15ef94895ef87f3e09b313b987375b5b1 https://github.com/dotnet/roslyn-analyzers @@ -397,9 +397,9 @@ 5ed336762c6260a83ece35cd1f6749251452bad0 - + https://github.com/dotnet/roslyn - 557c46c532788c16881dbe1b9bd3d938c2ed22e0 + 250065a15ef94895ef87f3e09b313b987375b5b1 diff --git a/eng/Versions.props b/eng/Versions.props index 43466d1e7b8521..861bd235583e0c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -44,9 +44,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.14.0-1.25077.5 - 4.14.0-1.25077.5 - 4.14.0-1.25077.5 + 4.14.0-2.25079.2 + 4.14.0-2.25079.2 + 4.14.0-2.25079.2 From 4e01649f4a39bc2d9ee0e758736a7d90954fed14 Mon Sep 17 00:00:00 2001 From: Matous Kozak <55735845+matouskozak@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:01:30 +0000 Subject: [PATCH 26/31] [iOS][globalization] Fix IndexOf on empty strings on iOS to return -1 (#111898) --- .../CompareInfo/CompareInfoTests.IndexOf.cs | 17 +++++++++++----- .../System/StringTests.cs | 1 + .../pal_collation.m | 20 +++++++++++-------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs index eb891f784618bd..9e6e46db401f4e 100644 --- a/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs +++ b/src/libraries/System.Runtime/tests/System.Globalization.Tests/CompareInfo/CompareInfoTests.IndexOf.cs @@ -12,10 +12,17 @@ public class CompareInfoIndexOfTests : CompareInfoTestsBase { public static IEnumerable IndexOf_TestData() { - // Empty string + // Empty string, invariant yield return new object[] { s_invariantCompare, "foo", "", 0, 3, CompareOptions.None, 0, 0 }; yield return new object[] { s_invariantCompare, "foo", "", 2, 1, CompareOptions.None, 2, 0 }; yield return new object[] { s_invariantCompare, "", "", 0, 0, CompareOptions.None, 0, 0 }; + yield return new object[] { s_invariantCompare, "", "foo", 0, 0, CompareOptions.None, -1, 0 }; + + // Empty string, using non-invariant (s_germanCompare) CompareInfo to test the ICU path + yield return new object[] { s_germanCompare, "foo", "", 0, 3, CompareOptions.None, 0, 0 }; + yield return new object[] { s_germanCompare, "foo", "", 2, 1, CompareOptions.None, 2, 0 }; + yield return new object[] { s_germanCompare, "", "", 0, 0, CompareOptions.None, 0, 0 }; + yield return new object[] { s_germanCompare, "", "foo", 0, 0, CompareOptions.None, -1, 0 }; // OrdinalIgnoreCase yield return new object[] { s_invariantCompare, "Hello", "l", 0, 5, CompareOptions.OrdinalIgnoreCase, 2, 1 }; @@ -138,8 +145,8 @@ public static IEnumerable IndexOf_TestData() } // Inputs where matched length does not equal value string length - yield return new object[] { s_germanCompare, "abc Strasse Strasse xyz", "stra\u00DFe", 0, 23, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, 4, 7 }; - yield return new object[] { s_germanCompare, "abc stra\u00DFe stra\u00DFe xyz", "Strasse", 0, 21, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, 4, 6 }; + yield return new object[] { s_germanCompare, "abc Strasse Strasse xyz", "stra\u00DFe", 0, 23, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, 4, 7 }; + yield return new object[] { s_germanCompare, "abc stra\u00DFe stra\u00DFe xyz", "Strasse", 0, 21, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, 4, 6 }; if (PlatformDetection.IsNotHybridGlobalizationOnApplePlatform) { yield return new object[] { s_invariantCompare, "abcdzxyz", "\u01F3", 0, 8, CompareOptions.IgnoreNonSpace, 3, 2 }; @@ -153,7 +160,7 @@ public static IEnumerable IndexOf_Aesc_Ligature_TestData() { bool useNls = PlatformDetection.IsNlsGlobalization; // Searches for the ligature \u00C6 - string source1 = "Is AE or ae the same as \u00C6 or \u00E6?"; // 3 failures here + string source1 = "Is AE or ae the same as \u00C6 or \u00E6?"; yield return new object[] { s_invariantCompare, source1, "AE", 8, 18, CompareOptions.None, useNls ? 24 : -1, useNls ? 1 : 0}; yield return new object[] { s_invariantCompare, source1, "ae", 8, 18, CompareOptions.None, 9 , 2}; yield return new object[] { s_invariantCompare, source1, "\u00C6", 8, 18, CompareOptions.None, 24, 1 }; @@ -171,7 +178,7 @@ public static IEnumerable IndexOf_Aesc_Ligature_TestData() public static IEnumerable IndexOf_U_WithDiaeresis_TestData() { // Searches for the combining character sequence Latin capital letter U with diaeresis or Latin small letter u with diaeresis. - string source = "Is \u0055\u0308 or \u0075\u0308 the same as \u00DC or \u00FC?"; // 7 failures here + string source = "Is \u0055\u0308 or \u0075\u0308 the same as \u00DC or \u00FC?"; yield return new object[] { s_invariantCompare, source, "U\u0308", 8, 18, CompareOptions.None, 24, 1 }; yield return new object[] { s_invariantCompare, source, "u\u0308", 8, 18, CompareOptions.None, 9, 2 }; yield return new object[] { s_invariantCompare, source, "\u00DC", 8, 18, CompareOptions.None, 24, 1 }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs index a9ebe3bbe8b9ce..0dd89ccdc5ee82 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs @@ -210,6 +210,7 @@ public static void Contains_Char(string s, char value, bool expected) [InlineData("Hello", 'e', StringComparison.CurrentCulture, true)] [InlineData("Hello", 'E', StringComparison.CurrentCulture, false)] [InlineData("", 'H', StringComparison.CurrentCulture, false)] + [InlineData("", '\u0301', StringComparison.CurrentCulture, false)] // Using non-ASCII character to test ICU path // CurrentCultureIgnoreCase [InlineData("Hello", 'H', StringComparison.CurrentCultureIgnoreCase, true)] [InlineData("Hello", 'Z', StringComparison.CurrentCultureIgnoreCase, false)] diff --git a/src/native/libs/System.Globalization.Native/pal_collation.m b/src/native/libs/System.Globalization.Native/pal_collation.m index ebe0db5c2c202a..56af941fb40033 100644 --- a/src/native/libs/System.Globalization.Native/pal_collation.m +++ b/src/native/libs/System.Globalization.Native/pal_collation.m @@ -117,6 +117,11 @@ int32_t GlobalizationNative_CompareStringNative(const uint16_t* localeName, int3 } } +/** + * Removes zero-width and other weightless characters such as U+200B (Zero Width Space), + * U+200C (Zero Width Non-Joiner), U+200D (Zero Width Joiner), U+FEFF (Zero Width No-Break Space), + * and the NUL character from the specified string. + */ static NSString* RemoveWeightlessCharacters(NSString* source) { NSError *error = nil; @@ -143,10 +148,9 @@ static int32_t IsIndexFound(int32_t fromBeginning, int32_t foundLocation, int32_ /* Function: IndexOf -Find detailed explanation how this function works in https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-hybrid-mode.md +Find detailed explanation how this function works in https://github.com/dotnet/runtime/blob/main/docs/design/features/globalization-hybrid-mode.md#string-indexing */ -Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNameLength, const uint16_t* lpTarget, int32_t cwTargetLength, - const uint16_t* lpSource, int32_t cwSourceLength, int32_t comparisonOptions, int32_t fromBeginning) +Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNameLength, const uint16_t* lpTarget, int32_t cwTargetLength, const uint16_t* lpSource, int32_t cwSourceLength, int32_t comparisonOptions, int32_t fromBeginning) { @autoreleasepool { @@ -158,6 +162,9 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam return result; } NSStringCompareOptions options = ConvertFromCompareOptionsToNSStringCompareOptions(comparisonOptions, true); + if (!fromBeginning) // LastIndexOf + options |= NSBackwardsSearch; + NSString *searchString = [NSString stringWithCharacters: lpTarget length: (NSUInteger)cwTargetLength]; NSString *searchStrCleaned = RemoveWeightlessCharacters(searchString); NSString *sourceString = [NSString stringWithCharacters: lpSource length: (NSUInteger)cwSourceLength]; @@ -168,7 +175,7 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam searchStrCleaned = ConvertToKatakana(searchStrCleaned); } - if (sourceStrCleaned.length == 0 || searchStrCleaned.length == 0) + if (searchStrCleaned.length == 0) { result.location = fromBeginning ? 0 : (int32_t)sourceString.length; return result; @@ -178,9 +185,6 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam NSString *searchStrPrecomposed = searchStrCleaned.precomposedStringWithCanonicalMapping; NSString *sourceStrPrecomposed = sourceStrCleaned.precomposedStringWithCanonicalMapping; - // last index - if (!fromBeginning) - options |= NSBackwardsSearch; // check if there is a possible match and return -1 if not // doesn't matter which normalization form is used here @@ -233,7 +237,7 @@ Range GlobalizationNative_IndexOfNative(const uint16_t* localeName, int32_t lNam result.location = (int32_t)precomposedRange.location; result.length = (int32_t)precomposedRange.length; if (!(comparisonOptions & IgnoreCase)) - return result; + return result; } // check if sourceString has decomposed form of characters and searchString has precomposed form of characters From f652094f081925774826a5be8147b5cdea3c386f Mon Sep 17 00:00:00 2001 From: Ruihan-Yin <107431934+Ruihan-Yin@users.noreply.github.com> Date: Thu, 30 Jan 2025 09:48:14 -0800 Subject: [PATCH 27/31] [JIT] Add legacy extended EVEX encoding and EVEX.ND/NF feature to x64 emitter backend (#108796) * Ruihan: POC with REX2 Update comments. Merge the REX2 changes into the original legacy emit path bug fix: Set REX2.W with correct mask code. register encoding and prefix emitting logics. Add REX2 prefix emit logic bug fixes Add Stress mode for REX2 encoding and some bug fixes resolve comments: 1. add assertion check for UD opcodes. 2. add checks for EGPRs. Add REX2 to emitOutputAM, and let LEA to be REX2 compatible. Add REX2.X encoding for SIB byte But fixes: add REX2 prefix on the path in RI where MOV is specially handled. Enable REX2 encoding for `movups` fixed bugs in REX2 prefix emitting logic when working with map 1 instructions, and enabled REX2 for POPCNT legacy map index-er bug fixes some clean-up Adding initial APX unit testing path. Adding a coredistools dll that has LLVM APX disasm capability. It must be coppied into a CORE_ROOT manually. clean up work for REX2 narrow the REX2 scope to `sub` only some clean up based on the comments. bug fix resolve comment * resolve comments * refactor register encoding for REX2 * merge REX2 path to legacy path * Enable REX2 in more instructions. * Avoid repeatedly estimate the size of REX2 prefix * Enable REX2 encoding on RI and SV path - SV path is mostly for debugging purposes Added encoding unit tests for instructions with immediates * Add rex2 support to rotate and shift. * CR session. * Testing infra updates: assert REX2 is enabled. Code refactoring: AddX86PrefixIfNeeded. * revert rcl_N and rcr_N, tp and latency data for these instructions is missing in JIT, may indicate these instructions are not being used in JIT, drop them for now. * partially enable REX2 on emitOutputAM, case covered: R_AR and AR_R. * Adding unit tests. * push, pop, inc, dec, neg, not, xadd, shld, shrd, cmpxchg, setcc, bswap. * bug fix for bswap * bt * xchg, idiv * Make sure add REX2 prefix if register encoding for EGPRs are being called before adding any prefix. * Ensure code size is correctly computed in R_R_I path. * clean up * Change all AddSimdPrefix to AddX86Prefix Refactor REX2 encoding stress logics. * div, mulEAX * filter out test from REX2 encoding when using ACC form. (this will have side effect that the estimated code will go up and mismatch with actual code size.) * Make sure REX prefix will not be added when emitting with REX2. * resolve comments. * make sure the APX debug knob is only available under debug build. * clean up some out-dated code. * enable movsxd * Enable "Call" * Enable "JMP" * resolve merge errors * formatting * remote coredistools.dll for internal tests only * bug fix * SUB reg, reg, reg * enable NDD on genCodeForBinary * consolidate TakesLegacyPromotedEvexPrefix logics. * ensure register encoding is correct under legacy-promoted-evex encoding. * Make sure the overflow check is correctly emitted. * simplify the compiler setup logics. * emitInsNddBinary * make sure REX will not be added when EVEX presents. * resolve comment and clean up. * enable more NDD instructions. * bug fixes * enable imul * add emitter unit tests, and fix encoding error for CMOVcc * bug fixes: 1. make sure RWR_RRD_SHF has correct register update mode 2. make sure shift instructions will get correct opcode in RR path. * refactor emitInsBinary * clean up * clean up and refactor some code * make sure the code size estimation is correct for some apx promoted instructions. * add tuning knob to EVEX.ND feature. * flip the Evex.nd knob. * put NDD control knob to the correct place. * resolve merge errors * Make sure APX related knobs are defined properly across platforms * Add Evex.nf to instrDesc * {nf} add reg, reg * Enable EVEX.NF in more instructions * more instructions * comments. * lzcnt, tzcnt, popcnt * Exclude ACC form from EVEX promotion. * BMI instructions. * bug fixes * Tweak the code size calculation to make sure REX2 and APX-EVEX are properly handled. * bug fixes for stress mode * Add idEvexNoPromotion to emitter to exclude the APX-EVEX promotion from LOCK prefixd instructions. * resolve merge error * fix merge error * Revert "Add idEvexNoPromotion to emitter to exclude the APX-EVEX promotion from LOCK prefixd instructions." This reverts commit 1be4b12f115f032e3fa878fd2d3f7619e6c994c6. * bug fix * introduce _no_evex suffix for some instructions for cases when LOCK will be prefixed. * resolve merge comflict * fix merge error. * fix comments and some checks. * formatting * remove unneeded env var. * Make sure the BMI instruction is properly hidden behind APX stress knob and will not be EVEX-encoded when JitStressEvexEncoding is set. * resolve merge error. * Resolve comments. * formatting. * Add rcr/rcl emitter unit tests for extended EVEX. * Resolve merge error. * formatting * resolve comments * formatting * use the right size for neg and not code gen. --- src/coreclr/jit/codegenxarch.cpp | 182 +++- src/coreclr/jit/compiler.cpp | 1 + src/coreclr/jit/compiler.h | 31 +- src/coreclr/jit/emit.h | 94 +- src/coreclr/jit/emitfmtsxarch.h | 1 + src/coreclr/jit/emitxarch.cpp | 1431 ++++++++++++++++++++++++----- src/coreclr/jit/emitxarch.h | 100 +- src/coreclr/jit/instr.h | 19 + src/coreclr/jit/instrsxarch.h | 161 ++-- src/coreclr/jit/jitconfigvalues.h | 6 +- 10 files changed, 1614 insertions(+), 412 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4f9b2c4f0184b2..a86a2ad5b36969 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -402,12 +402,13 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, else { // For section constant, the immediate will be relocatable - GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, + INS_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags)); } } else { - GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, INS_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags)); } } regSet.verifyRegUsed(reg); @@ -738,12 +739,10 @@ void CodeGen::genCodeForNegNot(GenTree* tree) { GenTree* operand = tree->gtGetOp1(); assert(operand->isUsedFromReg()); - regNumber operandReg = genConsumeReg(operand); + regNumber operandReg = genConsumeReg(operand); + instruction ins = genGetInsForOper(tree->OperGet(), targetType); - inst_Mov(targetType, targetReg, operandReg, /* canSkip */ true); - - instruction ins = genGetInsForOper(tree->OperGet(), targetType); - inst_RV(ins, targetReg, targetType); + GetEmitter()->emitIns_BASE_R_R(ins, emitActualTypeSize(tree), targetReg, operandReg); } genProduceReg(tree); @@ -1063,6 +1062,8 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) GenTree* op1 = treeNode->gtGetOp1(); GenTree* op2 = treeNode->gtGetOp2(); + bool eligibleForNDD = false; + // Commutative operations can mark op1 as contained or reg-optional to generate "op reg, memop/immed" if (!op1->isUsedFromReg()) { @@ -1158,31 +1159,57 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) // reg3 = reg3 op reg2 else { - var_types op1Type = op1->TypeGet(); - inst_Mov(op1Type, targetReg, op1reg, /* canSkip */ false); - regSet.verifyRegUsed(targetReg); - gcInfo.gcMarkRegPtrVal(targetReg, op1Type); - dst = treeNode; - src = op2; + // when reg3 != reg1 && reg3 != reg2, and NDD is available, we can use APX-EVEX.ND to optimize the codegen. + eligibleForNDD = emit->DoJitUseApxNDD(ins); + if (!eligibleForNDD) + { + var_types op1Type = op1->TypeGet(); + inst_Mov(op1Type, targetReg, op1reg, /* canSkip */ false); + regSet.verifyRegUsed(targetReg); + gcInfo.gcMarkRegPtrVal(targetReg, op1Type); + dst = treeNode; + src = op2; + } + else + { + dst = op1; + src = op2; + } } + // we can assume all the floating instructions are processed and returned above. + assert(!varTypeIsFloating(treeNode)); + // try to use an inc or dec - if (oper == GT_ADD && !varTypeIsFloating(treeNode) && src->isContainedIntOrIImmed() && !treeNode->gtOverflowEx()) + if (oper == GT_ADD && src->isContainedIntOrIImmed() && !treeNode->gtOverflowEx()) { if (src->IsIntegralConst(1)) { - emit->emitIns_R(INS_inc, emitTypeSize(treeNode), targetReg); + emit->emitIns_BASE_R_R(INS_inc, emitTypeSize(treeNode), targetReg, dst->GetRegNum()); genProduceReg(treeNode); return; } else if (src->IsIntegralConst(-1)) { - emit->emitIns_R(INS_dec, emitTypeSize(treeNode), targetReg); + emit->emitIns_BASE_R_R(INS_dec, emitTypeSize(treeNode), targetReg, dst->GetRegNum()); genProduceReg(treeNode); return; } } - regNumber r = emit->emitInsBinary(ins, emitTypeSize(treeNode), dst, src); + + regNumber r = REG_NA; + if (eligibleForNDD) + { + // operands should be already formatted above + assert(dst->isUsedFromReg()); + assert(op1reg != targetReg); + assert(op2reg != targetReg); + r = emit->emitIns_BASE_R_R_RM(ins, emitTypeSize(treeNode), targetReg, treeNode, dst, src); + } + else + { + r = emit->emitInsBinary(ins, emitTypeSize(treeNode), dst, src); + } noway_assert(r == targetReg); if (treeNode->gtOverflowEx()) @@ -1295,10 +1322,7 @@ void CodeGen::genCodeForMul(GenTreeOp* treeNode) } assert(regOp->isUsedFromReg()); - // Setup targetReg when neither of the source operands was a matching register - inst_Mov(targetType, mulTargetReg, regOp->GetRegNum(), /* canSkip */ true); - - emit->emitInsBinary(ins, size, treeNode, rmOp); + emit->emitIns_BASE_R_R_RM(ins, size, mulTargetReg, treeNode, regOp, rmOp); // Move the result to the desired register, if necessary if (ins == INS_mulEAX) @@ -4406,23 +4430,24 @@ void CodeGen::genCodeForLockAdd(GenTreeOp* node) if (imm == 1) { // inc [addr] - GetEmitter()->emitIns_AR(INS_inc, size, addr->GetRegNum(), 0); + GetEmitter()->emitIns_AR(INS_inc, size, addr->GetRegNum(), 0, INS_OPTS_EVEX_NoApxPromotion); } else if (imm == -1) { // dec [addr] - GetEmitter()->emitIns_AR(INS_dec, size, addr->GetRegNum(), 0); + GetEmitter()->emitIns_AR(INS_dec, size, addr->GetRegNum(), 0, INS_OPTS_EVEX_NoApxPromotion); } else { // add [addr], imm - GetEmitter()->emitIns_I_AR(INS_add, size, imm, addr->GetRegNum(), 0); + GetEmitter()->emitIns_I_AR(INS_add, size, imm, addr->GetRegNum(), 0, INS_OPTS_EVEX_NoApxPromotion); } } else { // add [addr], data - GetEmitter()->emitIns_AR_R(INS_add, size, data->GetRegNum(), addr->GetRegNum(), 0); + GetEmitter()->emitIns_AR_R(INS_add, size, data->GetRegNum(), addr->GetRegNum(), 0, + INS_OPTS_EVEX_NoApxPromotion); } } @@ -4459,7 +4484,8 @@ void CodeGen::genLockedInstructions(GenTreeOp* node) // or/and dword ptr [addrReg], val // instGen(INS_lock); - GetEmitter()->emitIns_AR_R(ins, size, data->GetRegNum(), addr->GetRegNum(), 0); + GetEmitter()->emitIns_AR_R(ins, size, data->GetRegNum(), addr->GetRegNum(), 0, + INS_OPTS_EVEX_NoApxPromotion); } else { @@ -4842,11 +4868,10 @@ void CodeGen::genCodeForShift(GenTree* tree) return; } #endif - // First, move the operand to the destination register and - // later on perform the shift in-place. - // (LSRA will try to avoid this situation through preferencing.) - inst_Mov(targetType, tree->GetRegNum(), operandReg, /* canSkip */ true); - inst_RV_SH(ins, size, tree->GetRegNum(), shiftByValue); + ins = genMapShiftInsToShiftByConstantIns(ins, shiftByValue); + GetEmitter()->emitIns_BASE_R_R_I(ins, emitTypeSize(tree), tree->GetRegNum(), operandReg, shiftByValue); + genProduceReg(tree); + return; } } #if defined(TARGET_64BIT) @@ -4887,8 +4912,7 @@ void CodeGen::genCodeForShift(GenTree* tree) // The operand to be shifted must not be in ECX noway_assert(operandReg != REG_RCX); - inst_Mov(targetType, tree->GetRegNum(), operandReg, /* canSkip */ true); - inst_RV(ins, tree->GetRegNum(), targetType); + GetEmitter()->emitIns_BASE_R_R(ins, emitTypeSize(tree), tree->GetRegNum(), operandReg); } genProduceReg(tree); @@ -9237,6 +9261,96 @@ void CodeGen::genAmd64EmitterUnitTestsApx() theEmitter->emitIns_S(INS_neg, EA_2BYTE, 0, 0); theEmitter->emitIns_S(INS_not, EA_2BYTE, 0, 0); + + // APX-EVEX + + theEmitter->emitIns_R_R_R(INS_add, EA_8BYTE, REG_R10, REG_EAX, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_R(INS_sub, EA_2BYTE, REG_R10, REG_EAX, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_R(INS_or, EA_2BYTE, REG_R10, REG_EAX, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_R(INS_and, EA_2BYTE, REG_R10, REG_EAX, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_R(INS_xor, EA_1BYTE, REG_R10, REG_EAX, REG_ECX, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R_I(INS_or, EA_2BYTE, REG_R10, REG_EAX, 10565, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_I(INS_or, EA_8BYTE, REG_R10, REG_EAX, 10, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_S(INS_or, EA_8BYTE, REG_R10, REG_EAX, 0, 1, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R(INS_neg, EA_2BYTE, REG_R10, REG_ECX, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R(INS_shl, EA_2BYTE, REG_R11, REG_EAX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R(INS_shl_1, EA_2BYTE, REG_R11, REG_EAX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_I(INS_shl_N, EA_2BYTE, REG_R11, REG_ECX, 7, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_I(INS_shl_N, EA_2BYTE, REG_R11, REG_ECX, 7, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_I(INS_rcr_N, EA_2BYTE, REG_R11, REG_ECX, 7, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_I(INS_rcl_N, EA_2BYTE, REG_R11, REG_ECX, 7, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R(INS_inc, EA_2BYTE, REG_R11, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R(INS_dec, EA_2BYTE, REG_R11, REG_ECX, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R_R(INS_cmovo, EA_4BYTE, REG_R12, REG_R11, REG_EAX, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R_R(INS_imul, EA_4BYTE, REG_R12, REG_R11, REG_ECX, INS_OPTS_EVEX_nd); + theEmitter->emitIns_R_R_S(INS_imul, EA_4BYTE, REG_R12, REG_R11, 0, 1, INS_OPTS_EVEX_nd); + + theEmitter->emitIns_R_R(INS_add, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_sub, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_and, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_or, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_xor, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_inc, EA_4BYTE, REG_R12, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_dec, EA_4BYTE, REG_R12, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_I(INS_add, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_sub, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_and, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_or, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_xor, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_S(INS_add, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_sub, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_and, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_or, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_xor, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R(INS_neg, EA_2BYTE, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_shl, EA_2BYTE, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_shl_1, EA_2BYTE, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_shl_N, EA_2BYTE, REG_R11, 7, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_shl_N, EA_2BYTE, REG_R11, 7, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_rcr_N, EA_2BYTE, REG_R11, 7, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_I(INS_rcl_N, EA_2BYTE, REG_R11, 7, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_R(INS_imul, EA_4BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_imul, EA_4BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_I(INS_imul_15, EA_4BYTE, REG_R12, 5, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R(INS_imulEAX, EA_8BYTE, REG_R12, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_mulEAX, EA_8BYTE, REG_R12, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_div, EA_8BYTE, REG_R12, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R(INS_idiv, EA_8BYTE, REG_R12, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_R(INS_tzcnt_apx, EA_8BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_lzcnt_apx, EA_8BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_popcnt_apx, EA_8BYTE, REG_R12, REG_R11, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_S(INS_tzcnt_apx, EA_8BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_lzcnt_apx, EA_8BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_popcnt_apx, EA_8BYTE, REG_R12, 0, 1, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_R_R(INS_add, EA_2BYTE, REG_R12, REG_R13, REG_R11, + (insOpts)(INS_OPTS_EVEX_nf | INS_OPTS_EVEX_nd)); + + theEmitter->emitIns_R_R_R(INS_andn, EA_8BYTE, REG_R11, REG_R13, REG_R11, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R_R(INS_bextr, EA_8BYTE, REG_R11, REG_R13, REG_R11, INS_OPTS_EVEX_nf); + + theEmitter->emitIns_R_R(INS_blsi, EA_8BYTE, REG_R11, REG_R13, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_R(INS_blsmsk, EA_8BYTE, REG_R11, REG_R13, INS_OPTS_EVEX_nf); + theEmitter->emitIns_R_S(INS_blsr, EA_8BYTE, REG_R11, 0, 1); + + theEmitter->emitIns_AR(INS_inc, EA_4BYTE, REG_EAX, 0, INS_OPTS_EVEX_NoApxPromotion); + + theEmitter->emitIns_BASE_R_R(INS_inc, EA_4BYTE, REG_R11, REG_R12); + theEmitter->emitIns_BASE_R_R_I(INS_add, EA_4BYTE, REG_R11, REG_R12, 5); } void CodeGen::genAmd64EmitterUnitTestsAvx10v2() @@ -11434,7 +11548,7 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) if (barrierKind == BARRIER_FULL) { instGen(INS_lock); - GetEmitter()->emitIns_I_AR(INS_or, EA_4BYTE, 0, REG_SPBASE, 0); + GetEmitter()->emitIns_I_AR(INS_or, EA_4BYTE, 0, REG_SPBASE, 0, INS_OPTS_EVEX_NoApxPromotion); } } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 33dda8c734ca1b..274542e7823f56 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2299,6 +2299,7 @@ void Compiler::compSetProcessor() if (canUseApxEncoding()) { codeGen->GetEmitter()->SetUseRex2Encoding(true); + codeGen->GetEmitter()->SetUsePromotedEVEXEncoding(true); } } #endif // TARGET_XARCH diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 4a639c7e639f9e..473dc3dc47153a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4014,7 +4014,7 @@ class Compiler // false: we can add new tracked variables. // true: We cannot add new 'tracked' variable - bool lvaTrackedFixed = false; + bool lvaTrackedFixed = false; unsigned lvaCount; // total number of locals, which includes function arguments, // special arguments, IL local variables, and JIT temporary variables @@ -6924,7 +6924,7 @@ class Compiler unsigned acdCount = 0; // Get the index to use as part of the AddCodeDsc key for sharing throw blocks - unsigned bbThrowIndex(BasicBlock* blk, AcdKeyDesignator* dsg); + unsigned bbThrowIndex(BasicBlock* blk, AcdKeyDesignator* dsg); struct AddCodeDscKey { @@ -6932,7 +6932,7 @@ class Compiler AddCodeDscKey(): acdKind(SCK_NONE), acdData(0) {} AddCodeDscKey(SpecialCodeKind kind, BasicBlock* block, Compiler* comp); AddCodeDscKey(AddCodeDsc* add); - + static bool Equals(const AddCodeDscKey& x, const AddCodeDscKey& y) { return (x.acdData == y.acdData) && (x.acdKind == y.acdKind); @@ -10013,10 +10013,10 @@ class Compiler } //------------------------------------------------------------------------ - // canUseRex2Encoding - Answer the question: Is Rex2 encoding supported on this target. + // canUseApxEncoding - Answer the question: Are APX encodings supported on this target. // // Returns: - // `true` if Rex2 encoding is supported, `false` if not. + // `true` if APX encoding is supported, `false` if not. // bool canUseApxEncoding() const { @@ -10068,7 +10068,7 @@ class Compiler bool DoJitStressRex2Encoding() const { #ifdef DEBUG - if (JitConfig.JitStressRex2Encoding() && compOpportunisticallyDependsOn(InstructionSet_APX)) + if (JitConfig.JitStressRex2Encoding()) { // we should make sure EVEX is also stressed when REX2 is stressed, as we will need to guarantee EGPR // functionality is properly turned on for every instructions when REX2 is stress. @@ -10083,13 +10083,30 @@ class Compiler // JitStressEvexEncoding- Answer the question: Is Evex stress knob set // // Returns: - // `true` if user requests REX2 encoding. + // `true` if user requests EVEX encoding. // bool JitStressEvexEncoding() const { #ifdef DEBUG return JitConfig.JitStressEvexEncoding() || JitConfig.JitStressRex2Encoding(); #endif // DEBUG + return false; + } + + //------------------------------------------------------------------------ + // DoJitStressPromotedEvexEncoding- Answer the question: Do we force promoted EVEX encoding. + // + // Returns: + // `true` if user requests promoted EVEX encoding. + // + bool DoJitStressPromotedEvexEncoding() const + { +#ifdef DEBUG + if (JitConfig.JitStressPromotedEvexEncoding()) + { + return true; + } +#endif // DEBUG return false; } diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 02461633f3c547..e8854e78a2200d 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -471,6 +471,7 @@ class emitter SetUseVEXEncoding(false); SetUseEvexEncoding(false); SetUseRex2Encoding(false); + SetUsePromotedEVEXEncoding(false); #endif // TARGET_XARCH emitDataSecCur = nullptr; @@ -793,7 +794,19 @@ class emitter // For normal and embedded broadcast intrinsics, EVEX.L'L has the same semantic, vector length. // For embedded rounding, EVEX.L'L semantic changes to indicate the rounding mode. // Multiple bits in _idEvexbContext are used to inform emitter to specially handle the EVEX.L'L bits. - unsigned _idEvexbContext : 2; + unsigned _idCustom5 : 1; + unsigned _idCustom6 : 1; + +#define _idEvexbContext \ + (_idCustom6 << 1) | _idCustom5 /* Evex.b: embedded broadcast, embedded rounding, embedded SAE \ + */ +#define _idEvexNdContext _idCustom5 /* bits used for the APX-EVEX.nd context for promoted legacy instructions */ +#define _idEvexNfContext _idCustom6 /* bits used for the APX-EVEX.nf context for promoted legacy/vex instructions */ + + // In certian cases, we do not allow instructions to be promoted to APX-EVEX. + // e.g. instructions like add/and/or/inc/dec can be used with LOCK prefix, but cannot be prefixed by LOCK and + // EVEX together. + unsigned _idNoApxEvexXPromotion : 1; #endif // TARGET_XARCH #ifdef TARGET_ARM64 @@ -826,8 +839,8 @@ class emitter //////////////////////////////////////////////////////////////////////// // Space taken up to here: - // x86: 48 bits - // amd64: 48 bits + // x86: 49 bits + // amd64: 49 bits // arm: 48 bits // arm64: 55 bits // loongarch64: 46 bits @@ -845,7 +858,7 @@ class emitter #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) #define ID_EXTRA_BITFIELD_BITS (14) #elif defined(TARGET_XARCH) -#define ID_EXTRA_BITFIELD_BITS (16) +#define ID_EXTRA_BITFIELD_BITS (17) #else #error Unsupported or unset target architecture #endif @@ -879,8 +892,8 @@ class emitter //////////////////////////////////////////////////////////////////////// // Space taken up to here (with/without prev offset, assuming host==target): - // x86: 54/50 bits - // amd64: 55/50 bits + // x86: 55/51 bits + // amd64: 56/51 bits // arm: 54/50 bits // arm64: 62/57 bits // loongarch64: 53/48 bits @@ -1657,38 +1670,17 @@ class emitter #ifdef TARGET_XARCH bool idIsEvexbContextSet() const { - return _idEvexbContext != 0; + return idGetEvexbContext() != 0; } void idSetEvexbContext(insOpts instOptions) { assert(!idIsEvexbContextSet()); + assert(idGetEvexbContext() == 0); + unsigned value = static_cast(instOptions & INS_OPTS_EVEX_b_MASK); - switch (instOptions & INS_OPTS_EVEX_b_MASK) - { - case INS_OPTS_EVEX_eb_er_rd: - { - _idEvexbContext = 1; - break; - } - - case INS_OPTS_EVEX_er_ru: - { - _idEvexbContext = 2; - break; - } - - case INS_OPTS_EVEX_er_rz: - { - _idEvexbContext = 3; - break; - } - - default: - { - unreached(); - } - } + _idCustom5 = ((value >> 0) & 1); + _idCustom6 = ((value >> 1) & 1); } unsigned idGetEvexbContext() const @@ -1728,6 +1720,39 @@ class emitter assert(!idIsEvexZContextSet()); _idEvexZContext = 1; } + + bool idIsEvexNdContextSet() const + { + return _idEvexNdContext != 0; + } + + void idSetEvexNdContext() + { + assert(!idIsEvexNdContextSet()); + _idEvexNdContext = 1; + } + + bool idIsEvexNfContextSet() const + { + return _idEvexNfContext != 0; + } + + void idSetEvexNfContext() + { + assert(!idIsEvexNfContextSet()); + _idEvexNfContext = 1; + } + + bool idIsNoApxEvexPromotion() const + { + return _idNoApxEvexXPromotion != 0; + } + + void idSetNoApxEvexPromotion() + { + assert(!idIsNoApxEvexPromotion()); + _idNoApxEvexXPromotion = 1; + } #endif #ifdef TARGET_ARMARCH @@ -2531,7 +2556,12 @@ class emitter CORINFO_FIELD_HANDLE emitSimdMaskConst(simdmask_t constValue); #endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD + +#if defined(TARGET_XARCH) + regNumber emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src, regNumber targetReg = REG_NA); +#else regNumber emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src); +#endif regNumber emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src1, GenTree* src2); void emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, GenTreeIndir* mem); void emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* mem); diff --git a/src/coreclr/jit/emitfmtsxarch.h b/src/coreclr/jit/emitfmtsxarch.h index f893fce8d07eea..a94a7c1b3e7d5b 100644 --- a/src/coreclr/jit/emitfmtsxarch.h +++ b/src/coreclr/jit/emitfmtsxarch.h @@ -140,6 +140,7 @@ IF_DEF(RRW_RRW, IS_R1_RW|IS_R2_RW, NONE) // r/w IF_DEF(RRD_RRD_CNS, IS_R1_RD|IS_R2_RD, SCNS) // read reg1, read reg2, const IF_DEF(RWR_RRD_CNS, IS_R1_WR|IS_R2_RD, SCNS) // write reg1, read reg2, const IF_DEF(RRW_RRD_CNS, IS_R1_RW|IS_R2_RD, SCNS) // r/w reg1, read reg2, const +IF_DEF(RWR_RRD_SHF, IS_R1_WR|IS_R2_RD, SCNS) // write reg1, read reg2, shift IF_DEF(RRD_RRD_RRD, IS_R1_RD|IS_R2_RD|IS_R3_RD, NONE) // read reg1, read reg2, read reg3 IF_DEF(RWR_RRD_RRD, IS_R1_WR|IS_R2_RD|IS_R3_RD, NONE) // write reg1, read reg2, read reg3 diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index ddec8af5e753f5..8b51ff52f181f8 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -236,6 +236,18 @@ bool emitter::HasRex2Encoding(instruction ins) const return (flags & Encoding_REX2) != 0; } +bool emitter::HasApxNdd(instruction ins) const +{ + insFlags flags = CodeGenInterface::instInfo[ins]; + return (flags & INS_Flags_Has_NDD) != 0; +} + +bool emitter::HasApxNf(instruction ins) const +{ + insFlags flags = CodeGenInterface::instInfo[ins]; + return (flags & INS_Flags_Has_NF) != 0; +} + bool emitter::IsVexEncodableInstruction(instruction ins) const { if (!UseVEXEncoding()) @@ -293,6 +305,106 @@ bool emitter::IsRex2EncodableInstruction(instruction ins) const return HasRex2Encoding(ins); } +//------------------------------------------------------------------------ +// IsApxNDDEncodableInstruction: Answer the question- does this instruction have apx ndd form. +// +// Arguments: +// ins - The instruction to check. +// +// Returns: +// `true` if ins has apx ndd form. +// +bool emitter::IsApxNDDEncodableInstruction(instruction ins) const +{ + if (!UsePromotedEVEXEncoding()) + { + return false; + } + + return HasApxNdd(ins); +} + +//------------------------------------------------------------------------ +// IsApxNFEncodableInstruction: Answer the question - does this instruction have Evex.nf supported +// +// Arguments: +// ins - The instruction to check. +// +// Returns: +// `true` if ins is Evex.nf supported. +// +bool emitter::IsApxNFEncodableInstruction(instruction ins) const +{ + if (!UsePromotedEVEXEncoding()) + { + return false; + } + + return HasApxNf(ins); +} + +//------------------------------------------------------------------------ +// IsApxExtendedEvexInstruction: Answer the question - does this instruction have apx extended evex form. +// +// Arguments: +// ins - The instruction to check. +// +// Returns: +// `true` if ins has apx extended evex form. +// +bool emitter::IsApxExtendedEvexInstruction(instruction ins) const +{ + if (!UsePromotedEVEXEncoding()) + { + return false; + } + + return HasApxNdd(ins) || HasApxNf(ins); +} + +//------------------------------------------------------------------------ +// IsShiftInstruction: Answer the question- is this instruction a shift instruction. +// +// Arguments: +// ins - The instruction to check. +// +// Returns: +// `true` if ins is a shift instruction. +// +bool emitter::IsShiftInstruction(instruction ins) const +{ + switch (ins) + { + case INS_rcl_1: + case INS_rcr_1: + case INS_rol_1: + case INS_ror_1: + case INS_shl_1: + case INS_shr_1: + case INS_sar_1: + + case INS_rcl: + case INS_rcr: + case INS_rol: + case INS_ror: + case INS_shl: + case INS_shr: + case INS_sar: + + case INS_rcl_N: + case INS_rcr_N: + case INS_rol_N: + case INS_ror_N: + case INS_shl_N: + case INS_shr_N: + case INS_sar_N: + return true; + + default: + return false; + } +} + //------------------------------------------------------------------------ // IsLegacyMap1: Answer the question- Is this instruction on legacy-map-1 // @@ -324,7 +436,7 @@ bool emitter::IsLegacyMap1(code_t code) const if ((code & 0xFF00FF00) == 0x0F000000) { - // 4-byte, need to check if PP is a prefix. + // 4-byte, need to check if PP is prefixs BYTE prefix = (BYTE)((code & 0xFF0000) >> 16); return ((prefix == 0xF2) || (prefix == 0xF3) || (prefix == 0x66)); } @@ -647,6 +759,24 @@ bool emitter::IsRexW1EvexInstruction(instruction ins) return false; } +//------------------------------------------------------------------------ +// DoJitUseApxNDD: Answer the question: does JIT use APX NDD feature on the given instruction? +// +// Arguments: +// ins - instruction to test +// +// Return Value: +// true if JIT allows APX NDD to be applied on the instructions. +// +bool emitter::DoJitUseApxNDD(instruction ins) const +{ +#if !defined(TARGET_AMD64) + return false; +#else + return JitConfig.EnableApxNDD() && IsApxNDDEncodableInstruction(ins); +#endif +} + #ifdef TARGET_64BIT //------------------------------------------------------------------------ // AreUpperBitsZero: check if some previously emitted @@ -1257,6 +1387,179 @@ insOpts emitter::GetEmbRoundingMode(uint8_t mode) const } } +//------------------------------------------------------------------------ +// emitHandleGCrefRegs: Update GC ref related registers' liveness. +// +// Arguments: +// dst - Destination buffer. +// id - instruction descriptor to the GC ref instruction. +// +void emitter::emitHandleGCrefRegs(BYTE* dst, instrDesc* id) +{ + regNumber reg1 = id->idReg1(); // dst and src1 + regNumber reg2 = id->idReg2(); // src2 + switch (id->idInsFmt()) + { + case IF_RRD_RRD: + break; + + case IF_RWR_RRD: + { + if (emitSyncThisObjReg != REG_NA && emitIGisInProlog(emitCurIG) && reg2 == (int)REG_ARG_0) + { + // We're relocating "this" in the prolog + assert(emitComp->lvaIsOriginalThisArg(0)); + assert(emitComp->lvaTable[0].lvRegister); + assert(emitComp->lvaTable[0].GetRegNum() == reg1); + + if (emitFullGCinfo) + { + emitGCregLiveSet(id->idGCref(), genRegMask(reg1), dst, true); + break; + } + else + { + /* If emitFullGCinfo==false, the we don't use any + regPtrDsc's and so explicitly note the location + of "this" in GCEncode.cpp + */ + } + } + + emitGCregLiveUpd(id->idGCref(), reg1, dst); + break; + } + + case IF_RRW_RRD: + case IF_RWR_RRD_RRD: + { + regNumber targetReg = reg1; // dst + + // if the instructions is encoded in NDD form, + // src registers will be the 2nd and 3rd register on id. + if (id->idInsFmt() == IF_RWR_RRD_RRD) + { + reg1 = id->idReg2(); // src1 + reg2 = id->idReg3(); // src2 + } + + switch (id->idIns()) + { + /* + This must be one of the following cases: + + xor reg, reg to assign NULL + + and r1 , r2 if (ptr1 && ptr2) ... + or r1 , r2 if (ptr1 || ptr2) ... + + add r1 , r2 to compute a normal byref + sub r1 , r2 to compute a strange byref (VC only) + + */ + case INS_xor: + assert(reg1 == reg2); + emitGCregLiveUpd(id->idGCref(), targetReg, dst); + break; + + case INS_or: + case INS_and: + emitGCregDeadUpd(targetReg, dst); + break; + + case INS_add: + case INS_sub: + case INS_sub_hide: + assert(id->idGCref() == GCT_BYREF); + +#if 0 +#ifdef DEBUG + // Due to elided register moves, we can't have the following assert. + // For example, consider: + // t85 = LCL_VAR byref V01 arg1 rdx (last use) REG rdx + // /--* t85 byref + // * STORE_LCL_VAR byref V40 tmp31 rdx REG rdx + // Here, V01 is type `long` on entry, then is stored as a byref. But because + // the register allocator assigned the same register, no instruction was + // generated, and we only (currently) make gcref/byref changes in emitter GC info + // when an instruction is generated. We still generate correct GC info, as this + // instruction, if writing a GC ref even through reading a long, will go live here. + // These situations typically occur due to unsafe casting, such as with Span. + + regMaskTP regMask; + regMask = genRegMask(reg1) | genRegMask(reg2); + + // r1/r2 could have been a GCREF as GCREF + int=BYREF + // or BYREF+/-int=BYREF + assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || + ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub || ins == INS_sub_hide))); +#endif // DEBUG +#endif // 0 + + // Mark r1 as holding a byref + emitGCregLiveUpd(GCT_BYREF, targetReg, dst); + break; + + default: +#ifdef DEBUG + emitDispIns(id, false, false, false); +#endif + assert(!"unexpected GC reg update instruction"); + } + + break; + } + + case IF_RRW_RRW: + { + // This must be "xchg reg1, reg2" + assert(id->idIns() == INS_xchg); + + // If we got here, the GC-ness of the registers doesn't match, so we have to "swap" them in the GC + // register pointer mask. + + GCtype gc1, gc2; + + gc1 = emitRegGCtype(reg1); + gc2 = emitRegGCtype(reg2); + + if (gc1 != gc2) + { + // Kill the GC-info about the GC registers + + if (needsGC(gc1)) + { + emitGCregDeadUpd(reg1, dst); + } + + if (needsGC(gc2)) + { + emitGCregDeadUpd(reg2, dst); + } + + // Now, swap the info + + if (needsGC(gc1)) + { + emitGCregLiveUpd(gc1, reg2, dst); + } + + if (needsGC(gc2)) + { + emitGCregLiveUpd(gc2, reg1, dst); + } + } + break; + } + + default: +#ifdef DEBUG + emitDispIns(id, false, false, false); +#endif + assert(!"unexpected GC ref instruction format"); + } +} + //------------------------------------------------------------------------ // encodeRegAsIval: Encodes a register as an ival for use by a SIMD instruction // @@ -1343,9 +1646,23 @@ bool emitter::TakesEvexPrefix(const instrDesc* id) const return true; } + if (id->idIsEvexNfContextSet() && IsBMIInstruction(ins)) + { + // Only a few BMI instructions shall be promoted to APX-EVEX due to NF feature. + // TODO-XArch-APX: convert the check into forms like Has* as above. + return true; + } + #if defined(DEBUG) if (emitComp->DoJitStressEvexEncoding()) { + if (IsBMIInstruction(ins)) + { + // The Encoding_EVEX on some BMI instructions is tagged due to APX, + // they cannot be stressed with JitStressEvexEncoding. + return false; + } + // Requires the EVEX encoding due to STRESS mode and no change in semantics // // Some instructions, like VCMPEQW return the value in a SIMD register for @@ -1354,6 +1671,12 @@ bool emitter::TakesEvexPrefix(const instrDesc* id) const // check above so we need to still return false here to preserve semantics. return !HasKMaskRegisterDest(ins); } + + if (IsApxExtendedEvexInstruction(ins) && emitComp->DoJitStressPromotedEvexEncoding()) + { + // This path will be hit when we stress APX-EVEX and encode VEX with Extended EVEX. + return (IsBMIInstruction(ins) && HasApxNf(ins)); + } #endif // DEBUG if ((ins == INS_pslldq) || (ins == INS_psrldq)) @@ -1408,6 +1731,57 @@ bool emitter::TakesRex2Prefix(const instrDesc* id) const return false; } +//------------------------------------------------------------------------ +// TakesApxExtendedEvexPrefix: Checks if the instruction should be legacy-promoted-EVEX encoded. +// +// Arguments: +// instruction -- processor instruction to check +// +// Return Value: +// true if this instruction requires a legacy-promoted-EVEX prefix. +// +bool emitter::TakesApxExtendedEvexPrefix(const instrDesc* id) const +{ + // TODO-XArch-APX: + // Isolating legacy-promoted-EVEX case out from VEX/EVEX-promoted-EVEX, + // as the latter ones are relatively simple, providing EGPRs functionality, + instruction ins = id->idIns(); + if (!IsApxExtendedEvexInstruction(ins)) + { + return false; + } + + if (IsSimdInstruction(ins)) + { + // This check should reject any instruction not from legacy map-0 or 1. + return false; + } + + if (id->idIsNoApxEvexPromotion()) + { + return false; + } + + if (id->idIsEvexNdContextSet()) + { + return true; + } + + if (id->idIsEvexNfContextSet()) + { + return true; + } + +#if defined(DEBUG) + if (emitComp->DoJitStressPromotedEvexEncoding()) + { + return true; + } +#endif // DEBUG + + return false; +} + // Intel AVX-512 encoding is defined in "Intel 64 and ia-32 architectures software developer's manual volume 2", Section // 2.6. // Add base EVEX prefix without setting W, R, X, or B bits @@ -1450,6 +1824,10 @@ bool emitter::TakesRex2Prefix(const instrDesc* id) const #define ZBIT_IN_BYTE_EVEX_PREFIX 0x0000008000000000ULL #define uBIT_IN_BYTE_EVEX_PREFIX 0x0000040000000000ULL +#define MAP4_IN_BYTE_EVEX_PREFIX 0x4000000000000ULL +#define ND_BIT_IN_BYTE_EVEX_PREFIX 0x1000000000ULL +#define NF_BIT_IN_BYTE_EVEX_PREFIX 0x400000000ULL +#define EXTENDED_EVEX_PP_BITS 0x10000000000ULL //------------------------------------------------------------------------ // AddEvexPrefix: Add default EVEX prefix with only LL' bits set. // @@ -1459,19 +1837,72 @@ bool emitter::TakesRex2Prefix(const instrDesc* id) const // attr -- operand size // // Return Value: -// encoded code with Evex prefix. +// encoded code with EVEX prefix. // emitter::code_t emitter::AddEvexPrefix(const instrDesc* id, code_t code, emitAttr attr) { // Only AVX512 instructions require EVEX prefix - assert(IsEvexEncodableInstruction(id->idIns())); + // After APX, some instructions in legacy or VEX space will be promoted to EVEX. + instruction ins = id->idIns(); + assert(IsEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + + if (instrIsExtendedReg3opImul(ins)) + { + // the only case imul(0x68) will need EVEX prefix is EVEX.NF feature enabled. + // imul(0x68) opcode comes with ModR/M.REG byte to indicate implicit register use, + // when it is using extended registers (>= REG_R8), it comes with built-in REX prefix, + // remove them first and add the counter part in EVEX. + code &= 0xFFFFFFFF; + } // Shouldn't have already added EVEX prefix assert(!hasEvexPrefix(code)); - assert((code & DEFAULT_BYTE_EVEX_PREFIX_MASK) == 0); + assert((code & DEFAULT_BYTE_EVEX_PREFIX_MASK) == 0); + + code |= DEFAULT_BYTE_EVEX_PREFIX; + + if (IsApxExtendedEvexInstruction(ins)) + { + if (!HasEvexEncoding(ins)) + { + // Legacy-promoted insutrcions are not labeled with Encoding_EVEX. + code |= MAP4_IN_BYTE_EVEX_PREFIX; + } + + // TODO-XArch-APX: + // verify if it is actually safe to reuse the EVEX.ND with EVEX.B on instrDesc. + if (id->idIsEvexNdContextSet()) + { + code |= ND_BIT_IN_BYTE_EVEX_PREFIX; + } + + if (id->idIsEvexNfContextSet()) + { + code |= NF_BIT_IN_BYTE_EVEX_PREFIX; + } + + if (attr == EA_2BYTE) + { + code |= EXTENDED_EVEX_PP_BITS; + } + + if (instrIsExtendedReg3opImul(ins)) + { + // EVEX.R3 + // TODO-XArch-APX: + // A few side notes: based on how JIT defined IMUL, we may need to extend + // the definition to `IMUL_31` to cover EGPRs. And it can be defined in a + // similar way that opcodes comes with built-in REX2 prefix, and convert + // it to EVEX when needed with some helper functions. + code &= 0xFF7FFFFFFFFFFFFFULL; + } - code |= DEFAULT_BYTE_EVEX_PREFIX; + return code; + } + + // No APX-NDD instructions should reach code below. + assert(!IsApxExtendedEvexInstruction(ins)); if (attr == EA_32BYTE) { @@ -2022,6 +2453,14 @@ emitter::code_t emitter::AddRexWPrefix(const instrDesc* id, code_t code) } } #ifdef TARGET_AMD64 + else if (TakesApxExtendedEvexPrefix(id)) + { + // If the instruction is not VEX/EVEX encodable, and has EVEX prefix, + // then it is legacy promoted EVEX. + assert(hasEvexPrefix(code)); + assert(IsApxExtendedEvexInstruction(ins)); + return emitter::code_t(code | 0x0000800000000000ULL); + } else if (hasRex2Prefix(code)) { return emitter::code_t(code | 0x000800000000ULL); @@ -2060,13 +2499,18 @@ emitter::code_t emitter::AddRexRPrefix(const instrDesc* id, code_t code) return code & 0xFF7FFFFFFFFFFFULL; } } -#ifdef TARGET_AMD64 + else if (TakesApxExtendedEvexPrefix(id)) + { + assert(hasEvexPrefix(code)); + assert(IsApxExtendedEvexInstruction(ins)); + // R-bit is added in bit-inverted form. + return code & 0xFF7FFFFFFFFFFFFFULL; + } else if (TakesRex2Prefix(id)) { assert(IsRex2EncodableInstruction(ins)); return code |= 0xD50400000000ULL; // REX2.B3 } -#endif // TARGET_AMD64 return code | 0x4400000000ULL; } @@ -2096,13 +2540,18 @@ emitter::code_t emitter::AddRexXPrefix(const instrDesc* id, code_t code) return code & 0xFFBFFFFFFFFFFFULL; } } -#ifdef TARGET_AMD64 + else if (TakesApxExtendedEvexPrefix(id)) + { + assert(hasEvexPrefix(code)); + assert(IsApxExtendedEvexInstruction(ins)); + // X-bit is added in bit-inverted form. + return code & 0xFFBFFFFFFFFFFFFFULL; + } else if (TakesRex2Prefix(id)) { assert(IsRex2EncodableInstruction(ins)); return code |= 0xD50200000000ULL; // REX2.B3 } -#endif // TARGET_AMD64 return code | 0x4200000000ULL; } @@ -2132,13 +2581,17 @@ emitter::code_t emitter::AddRexBPrefix(const instrDesc* id, code_t code) return code & 0xFFDFFFFFFFFFFFULL; } } -#ifdef TARGET_AMD64 + else if (TakesApxExtendedEvexPrefix(id)) + { + assert(IsApxExtendedEvexInstruction(ins)); + // R-bit is added in bit-inverted form. + return code & 0xFFDFFFFFFFFFFFFFULL; + } else if (TakesRex2Prefix(id)) { assert(IsRex2EncodableInstruction(ins)); return code |= 0xD50100000000ULL; // REX2.B3 } -#endif // TARGET_AMD64 return code | 0x4100000000ULL; } @@ -2221,7 +2674,7 @@ bool isPrefix(BYTE b) // emitter::code_t emitter::emitExtractEvexPrefix(instruction ins, code_t& code) const { - assert(IsEvexEncodableInstruction(ins)); + assert(IsEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); code_t evexPrefix = (code >> 32) & 0xFFFFFFFF; code &= 0x00000000FFFFFFFFLL; @@ -2253,6 +2706,14 @@ emitter::code_t emitter::emitExtractEvexPrefix(instruction ins, code_t& code) co case 0x66: { // None of the existing BMI instructions should be EVEX encoded. + // After APX, BMI instructions can be EVEX encoded with NF feature. + if (IsBMIInstruction(ins)) + { + // if BMI instructions reaches this part, then it should be APX-EVEX. + // although the opcode of all the BMI instructions are defined with 0x66, + // but it should not, skip this check. + break; + } assert(!IsBMIInstruction(ins)); evexPrefix |= (0x01 << 8); break; @@ -2306,9 +2767,14 @@ emitter::code_t emitter::emitExtractEvexPrefix(instruction ins, code_t& code) co // 2-byte opcode with the bytes ordered as 0x0011RM22. There are 2 posibilities here: // 1. the byte in position 11 must be an escape byte. // 2. the byte in position 11 must be a map number from 0 to 7. + + // APX promoted EVEX instructions might also go onto this path, so the opcode can also be 1-byte in the form of + // 0x0000RM11. leadingBytes = (code >> 16) & 0xFF; - assert(leadingBytes == 0x0F || (emitComp->compIsaSupportedDebugOnly(InstructionSet_AVX10v2) && - leadingBytes >= 0x00 && leadingBytes <= 0x07)); + assert(leadingBytes == 0x0F || + (emitComp->compIsaSupportedDebugOnly(InstructionSet_AVX10v2) && leadingBytes >= 0x00 && + leadingBytes <= 0x07) || + (IsApxExtendedEvexInstruction(ins) && leadingBytes == 0)); code &= 0xFFFF; } @@ -2330,6 +2796,12 @@ emitter::code_t emitter::emitExtractEvexPrefix(instruction ins, code_t& code) co case 0x0F: { + if (((evexPrefix >> 16) & 0x07) == 0x04) + { + // MAP index equal to 4 indicates this instruction is a promoted legacy instruction. + // the MAP ID has been set when EVEX prefix is added. + break; + } evexPrefix |= (0x01 << 16); break; } @@ -2803,6 +3275,11 @@ unsigned emitter::emitGetRexPrefixSize(instrDesc* id, instruction ins) return 0; } + if (TakesApxExtendedEvexPrefix(id)) + { + return 0; + } + if (TakesRex2Prefix(id)) { return 0; @@ -2913,10 +3390,20 @@ unsigned emitter::emitGetAdjustedSize(instrDesc* id, code_t code) const adjustedSize++; } #ifdef TARGET_AMD64 - else if (IsRex2EncodableInstruction(ins)) + else if (IsRex2EncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)) { unsigned prefixAdjustedSize = 0; - if (TakesRex2Prefix(id)) + if (TakesApxExtendedEvexPrefix(id)) + { + prefixAdjustedSize = 4; + // If the opcode will be prefixed by EVEX, then all the map-1-legacy instructions can remove the escape + // prefix + if (IsLegacyMap1(code)) + { + prefixAdjustedSize -= 1; + } + } + else if (TakesRex2Prefix(id)) { prefixAdjustedSize = 2; // If the opcode will be prefixed by REX2, then all the map-1-legacy instructions can remove the escape @@ -2927,15 +3414,14 @@ unsigned emitter::emitGetAdjustedSize(instrDesc* id, code_t code) const } } - adjustedSize = prefixAdjustedSize; - emitAttr attr = id->idOpSize(); - - if ((attr == EA_2BYTE) && (ins != INS_movzx) && (ins != INS_movsx)) + if ((attr == EA_2BYTE) && (ins != INS_movzx) && (ins != INS_movsx) && !TakesApxExtendedEvexPrefix(id)) { // Most 16-bit operand instructions will need a 0x66 prefix. - adjustedSize++; + prefixAdjustedSize++; } + + adjustedSize = prefixAdjustedSize; } #endif // TARGET_AMD64 else @@ -2987,6 +3473,14 @@ unsigned emitter::emitGetPrefixSize(instrDesc* id, code_t code, bool includeRexP if (includeRexPrefixSize && hasRexPrefix(code)) { + if (instrIsExtendedReg3opImul(id->idIns()) && TakesApxExtendedEvexPrefix(id)) + { + // there is a special case when calculating the size of IMUL with APX-EVEX, + // IMUL_08 or beyond will have a built-in REX prefix with its opcode, + // so it will hit this branch, but when IMUL is encoded with APX-EVEX, + // the size of REX is included in the prefix size, where should be calculated outside. + return 0; + } return 1; } @@ -3628,7 +4122,7 @@ inline unsigned emitter::insEncodeReg012(const instrDesc* id, regNumber reg, emi { // We are assuming that we only use/encode SPL, BPL, SIL and DIL // not the corresponding AH, CH, DH, or BH - *code = hasRex2Prefix(*code) ? *code : AddRexPrefix(ins, *code); // REX + *code = (hasRex2Prefix(*code) || hasEvexPrefix(*code)) ? *code : AddRexPrefix(ins, *code); // REX } #endif // TARGET_AMD64 @@ -3668,7 +4162,7 @@ inline unsigned emitter::insEncodeReg345(const instrDesc* id, regNumber reg, emi } if (false /*reg >= REG_R16 && reg <= REG_R31*/) { - // seperate the encoding for REX2.R3/R4, REX2.R3 will be handled in `AddRexRPrefix`. + // Seperate the encoding for REX2.R3/R4, REX2.R3 will be handled in `AddRexRPrefix`. assert(TakesRex2Prefix(id)); *code |= 0x004000000000ULL; // REX2.R4 } @@ -3677,7 +4171,7 @@ inline unsigned emitter::insEncodeReg345(const instrDesc* id, regNumber reg, emi { // We are assuming that we only use/encode SPL, BPL, SIL and DIL // not the corresponding AH, CH, DH, or BH - *code = hasRex2Prefix(*code) ? *code : AddRexPrefix(ins, *code); // REX + *code = (hasRex2Prefix(*code) || hasEvexPrefix(*code)) ? *code : AddRexPrefix(ins, *code); // REX } #endif // TARGET_AMD64 @@ -3697,7 +4191,7 @@ inline emitter::code_t emitter::insEncodeReg3456(const instrDesc* id, regNumber instruction ins = id->idIns(); assert(reg < REG_STK); - assert(IsVexOrEvexEncodableInstruction(ins)); + assert(IsVexOrEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); assert(hasVexOrEvexPrefix(code)); // Get 4-bit register encoding @@ -3744,6 +4238,25 @@ inline emitter::code_t emitter::insEncodeReg3456(const instrDesc* id, regNumber return code ^ regBits; } } + else + { + assert(TakesApxExtendedEvexPrefix(id)); + assert(hasEvexPrefix(code)); +#if defined(TARGET_AMD64) + // TODO-XARCH-AVX512 I don't like that we redefine regBits on the EVEX case. + // Rather see these paths cleaned up. + regBits = HighAwareRegEncoding(reg); + + if (false /*reg >= REG_R16 && reg <= REG_R31*/) + { + // Have to set the EVEX V' bit + code = AddEvexVPrimePrefix(code); + } +#endif + // Shift count = 5-bytes of opcode + 0-2 bits for EVEX + regBits <<= 43; + return code ^ regBits; + } return code ^ regBits; } @@ -3779,7 +4292,7 @@ inline unsigned emitter::insEncodeRegSIB(const instrDesc* id, regNumber reg, cod } if (false /*reg >= REG_R16 && reg <= REG_R31*/) { - // seperate the encoding for REX2.X3/X4, REX2.X3 will be handled in `AddRexXPrefix`. + // Separate the encoding for REX2.X3/X4, REX2.X3 will be handled in `AddRexXPrefix`. assert(TakesRex2Prefix(id)); *code |= 0x002000000000ULL; // REX2.X4 } @@ -4175,7 +4688,8 @@ inline UNATIVE_OFFSET emitter::emitInsSizeRR(instrDesc* id) if ((code & 0xFF00) != 0) { - sz += IsSimdInstruction(ins) ? emitInsSize(id, code, includeRexPrefixSize) : 5; + sz += (IsSimdInstruction(ins) || TakesApxExtendedEvexPrefix(id)) ? emitInsSize(id, code, includeRexPrefixSize) + : 5; } else { @@ -4303,7 +4817,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSVCalcDisp(instrDesc* id, code_t code, assert(emitComp->lvaTempsHaveLargerOffsetThanVars()); // Check whether we can use compressed displacement if EVEX. - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { bool compressedFitsInByte = false; TryEvexCompressDisp8Byte(id, ssize_t(offs), &compressedFitsInByte); @@ -4347,7 +4861,7 @@ inline UNATIVE_OFFSET emitter::emitInsSizeSVCalcDisp(instrDesc* id, code_t code, #endif // !FEATURE_FIXED_OUT_ARGS bool useSmallEncoding = false; - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { TryEvexCompressDisp8Byte(id, ssize_t(offs), &useSmallEncoding); } @@ -4514,7 +5028,7 @@ UNATIVE_OFFSET emitter::emitInsSizeAM(instrDesc* id, code_t code) } else { - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { dsp = TryEvexCompressDisp8Byte(id, dsp, &dspInByte); } @@ -5459,17 +5973,37 @@ void emitter::emitInsStoreLcl(instruction ins, emitAttr attr, GenTreeLclVarCommo // attr - the instruction operand size // dst - the destination and first source operand // src - the second source operand +// targetReg - target register of this binary node (only used for APX-NDD form) // // Assumptions: // i) caller of this routine needs to call genConsumeReg() // ii) caller of this routine needs to call genProduceReg() -regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src) +regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src, regNumber targetReg) { // We can only have one memory operand and only src can be a constant operand // However, the handling for a given operand type (mem, cns, or other) is fairly // consistent regardless of whether they are src or dst. As such, we will find // the type of each operand and only check them against src/dst where relevant. + const bool useNDD = UsePromotedEVEXEncoding() && (targetReg != REG_NA); +#if !defined(TARGET_AMD64) + // APX does not support 32-bit system. + assert(!useNDD); +#else + if (useNDD) + { + assert(IsApxNDDEncodableInstruction(ins)); + // targetReg has to be an actual register if using NDD. + assert(targetReg < REG_STK); + // make sure target register is not either of the src registers. + assert(dst->isUsedFromReg()); + regNumber dstreg = dst->GetRegNum(); + regNumber srcreg = src->isUsedFromReg() ? src->GetRegNum() : REG_NA; + assert(targetReg != dstreg); + assert(targetReg != srcreg); + } +#endif + GenTree* memOp = nullptr; GenTree* cnsOp = nullptr; GenTree* otherOp = nullptr; @@ -5481,6 +6015,9 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G assert(dst->isUsedFromMemory() || (dst->GetRegNum() == REG_NA) || instrIs3opImul(ins)); assert(!src->isUsedFromMemory()); + // APX code cannot hit this path. + assert(!useNDD); + memOp = dst; if (src->isContained()) @@ -5588,6 +6125,9 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G assert(otherOp == nullptr); assert(src->IsCnsIntOrI()); + // APX code cannot hit this path. + assert(!useNDD); + id = emitNewInstrAmdCns(attr, memIndir->Offset(), (int)src->AsIntConCommon()->IconValue()); } else @@ -5605,6 +6145,13 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G assert(id != nullptr); id->idIns(ins); // Set the instruction. + if (useNDD) + { + assert(memOp == src); + id->idReg1(targetReg); + id->idReg2(dst->GetRegNum()); + id->idSetEvexNdContext(); + } // Determine the instruction format insFormat fmt = IF_NONE; @@ -5620,12 +6167,13 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G } else { - fmt = emitInsModeFormat(ins, IF_RRD_ARD); + fmt = useNDD ? emitInsModeFormat(ins, IF_RWR_RRD_ARD) : emitInsModeFormat(ins, IF_RRD_ARD); } } else { assert(memOp == dst); + assert(!useNDD); if (cnsOp != nullptr) { @@ -5664,6 +6212,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G else { assert(memOp == dst); + assert(!useNDD); if (cnsOp != nullptr) { @@ -5686,7 +6235,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G dispIns(id); emitCurIGsize += sz; - return (memOp == src) ? dst->GetRegNum() : REG_NA; + return (memOp == src) ? (useNDD ? targetReg : dst->GetRegNum()) : REG_NA; } } } @@ -5734,15 +6283,24 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G } else { - // src is a stack based local variable - // dst is a register - emitIns_R_S(ins, attr, dst->GetRegNum(), varNum, offset); + if (useNDD) + { + emitIns_R_R_S(ins, attr, targetReg, dst->GetRegNum(), varNum, offset, INS_OPTS_EVEX_nd); + return targetReg; + } + else + { + // src is a stack based local variable + // dst is a register + emitIns_R_S(ins, attr, dst->GetRegNum(), varNum, offset); + } } } else { assert(memOp == dst); assert((dst->GetRegNum() == REG_NA) || dst->IsRegOptional()); + assert(!useNDD); if (cnsOp != nullptr) { @@ -5774,10 +6332,20 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G { assert(!dst->isContained()); GenTreeIntConCommon* intCns = src->AsIntConCommon(); - emitIns_R_I(ins, attr, dst->GetRegNum(), intCns->IconValue()); + + if (useNDD) + { + emitIns_R_R_I(ins, attr, targetReg, dst->GetRegNum(), (int)intCns->IconValue(), INS_OPTS_EVEX_nd); + return targetReg; + } + else + { + emitIns_R_I(ins, attr, dst->GetRegNum(), intCns->IconValue()); + } } else { + assert(!useNDD); assert(src->IsCnsFltOrDbl()); GenTreeDblCon* dblCns = src->AsDblCon(); @@ -5796,7 +6364,15 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G } else { - emitIns_R_R(ins, attr, dst->GetRegNum(), src->GetRegNum()); + if (useNDD) + { + emitIns_R_R_R(ins, attr, targetReg, dst->GetRegNum(), src->GetRegNum(), INS_OPTS_EVEX_nd); + return targetReg; + } + else + { + emitIns_R_R(ins, attr, dst->GetRegNum(), src->GetRegNum()); + } } } @@ -5947,7 +6523,7 @@ void emitter::emitInsRMW(instruction ins, emitAttr attr, GenTreeStoreInd* storeI * Add an instruction referencing a single register. */ -void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) +void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts instOptions /* = INS_OPTS_NONE */) { emitAttr size = EA_SIZE(attr); @@ -6023,6 +6599,8 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) id->idInsFmt(fmt); id->idReg1(reg); + SetEvexNfIfNeeded(id, instOptions); + // Vex bytes sz += emitGetAdjustedSize(id, insEncodeMRreg(id, reg, attr, insCodeMR(ins))); @@ -6095,10 +6673,11 @@ void emitter::emitStoreSimd12ToLclOffset(unsigned varNum, unsigned offset, regNu * Add an instruction referencing a register and a constant. */ -void emitter::emitIns_R_I(instruction ins, - emitAttr attr, - regNumber reg, - ssize_t val DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) +void emitter::emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + ssize_t val, + insOpts instOptions DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { emitAttr size = EA_SIZE(attr); @@ -6238,6 +6817,8 @@ void emitter::emitIns_R_I(instruction ins, id->idDebugOnlyInfo()->idMemCookie = targetHandle; #endif + SetEvexNfIfNeeded(id, instOptions); + if (isSimdInsAndValInByte) { bool includeRexPrefixSize = true; @@ -6251,8 +6832,14 @@ void emitter::emitIns_R_I(instruction ins, sz += emitInsSize(id, insCodeMI(ins), includeRexPrefixSize); } - sz += emitGetAdjustedSize(id, insCodeMI(ins)); +#ifdef TARGET_AMD64 + if (reg == REG_EAX && !instrIs3opImul(ins) && TakesApxExtendedEvexPrefix(id)) + { + // ACC form is not promoted into EVEX space, need to emit with MI form. + sz += 1; + } +#endif // TARGET_AMD64 // Do we need a REX prefix for AMD64? We need one if we are using any extended register (REX.R), or if we have a // 64-bit sized operand (REX.W). Note that IMUL in our encoding is special, with a "built-in", implicit, target @@ -7026,6 +7613,14 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum id->idReg1(reg1); id->idReg2(reg2); + SetEvexNdIfNeeded(id, instOptions); + SetEvexNfIfNeeded(id, instOptions); + + if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins)) + { + id->idInsFmt(IF_RWR_RRD); + } + if ((instOptions & INS_OPTS_EVEX_b_MASK) != INS_OPTS_NONE) { // if EVEX.b needs to be set in this path, then it should be embedded rounding. @@ -7079,6 +7674,32 @@ void emitter::emitIns_R_R_I( assert((instOptions & INS_OPTS_EVEX_b_MASK) == 0); SetEvexEmbMaskIfNeeded(id, instOptions); + SetEvexNdIfNeeded(id, instOptions); + + if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins)) + { + // need to fix the instruction opcode for legacy instructions as they has different opcode for RI form. + code = insCodeMI(ins); + // need to fix the instructions format for NDD legacy instructions. + insFormat fmt; + switch (ins) + { + case INS_shl_N: + case INS_shr_N: + case INS_sar_N: + case INS_ror_N: + case INS_rol_N: + case INS_rcr_N: + case INS_rcl_N: + fmt = IF_RWR_RRD_SHF; + break; + + default: + fmt = IF_RWR_RRD_CNS; + break; + } + id->idInsFmt(fmt); + } UNATIVE_OFFSET sz = emitInsSizeRR(id, code, ival); id->idCodeSize(sz); @@ -7087,7 +7708,7 @@ void emitter::emitIns_R_R_I( emitCurIGsize += sz; } -void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs) +void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs, insOpts instOptions) { assert(ins == INS_prefetcht0 || ins == INS_prefetcht1 || ins == INS_prefetcht2 || ins == INS_prefetchnta || ins == INS_inc || ins == INS_dec); @@ -7100,6 +7721,11 @@ void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int off id->idAddr()->iiaAddrMode.amBaseReg = base; id->idAddr()->iiaAddrMode.amIndxReg = REG_NA; + if ((instOptions & INS_OPTS_EVEX_NoApxPromotion) != 0) + { + id->idSetNoApxEvexPromotion(); + } + UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeMR(ins)); id->idCodeSize(sz); @@ -7443,8 +8069,8 @@ void emitter::emitIns_R_R_C(instruction ins, void emitter::emitIns_R_R_R( instruction ins, emitAttr attr, regNumber targetReg, regNumber reg1, regNumber reg2, insOpts instOptions) { - assert(IsSimdInstruction(ins)); - assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins)); + assert(IsSimdInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins) || IsApxExtendedEvexInstruction(ins)); instrDesc* id = emitNewInstr(attr); id->idIns(ins); @@ -7460,6 +8086,14 @@ void emitter::emitIns_R_R_R( id->idSetEvexbContext(instOptions); } SetEvexEmbMaskIfNeeded(id, instOptions); + SetEvexNdIfNeeded(id, instOptions); + SetEvexNfIfNeeded(id, instOptions); + + if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins)) + { + // need to fix the instructions format for NDD legacy instructions. + id->idInsFmt(IF_RWR_RRD_RRD); + } UNATIVE_OFFSET sz = emitInsSizeRR(id, insCodeRM(ins)); id->idCodeSize(sz); @@ -7471,8 +8105,8 @@ void emitter::emitIns_R_R_R( void emitter::emitIns_R_R_S( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int varx, int offs, insOpts instOptions) { - assert(IsSimdInstruction(ins)); - assert(IsThreeOperandAVXInstruction(ins)); + assert(IsSimdInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + assert(IsThreeOperandAVXInstruction(ins) || IsApxExtendedEvexInstruction(ins)); instrDesc* id = emitNewInstr(attr); @@ -7484,6 +8118,12 @@ void emitter::emitIns_R_R_S( SetEvexBroadcastIfNeeded(id, instOptions); SetEvexEmbMaskIfNeeded(id, instOptions); + SetEvexNdIfNeeded(id, instOptions); + + if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins)) + { + id->idInsFmt(IF_RWR_RRD_SRD); + } #ifdef DEBUG id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs; @@ -8156,7 +8796,7 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu * The following adds instructions referencing address modes. */ -void emitter::emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber reg, int disp) +void emitter::emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber reg, int disp, insOpts instOptions) { assert((CodeGen::instIsFP(ins) == false) && (EA_SIZE(attr) <= EA_8BYTE)); @@ -8202,6 +8842,10 @@ void emitter::emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber re id->idAddr()->iiaAddrMode.amBaseReg = reg; id->idAddr()->iiaAddrMode.amIndxReg = REG_NA; + if ((instOptions & INS_OPTS_EVEX_NoApxPromotion) != 0) + { + id->idSetNoApxEvexPromotion(); + } assert(emitGetInsAmdAny(id) == disp); // make sure "disp" is stored properly @@ -8310,9 +8954,10 @@ void emitter::emitIns_R_AI(instruction ins, emitCurIGsize += sz; } -void emitter::emitIns_AR_R(instruction ins, emitAttr attr, regNumber reg, regNumber base, cnsval_ssize_t disp) +void emitter::emitIns_AR_R( + instruction ins, emitAttr attr, regNumber reg, regNumber base, cnsval_ssize_t disp, insOpts instOptions) { - emitIns_ARX_R(ins, attr, reg, base, REG_NA, 1, disp); + emitIns_ARX_R(ins, attr, reg, base, REG_NA, 1, disp, instOptions); } //------------------------------------------------------------------------ @@ -8595,8 +9240,14 @@ void emitter::emitIns_R_ARX( emitCurIGsize += sz; } -void emitter::emitIns_ARX_R( - instruction ins, emitAttr attr, regNumber reg, regNumber base, regNumber index, unsigned scale, cnsval_ssize_t disp) +void emitter::emitIns_ARX_R(instruction ins, + emitAttr attr, + regNumber reg, + regNumber base, + regNumber index, + unsigned scale, + cnsval_ssize_t disp, + insOpts instOptions) { UNATIVE_OFFSET sz; instrDesc* id = emitNewInstrAmd(attr, disp); @@ -8622,6 +9273,10 @@ void emitter::emitIns_ARX_R( id->idAddr()->iiaAddrMode.amBaseReg = base; id->idAddr()->iiaAddrMode.amIndxReg = index; id->idAddr()->iiaAddrMode.amScale = emitEncodeScale(scale); + if ((instOptions & INS_OPTS_EVEX_NoApxPromotion) != 0) + { + id->idSetNoApxEvexPromotion(); + } assert(emitGetInsAmdAny(id) == disp); // make sure "disp" is stored properly @@ -9606,6 +10261,74 @@ void emitter::emitIns_S(instruction ins, emitAttr attr, int varx, int offs) emitAdjustStackDepthPushPop(ins); } +void emitter::emitIns_BASE_R_R(instruction ins, emitAttr attr, regNumber op1Reg, regNumber op2Reg) +{ + if (DoJitUseApxNDD(ins) && (op1Reg != op2Reg)) + { + // If APX-EVEX-NDD is available and needed, emit instructions in: + // ins dst, src + emitIns_R_R(ins, attr, op1Reg, op2Reg, INS_OPTS_EVEX_nd); + } + else + { + // mov dst, src + // ins dst + emitIns_Mov(INS_mov, attr, op1Reg, op2Reg, /*canSkip*/ true); + emitIns_R(ins, attr, op1Reg); + } +} + +void emitter::emitIns_BASE_R_R_I(instruction ins, emitAttr attr, regNumber op1Reg, regNumber op2Reg, int ival) +{ + if (DoJitUseApxNDD(ins) && (op1Reg != op2Reg)) + { + // If APX-EVEX-NDD is available and needed, emit instructions in: + // ins dst, src, cns + if (IsShiftInstruction(ins) && ival == 1) + { + emitIns_R_R(ins, attr, op1Reg, op2Reg, INS_OPTS_EVEX_nd); + } + else + { + emitIns_R_R_I(ins, attr, op1Reg, op2Reg, ival, INS_OPTS_EVEX_nd); + } + } + else + { + // mov dst, src + // ins dst, cns + emitIns_Mov(INS_mov, attr, op1Reg, op2Reg, /*canSkip*/ true); + if (IsShiftInstruction(ins) && ival == 1) + { + emitIns_R(ins, attr, op1Reg); + } + else + { + emitIns_R_I(ins, attr, op1Reg, ival); + } + } +} + +regNumber emitter::emitIns_BASE_R_R_RM( + instruction ins, emitAttr attr, regNumber targetReg, GenTree* treeNode, GenTree* regOp, GenTree* rmOp) +{ + bool requiresOverflowCheck = treeNode->gtOverflowEx(); + regNumber r = REG_NA; + assert(regOp->isUsedFromReg()); + + if (DoJitUseApxNDD(ins) && regOp->GetRegNum() != targetReg) + { + r = emitInsBinary(ins, attr, regOp, rmOp, targetReg); + } + else + { + emitIns_Mov(INS_mov, attr, targetReg, regOp->GetRegNum(), /*canSkip*/ true); + r = emitInsBinary(ins, attr, treeNode, rmOp); + } + + return r; +} + //---------------------------------------------------------------------------------------- // IsRedundantStackMov: // Check if the current `mov` instruction is redundant and can be omitted when dealing with Load/Store from stack. @@ -9743,6 +10466,7 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber ireg, int va SetEvexBroadcastIfNeeded(id, instOptions); SetEvexEmbMaskIfNeeded(id, instOptions); + SetEvexNfIfNeeded(id, instOptions); UNATIVE_OFFSET sz = emitInsSizeSV(id, insCodeRM(ins), varx, offs); id->idCodeSize(sz); @@ -11447,6 +12171,13 @@ void emitter::emitDispEmbRounding(instrDesc* id) const { return; } + + if (IsApxExtendedEvexInstruction(id->idIns())) + { + // Apx-Evex.nd shared the same bit(s) with Evex.b, + // for ndd case, we don't need to display any thing special. + return; + } assert(!id->idHasMem()); unsigned roundingMode = id->idGetEvexbContext(); if (roundingMode == 1) @@ -11627,6 +12358,14 @@ void emitter::emitDispIns( /* Display the instruction name */ +#ifdef TARGET_AMD64 + if (IsApxNFEncodableInstruction(id->idIns()) && id->idIsEvexNfContextSet()) + { + // print the EVEX.NF indication in psudeo prefix style. + printf("{nf} "); + } +#endif // TARGET_AMD64 + sstr = codeGen->genInsDisplayName(id); printf(" %-9s", sstr); @@ -12377,6 +13116,20 @@ void emitter::emitDispIns( break; } + case INS_rol: + case INS_ror: + case INS_rcl: + case INS_rcr: + case INS_shl: + case INS_shr: + case INS_sar: + { + printf("%s", emitRegName(id->idReg1(), attr)); + printf(", %s", emitRegName(id->idReg2(), attr)); + emitDispShift(ins, (BYTE)0); + break; + } + default: { printf("%s", emitRegName(id->idReg1(), attr)); @@ -12394,8 +13147,8 @@ void emitter::emitDispIns( case IF_RRW_RRD_RRD: case IF_RWR_RWR_RRD: { - assert(IsVexOrEvexEncodableInstruction(ins)); - assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins)); + assert(IsVexOrEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + assert(IsThreeOperandAVXInstruction(ins) || IsKInstruction(ins) || IsApxExtendedEvexInstruction(ins)); regNumber reg2 = id->idReg2(); regNumber reg3 = id->idReg3(); @@ -12630,6 +13383,19 @@ void emitter::emitDispIns( break; } + case IF_RWR_RRD_SHF: + { + assert(IsApxExtendedEvexInstruction(id->idIns())); + printf("%s, %s", emitRegName(id->idReg1(), attr), emitRegName(id->idReg2(), attr)); + + emitGetInsCns(id, &cnsVal); + val = cnsVal.cnsVal; + + emitDispShift(ins, (BYTE)val); + + break; + } + case IF_RRD_MRD: case IF_RWR_MRD: case IF_RRW_MRD: @@ -13578,12 +14344,21 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) break; case EA_2BYTE: - - /* Output a size prefix for a 16-bit operand */ - - dst += emitOutputByte(dst, 0x66); - + { + // Output a size prefix for a 16-bit operand + if (TakesApxExtendedEvexPrefix(id)) + { + assert(IsApxExtendedEvexInstruction(ins)); + assert(hasEvexPrefix(code)); + // Evex.pp should already be added when adding the prefix. + assert((code & EXTENDED_EVEX_PP_BITS) != 0); + } + else + { + dst += emitOutputByte(dst, 0x66); + } FALLTHROUGH; + } case EA_4BYTE: #ifdef TARGET_AMD64 @@ -13627,7 +14402,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) } else { - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { dsp = TryEvexCompressDisp8Byte(id, dsp, &dspInByte); } @@ -14165,6 +14940,14 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst); break; + case IF_RWR_RRD_ARD: + assert(((id->idGCref() == GCT_BYREF) && + (ins == INS_add || ins == INS_sub || ins == INS_sub_hide || insIsCMOV(ins))) || + ((id->idGCref() == GCT_GCREF) && insIsCMOV(ins))); + assert(id->idIsEvexNdContextSet()); + emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst); + break; + case IF_ARD_RRD: case IF_AWR_RRD: break; @@ -14411,25 +15194,45 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) switch (size) { case EA_1BYTE: +#ifdef TARGET_AMD64 + assert((ins != INS_lzcnt_apx) && (ins != INS_tzcnt_apx) && (ins != INS_popcnt_apx)); +#endif // TARGET_AMD64 break; case EA_2BYTE: // Output a size prefix for a 16-bit operand - dst += emitOutputByte(dst, 0x66); + { + if (!TakesApxExtendedEvexPrefix(id)) + { + dst += emitOutputByte(dst, 0x66); + } + } FALLTHROUGH; case EA_4BYTE: + code |= 0x01; + break; + #ifdef TARGET_AMD64 case EA_8BYTE: -#endif // TARGET_AMD64 - /* Set the 'w' size bit to indicate 32-bit operation * Note that incrementing "code" for INS_call (0xFF) would * overflow, whereas setting the lower bit to 1 just works out */ - - code |= 0x01; - break; + { + if (TakesApxExtendedEvexPrefix(id)) + { + assert(hasEvexPrefix(code)); + code = AddRexWPrefix(id, code); + } + if ((ins != INS_lzcnt_apx) && (ins != INS_tzcnt_apx) && (ins != INS_popcnt_apx)) + // These instructions do not support 1-byte inputs and the opcode is exact. + { + code |= 0x01; + } + break; + } +#endif // TARGET_AMD64 #ifdef TARGET_X86 case EA_8BYTE: @@ -14463,7 +15266,7 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) // function, to which the remainder of the emitter logic should handle properly. // TODO-XARCH-AVX512 : embedded broadcast might change this int dspAsByte = dsp; - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { dspAsByte = int(TryEvexCompressDisp8Byte(id, ssize_t(dsp), &dspInByte)); } @@ -14517,7 +15320,7 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) // TODO-XARCH-AVX512 : working to wrap up all adjusted disp8 compression logic into the following // function, to which the remainder of the emitter logic should handle properly. // TODO-XARCH-AVX512 : embedded broadcast might change this - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { dspAsByte = int(TryEvexCompressDisp8Byte(id, ssize_t(dsp), &dspInByte)); } @@ -14664,6 +15467,15 @@ BYTE* emitter::emitOutputSV(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst); break; + case IF_RWR_RRD_SRD: // Register Read/Write, Stack Read (So we need to update GC live for register) + + // reg could have been a GCREF as GCREF + int=BYREF + // or BYREF+/-int=BYREF + assert(id->idGCref() == GCT_BYREF && (ins == INS_add || ins == INS_sub || ins == INS_sub_hide)); + assert(id->idIsEvexNdContextSet()); + emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst); + break; + case IF_SRW_CNS: case IF_SRW_RRD: case IF_SRW_RRW: @@ -15249,7 +16061,7 @@ BYTE* emitter::emitOutputR(BYTE* dst, instrDesc* id) // Can't use the compact form, use the long form ins = (instruction)(ins + 1); - if (size == EA_2BYTE) + if (size == EA_2BYTE && !TakesApxExtendedEvexPrefix(id)) { // Output a size prefix for a 16-bit operand dst += emitOutputByte(dst, 0x66); @@ -15262,10 +16074,7 @@ BYTE* emitter::emitOutputR(BYTE* dst, instrDesc* id) code |= 0x1; } - if (TakesRex2Prefix(id)) - { - code = AddRex2Prefix(ins, code); - } + code = AddX86PrefixIfNeeded(id, code, size); if (TakesRexWPrefix(id)) { @@ -15400,23 +16209,22 @@ BYTE* emitter::emitOutputR(BYTE* dst, instrDesc* id) default: assert(id->idGCref() == GCT_NONE); - - code = insEncodeMRreg(id, reg, size, insCodeMR(ins)); + code = insCodeMR(ins); + code = AddX86PrefixIfNeeded(id, code, size); + code = insEncodeMRreg(id, reg, size, code); if (size != EA_1BYTE) { // Set the 'w' bit to get the large version code |= 0x1; - if (size == EA_2BYTE) + if (size == EA_2BYTE && !TakesApxExtendedEvexPrefix(id)) { // Output a size prefix for a 16-bit operand dst += emitOutputByte(dst, 0x66); } } - code = AddX86PrefixIfNeeded(id, code, size); - if (TakesRexWPrefix(id)) { code = AddRexWPrefix(id, code); @@ -15553,7 +16361,11 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) } #ifdef FEATURE_HW_INTRINSICS else if ((ins == INS_bsf) || (ins == INS_bsr) || (ins == INS_crc32) || (ins == INS_lzcnt) || (ins == INS_popcnt) || - (ins == INS_tzcnt)) + (ins == INS_tzcnt) +#ifdef TARGET_AMD64 + || (ins == INS_lzcnt_apx) || (ins == INS_tzcnt_apx) || (ins == INS_popcnt_apx) +#endif // TARGET_AMD64 + ) { assert(hasCodeRM(ins) && !hasCodeMI(ins) && !hasCodeMR(ins)); code = insCodeRM(ins); @@ -15564,7 +16376,7 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) code |= 0x0100; } - if (size == EA_2BYTE) + if (size == EA_2BYTE && !TakesApxExtendedEvexPrefix(id)) { assert(ins == INS_crc32); dst += emitOutputByte(dst, 0x66); @@ -15577,15 +16389,21 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) #endif // FEATURE_HW_INTRINSICS else { + // TODO-XArch-APX: + // some instructions with NDD form might go into this path with EVEX prefix. + // might consider having a separate path with checks like: TakesApxExtendedEvexPrefix + // essentially, we need to make it clear on the priority and necessity of REX2 and EVEX: + // REX2 is needed iff EGPRs are involved. + // EVEX is needed when NDD, NF or other features are involved. + // So the logic should be: + // checking if those new features are used, then check if EGPRs are involved. + // EGPRs will be supported by EVEX anyway, so don't need to check in the first place. assert(!TakesSimdPrefix(id)); code = insCodeMR(ins); - if (TakesRex2Prefix(id)) - { - code = AddRex2Prefix(ins, code); - } + code = AddX86PrefixIfNeeded(id, code, size); code = insEncodeMRreg(id, code); - if (ins != INS_test) + if (ins != INS_test && !IsShiftInstruction(ins)) { code |= 2; } @@ -15599,7 +16417,17 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) case EA_2BYTE: // Output a size prefix for a 16-bit operand - dst += emitOutputByte(dst, 0x66); + if (TakesApxExtendedEvexPrefix(id)) + { + assert(IsApxExtendedEvexInstruction(ins)); + assert(hasEvexPrefix(code)); + // Evex.pp should already be added when adding the prefix. + assert((code & EXTENDED_EVEX_PP_BITS) != 0); + } + else + { + dst += emitOutputByte(dst, 0x66); + } FALLTHROUGH; case EA_4BYTE: @@ -15650,8 +16478,18 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) } } - unsigned regCode = insEncodeReg345(id, regFor345Bits, size, &code); - regCode |= insEncodeReg012(id, regFor012Bits, size, &code); + unsigned regCode; + if (!id->idIsEvexNdContextSet() || !IsApxNDDEncodableInstruction(ins)) + { + regCode = insEncodeReg345(id, regFor345Bits, size, &code); + regCode |= insEncodeReg012(id, regFor012Bits, size, &code); + } + else + { + // unary ins with NDD form use Evex.vvvvv for dst, and ModRM.rm for src + code = insEncodeReg3456(id, reg1, size, code); + regCode = insEncodeReg012(id, reg2, size, &code); + } if (TakesSimdPrefix(id)) { @@ -15709,6 +16547,11 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) dst += emitOutputByte(dst, (code >> 8) & 0xFF); dst += emitOutputByte(dst, (0xC0 | regCode)); } + else if (IsApxNDDEncodableInstruction(ins) && id->idIsEvexNdContextSet()) + { + dst += emitOutputByte(dst, (code & 0xFF)); + dst += emitOutputByte(dst, (0xC0 | regCode | (code >> 8))); + } else { dst += emitOutputWord(dst, code); @@ -15718,155 +16561,7 @@ BYTE* emitter::emitOutputRR(BYTE* dst, instrDesc* id) // Does this instruction operate on a GC ref value? if (id->idGCref()) { - switch (id->idInsFmt()) - { - case IF_RRD_RRD: - break; - - case IF_RWR_RRD: - { - if (emitSyncThisObjReg != REG_NA && emitIGisInProlog(emitCurIG) && reg2 == (int)REG_ARG_0) - { - // We're relocating "this" in the prolog - assert(emitComp->lvaIsOriginalThisArg(0)); - assert(emitComp->lvaTable[0].lvRegister); - assert(emitComp->lvaTable[0].GetRegNum() == reg1); - - if (emitFullGCinfo) - { - emitGCregLiveSet(id->idGCref(), genRegMask(reg1), dst, true); - break; - } - else - { - /* If emitFullGCinfo==false, the we don't use any - regPtrDsc's and so explicitly note the location - of "this" in GCEncode.cpp - */ - } - } - - emitGCregLiveUpd(id->idGCref(), reg1, dst); - break; - } - - case IF_RRW_RRD: - { - switch (id->idIns()) - { - /* - This must be one of the following cases: - - xor reg, reg to assign NULL - - and r1 , r2 if (ptr1 && ptr2) ... - or r1 , r2 if (ptr1 || ptr2) ... - - add r1 , r2 to compute a normal byref - sub r1 , r2 to compute a strange byref (VC only) - - */ - case INS_xor: - assert(reg1 == reg2); - emitGCregLiveUpd(id->idGCref(), reg1, dst); - break; - - case INS_or: - case INS_and: - emitGCregDeadUpd(reg1, dst); - break; - - case INS_add: - case INS_sub: - case INS_sub_hide: - assert(id->idGCref() == GCT_BYREF); - -#if 0 -#ifdef DEBUG - // Due to elided register moves, we can't have the following assert. - // For example, consider: - // t85 = LCL_VAR byref V01 arg1 rdx (last use) REG rdx - // /--* t85 byref - // * STORE_LCL_VAR byref V40 tmp31 rdx REG rdx - // Here, V01 is type `long` on entry, then is stored as a byref. But because - // the register allocator assigned the same register, no instruction was - // generated, and we only (currently) make gcref/byref changes in emitter GC info - // when an instruction is generated. We still generate correct GC info, as this - // instruction, if writing a GC ref even through reading a long, will go live here. - // These situations typically occur due to unsafe casting, such as with Span. - - regMaskTP regMask; - regMask = genRegMask(reg1) | genRegMask(reg2); - - // r1/r2 could have been a GCREF as GCREF + int=BYREF - // or BYREF+/-int=BYREF - assert(((regMask & emitThisGCrefRegs) && (ins == INS_add)) || - ((regMask & emitThisByrefRegs) && (ins == INS_add || ins == INS_sub || ins == INS_sub_hide))); -#endif // DEBUG -#endif // 0 - - // Mark r1 as holding a byref - emitGCregLiveUpd(GCT_BYREF, reg1, dst); - break; - - default: -#ifdef DEBUG - emitDispIns(id, false, false, false); -#endif - assert(!"unexpected GC reg update instruction"); - } - - break; - } - - case IF_RRW_RRW: - { - // This must be "xchg reg1, reg2" - assert(id->idIns() == INS_xchg); - - // If we got here, the GC-ness of the registers doesn't match, so we have to "swap" them in the GC - // register pointer mask. - - GCtype gc1, gc2; - - gc1 = emitRegGCtype(reg1); - gc2 = emitRegGCtype(reg2); - - if (gc1 != gc2) - { - // Kill the GC-info about the GC registers - - if (needsGC(gc1)) - { - emitGCregDeadUpd(reg1, dst); - } - - if (needsGC(gc2)) - { - emitGCregDeadUpd(reg2, dst); - } - - // Now, swap the info - - if (needsGC(gc1)) - { - emitGCregLiveUpd(gc1, reg2, dst); - } - - if (needsGC(gc2)) - { - emitGCregLiveUpd(gc2, reg1, dst); - } - } - break; - } - - default: -#ifdef DEBUG - emitDispIns(id, false, false, false); -#endif - assert(!"unexpected GC ref instruction format"); - } + emitHandleGCrefRegs(dst, id); } else { @@ -15911,8 +16606,9 @@ BYTE* emitter::emitOutputRRR(BYTE* dst, instrDesc* id) code_t code; instruction ins = id->idIns(); - assert(IsVexOrEvexEncodableInstruction(ins)); - assert(IsThreeOperandAVXInstruction(ins) || isAvxBlendv(ins) || isAvx512Blendv(ins) || IsKInstruction(ins)); + assert(IsVexOrEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + assert(IsThreeOperandAVXInstruction(ins) || isAvxBlendv(ins) || isAvx512Blendv(ins) || IsKInstruction(ins) || + IsApxExtendedEvexInstruction(ins)); regNumber targetReg = id->idReg1(); regNumber src1 = id->idReg2(); regNumber src2 = id->idReg3(); @@ -15921,6 +16617,51 @@ BYTE* emitter::emitOutputRRR(BYTE* dst, instrDesc* id) code = insCodeRM(ins); code = AddX86PrefixIfNeeded(id, code, size); + if (IsApxExtendedEvexInstruction(ins) && !IsBMIInstruction(ins)) + { + // TODO-XArch-apx: + // For rm-like operand encoding instructions: + // legacy promoted EVEX encoding has introduced different semantic: + // op1 - vvvvv + // op2 - MODRM.REG + // op3 - MODRM.R/M + regNumber tmp = src1; + src1 = targetReg; + targetReg = tmp; + + switch (size) + { + case EA_1BYTE: + // TODO-APX : verify We should never end up here. Atleast for instructions I have looked into, we + // promote to int to do operation + noway_assert(RBM_BYTE_REGS & genRegMask(src1)); + noway_assert(RBM_BYTE_REGS & genRegMask(src2)); + noway_assert(RBM_BYTE_REGS & genRegMask(targetReg)); + break; + + case EA_2BYTE: + case EA_4BYTE: + // Set the 'w' bit to get the large version + code = insIsCMOV(ins) ? code : (code | (0x01)); + break; + +#ifdef TARGET_AMD64 + case EA_8BYTE: + // TODO-AMD64-CQ: Better way to not emit REX.W when we don't need it + // Don't need to zero out the high bits explicitly + code = AddRexWPrefix(id, code); // TODO-APX : Revisit. does xor or other cases need to be handled + // differently? see emitOutputRR + // Set the 'w' bit to get the large version + code = insIsCMOV(ins) ? code : (code | (0x01)); + break; + +#endif // TARGET_AMD64 + + default: + assert(!"unexpected size"); + } + } + code = insEncodeRMreg(id, code); if (TakesRexWPrefix(id)) @@ -15968,7 +16709,10 @@ BYTE* emitter::emitOutputRRR(BYTE* dst, instrDesc* id) dst += emitOutputByte(dst, (0xC0 | regCode)); } - noway_assert(!id->idGCref()); + if (id->idGCref()) + { + emitHandleGCrefRegs(dst, id); + } if (!emitInsCanOnlyWriteSSE2OrAVXReg(id)) { @@ -16151,6 +16895,12 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) useACC = true; } } + + if (TakesApxExtendedEvexPrefix(id)) + { + // ACC form does not have support for promoted EVEX. + useACC = false; + } } else { @@ -16206,7 +16956,10 @@ BYTE* emitter::emitOutputRI(BYTE* dst, instrDesc* id) case EA_2BYTE: // Output a size prefix for a 16-bit operand - dst += emitOutputByte(dst, 0x66); + if (!TakesApxExtendedEvexPrefix(id)) + { + dst += emitOutputByte(dst, 0x66); + } FALLTHROUGH; case EA_4BYTE: @@ -16884,7 +17637,23 @@ ssize_t emitter::GetInputSizeInBytes(instrDesc* id) const // ssize_t emitter::TryEvexCompressDisp8Byte(instrDesc* id, ssize_t dsp, bool* dspInByte) { - assert(TakesEvexPrefix(id)); + assert(TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)); + + if (!hasTupleTypeInfo(id->idIns())) + { + // After APX, some instructions with APX features will be promoted + // to APX-EVEX, we will re-use the existing displacement emitting + // path, but for those instructions with no tuple information, + // APX-EVEX treat the scaling factor to be 1 constantly. + instruction ins = id->idIns(); + // TODO-XArch-APX: + // This assert may need tweak if BMI1 instructions are promoted + // into EVEX for multiple features, currently only EVEX.NF. + assert(IsApxExtendedEvexInstruction(id->idIns())); + *dspInByte = ((signed char)dsp == (ssize_t)dsp); + return dsp; + } + insTupleType tt = insTupleTypeInfo(id->idIns()); assert(hasTupleTypeInfo(id->idIns())); @@ -17539,7 +18308,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } // Output a size prefix for a 16-bit operand - if (size == EA_2BYTE) + if (size == EA_2BYTE && !TakesApxExtendedEvexPrefix(id)) { dst += emitOutputByte(dst, 0x66); } @@ -17555,6 +18324,37 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; } + case IF_RWR_RRD_SHF: + { + assert(IsApxExtendedEvexInstruction(ins)); + code = insCodeMR(ins); + code = AddX86PrefixIfNeeded(id, code, size); + code = insEncodeMRreg(id, id->idReg2(), size, code); + code = insEncodeReg3456(id, id->idReg1(), size, code); + + // set the W bit + if (size != EA_1BYTE) + { + code |= 1; + } + + // Emit the REX prefix if it exists + if (TakesRexWPrefix(id)) + { + code = AddRexWPrefix(id, code); + } + + dst += emitOutputRexOrSimdPrefixIfNeeded(ins, dst, code); + dst += emitOutputWord(dst, code); + dst += emitOutputByte(dst, emitGetInsSC(id)); + sz = emitSizeOfInsDsc_CNS(id); + + // Update GC info. + assert(!id->idGCref()); + emitGCregDeadUpd(id->idReg1(), dst); + break; + } + case IF_RRD_RRD: case IF_RWR_RRD: case IF_RRW_RRD: @@ -17628,7 +18428,105 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) // Also, determine which operand goes where in the ModRM byte. regNumber mReg; regNumber rReg; - if (hasCodeMR(ins)) + if (IsApxExtendedEvexInstruction(ins)) + { + assert(hasCodeMI(ins)); + code = insCodeMI(ins); + code = AddX86PrefixIfNeeded(id, code, size); + code = insEncodeReg3456(id, id->idReg1(), size, code); + mReg = id->idReg2(); + code = insEncodeMIreg(id, mReg, size, code); + rReg = REG_NA; + ssize_t val = emitGetInsSC(id); + bool valInByte = ((signed char)val == (target_ssize_t)val) && (ins != INS_mov) && (ins != INS_test); + + switch (size) + { + case EA_1BYTE: + break; + + case EA_2BYTE: + code |= EXTENDED_EVEX_PP_BITS; + FALLTHROUGH; + + case EA_4BYTE: + code |= 1; + break; + +#ifdef TARGET_AMD64 + case EA_8BYTE: + code = AddRexWPrefix(id, code); + code |= 1; + break; +#endif // TARGET_AMD64 + + default: + assert(!"unexpected size"); + } + + dst += emitOutputRexOrSimdPrefixIfNeeded(ins, dst, code); + + if (valInByte && size > EA_1BYTE) + { + code |= 2; + dst += emitOutputWord(dst, code); + dst += emitOutputByte(dst, val); + } + else + { + dst += emitOutputWord(dst, code); + switch (size) + { + case EA_1BYTE: + dst += emitOutputByte(dst, val); + break; + case EA_2BYTE: + dst += emitOutputWord(dst, val); + break; + case EA_4BYTE: + dst += emitOutputLong(dst, val); + break; +#ifdef TARGET_AMD64 + case EA_8BYTE: + dst += emitOutputLong(dst, val); + break; +#endif // TARGET_AMD64 + default: + break; + } + + if (id->idIsCnsReloc()) + { + emitRecordRelocation((void*)(dst - sizeof(INT32)), (void*)(size_t)val, IMAGE_REL_BASED_HIGHLOW); + assert(size == EA_4BYTE); + } + } + + sz = emitSizeOfInsDsc_CNS(id); + + if (!emitInsCanOnlyWriteSSE2OrAVXReg(id)) + { + emitGCregDeadUpd(id->idReg1(), dst); + } + + switch (id->idInsFmt()) + { + case IF_RWR_RRD_CNS: + assert(!instrIs3opImul(ins)); + + emitGCregDeadUpd(id->idReg1(), dst); + break; + + default: +#ifdef DEBUG + emitDispIns(id, false, false, false); +#endif + assert(!"unexpected GC ref instruction format"); + } + + break; + } + else if (hasCodeMR(ins)) { code = insCodeMR(ins); // Emit the VEX prefix if it exists @@ -17863,6 +18761,23 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { code = insCodeRM(ins); + if (id->idIsEvexNdContextSet() && TakesApxExtendedEvexPrefix(id)) + { + // TODO-XArch-APX: + // I'm not sure why instructions on this path can be with instruction + // format other than IF_RWR_RRD_ARD, fix here for debug purpose only, + // need revisit. + id->idInsFmt(IF_RWR_RRD_ARD); + + code = AddX86PrefixIfNeeded(id, code, size); + code = insEncodeReg3456(id, id->idReg1(), size, code); + regcode = (insEncodeReg345(id, id->idReg2(), size, &code) << 8); + dst = emitOutputAM(dst, id, code | regcode); + + sz = emitSizeOfInsDsc_AMD(id); + break; + } + if (EncodedBySSE38orSSE3A(ins) || (ins == INS_crc32)) { // Special case 4-byte AVX instructions as the @@ -18130,7 +19045,19 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_RRW_RRD_SRD: case IF_RWR_RWR_SRD: { - assert(IsVexOrEvexEncodableInstruction(ins)); + assert(IsVexOrEvexEncodableInstruction(ins) || IsApxExtendedEvexInstruction(ins)); + + if (id->idIsEvexNdContextSet() && IsApxNDDEncodableInstruction(ins)) + { + // EVEX.vvvv has different semantic for APX-EVEX NDD instructions. + code = insCodeRM(ins); + code = AddX86PrefixIfNeeded(id, code, size); + code = insEncodeReg3456(id, id->idReg1(), size, code); + regcode = (insEncodeReg345(id, id->idReg2(), size, &code) << 8); + dst = emitOutputSV(dst, id, code | regcode); + sz = sizeof(instrDesc); + break; + } code = insCodeRM(ins); code = AddX86PrefixIfNeeded(id, code, size); @@ -19195,6 +20122,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins break; case IF_RRW: + // TODO-XArch-APX: to be verified if this data is correct for NDD form. + case IF_RWR_RRD: // ins reg, cl result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_2C; @@ -19222,6 +20151,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins switch (insFmt) { case IF_RRW: + // TODO-XArch-APX: to be verified if this data is correct for NDD form. + case IF_RWR_RRD: // ins reg, 1 result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; @@ -19255,6 +20186,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins switch (insFmt) { case IF_RRW_SHF: + case IF_RWR_RRD_SHF: // ins reg, cns result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; @@ -20225,6 +21157,11 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_vshuff64x2: case INS_vshufi32x4: case INS_vshufi64x2: +#ifdef TARGET_AMD64 + case INS_popcnt_apx: + case INS_lzcnt_apx: + case INS_tzcnt_apx: +#endif // TARGET_AMD64 { result.insThroughput = PERFSCORE_THROUGHPUT_1C; result.insLatency += PERFSCORE_LATENCY_3C; diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 5f820c7c022c20..8e149ed5be3389 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -134,11 +134,18 @@ static regNumber getSseShiftRegNumber(instruction ins); bool HasVexEncoding(instruction ins) const; bool HasEvexEncoding(instruction ins) const; bool HasRex2Encoding(instruction ins) const; +bool HasApxNdd(instruction ins) const; +bool HasApxNf(instruction ins) const; bool IsVexEncodableInstruction(instruction ins) const; bool IsEvexEncodableInstruction(instruction ins) const; bool IsRex2EncodableInstruction(instruction ins) const; +bool IsApxNDDEncodableInstruction(instruction ins) const; +bool IsApxNFEncodableInstruction(instruction ins) const; +bool IsApxExtendedEvexInstruction(instruction ins) const; +bool IsShiftInstruction(instruction ins) const; bool IsLegacyMap1(code_t code) const; bool IsVexOrEvexEncodableInstruction(instruction ins) const; +bool DoJitUseApxNDD(instruction ins) const; code_t insEncodeMIreg(const instrDesc* id, regNumber reg, emitAttr size, code_t code); @@ -179,6 +186,8 @@ bool AreFlagsSetForSignJumpOpt(regNumber reg, emitAttr opSize, GenCondition cond insOpts GetEmbRoundingMode(uint8_t mode) const; +void emitHandleGCrefRegs(BYTE* dst, instrDesc* id); + bool hasRexPrefix(code_t code) { #ifdef TARGET_AMD64 @@ -332,6 +341,18 @@ void SetUseRex2Encoding(bool value) useRex2Encodings = value; } +// Is Promoted EVEX encoding supported. +bool usePromotedEVEXEncodings; +bool UsePromotedEVEXEncoding() const +{ + return usePromotedEVEXEncodings; +} + +void SetUsePromotedEVEXEncoding(bool value) +{ + usePromotedEVEXEncodings = value; +} + //------------------------------------------------------------------------ // UseSimdEncoding: Returns true if either VEX or EVEX encoding is supported // contains Evex prefix. @@ -349,6 +370,7 @@ bool UseSimdEncoding() const #define EVEX_PREFIX_CODE 0x6200000000000000ULL bool TakesEvexPrefix(const instrDesc* id) const; +bool TakesApxExtendedEvexPrefix(const instrDesc* id) const; //------------------------------------------------------------------------ // hasEvexPrefix: Returns true if the instruction encoding already @@ -405,11 +427,7 @@ code_t AddSimdPrefixIfNeeded(const instrDesc* id, code_t code, emitAttr size) // code_t AddX86PrefixIfNeeded(const instrDesc* id, code_t code, emitAttr size) { - // TODO-xarch-apx: - // consider refactor this part with AddSimdPrefixIfNeeded as a lot of functionality - // of these functions are overlapping. - - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { return AddEvexPrefix(id, code, size); } @@ -445,7 +463,7 @@ code_t AddX86PrefixIfNeededAndNotPresent(const instrDesc* id, code_t code, emitA // consider refactor this part with AddSimdPrefixIfNeeded as a lot of functionality // of these functions are overlapping. - if (TakesEvexPrefix(id)) + if (TakesEvexPrefix(id) || TakesApxExtendedEvexPrefix(id)) { return !hasEvexPrefix(code) ? AddEvexPrefix(id, code, size) : code; } @@ -511,6 +529,48 @@ void SetEvexEmbMaskIfNeeded(instrDesc* id, insOpts instOptions) } } +//------------------------------------------------------------------------ +// SetEvexNdIfNeeded: set NDD form - new data destination if needed. +// +// Arguments: +// id - instruction descriptor +// instOptions - emit options +// +void SetEvexNdIfNeeded(instrDesc* id, insOpts instOptions) +{ + if ((instOptions & INS_OPTS_EVEX_nd_MASK) != 0) + { + assert(UsePromotedEVEXEncoding()); + assert(IsApxNDDEncodableInstruction(id->idIns())); + id->idSetEvexNdContext(); + } + else + { + assert((instOptions & INS_OPTS_EVEX_nd_MASK) == 0); + } +} + +//------------------------------------------------------------------------ +// SetEvexNdIfNeeded: set Evex.nf on instrDesc +// +// Arguments: +// id - instruction descriptor +// instOptions - emit options +// +void SetEvexNfIfNeeded(instrDesc* id, insOpts instOptions) +{ + if ((instOptions & INS_OPTS_EVEX_nf_MASK) != 0) + { + assert(UsePromotedEVEXEncoding()); + assert(IsApxNFEncodableInstruction(id->idIns())); + id->idSetEvexNfContext(); + } + else + { + assert((instOptions & INS_OPTS_EVEX_nf_MASK) == 0); + } +} + //------------------------------------------------------------------------ // AddSimdPrefixIfNeeded: Add the correct SIMD prefix. // Check if the prefix already exists befpre adding. @@ -753,7 +813,7 @@ void emitIns_Data16(); void emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t val); -void emitIns_R(instruction ins, emitAttr attr, regNumber reg); +void emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts instOptions = INS_OPTS_NONE); void emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int offs); @@ -762,7 +822,9 @@ void emitIns_A(instruction ins, emitAttr attr, GenTreeIndir* indir); void emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, - ssize_t val DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); + ssize_t val, + insOpts instOptions = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0) + DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip); @@ -771,7 +833,7 @@ void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, void emitIns_R_R_I( instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int ival, insOpts instOptions = INS_OPTS_NONE); -void emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs); +void emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs, insOpts instOptions = INS_OPTS_NONE); void emitIns_AR_R_R(instruction ins, emitAttr attr, @@ -942,7 +1004,8 @@ void emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg) void emitIns_R_D(instruction ins, emitAttr attr, unsigned offs, regNumber reg); -void emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber reg, int offs); +void emitIns_I_AR( + instruction ins, emitAttr attr, int val, regNumber reg, int offs, insOpts instOptions = INS_OPTS_NONE); void emitIns_I_AI(instruction ins, emitAttr attr, int val, ssize_t disp); @@ -953,7 +1016,12 @@ void emitIns_R_AI(instruction ins, regNumber ireg, ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); -void emitIns_AR_R(instruction ins, emitAttr attr, regNumber reg, regNumber base, cnsval_ssize_t disp); +void emitIns_AR_R(instruction ins, + emitAttr attr, + regNumber reg, + regNumber base, + cnsval_ssize_t disp, + insOpts instOptions = INS_OPTS_NONE); void emitIns_AI_R(instruction ins, emitAttr attr, regNumber ireg, ssize_t disp); @@ -974,7 +1042,8 @@ void emitIns_ARX_R(instruction ins, regNumber base, regNumber index, unsigned scale, - cnsval_ssize_t disp); + cnsval_ssize_t disp, + insOpts instOptions = INS_OPTS_NONE); void emitIns_I_AX(instruction ins, emitAttr attr, int val, regNumber reg, unsigned mul, int disp); @@ -1122,6 +1191,13 @@ void emitIns_SIMD_R_R_R_S_I(instruction ins, insOpts instOptions); #endif // FEATURE_HW_INTRINSICS +void emitIns_BASE_R_R(instruction ins, emitAttr attr, regNumber op1Reg, regNumber op2Reg); + +void emitIns_BASE_R_R_I(instruction ins, emitAttr attr, regNumber op1Reg, regNumber op2Reg, int ival); + +regNumber emitIns_BASE_R_R_RM( + instruction ins, emitAttr attr, regNumber targetReg, GenTree* treeNode, GenTree* regOp, GenTree* rmOp); + enum EmitCallType { EC_FUNC_TOKEN, // Direct call to a helper/static/nonvirtual/global method (call addr with RIP-relative encoding) diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 4f00ca62627966..3ff3708785cd4b 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -221,6 +221,12 @@ enum insFlags : uint64_t // APX: REX2 prefix: Encoding_REX2 = 1ULL << 44, + // APX: EVEX.ND: + INS_Flags_Has_NDD = 1ULL << 45, + + // APX: EVEX.NF: + INS_Flags_Has_NF = 1ULL << 46, + // TODO-Cleanup: Remove this flag and its usage from TARGET_XARCH INS_FLAGS_DONT_CARE = 0x00ULL, }; @@ -259,6 +265,19 @@ enum insOpts: unsigned INS_OPTS_EVEX_z_MASK = 0x20, // mask for EVEX.z related features INS_OPTS_EVEX_em_zero = 1 << 5, // Embedded mask merges with zero + + // One-bit: 0b0100_0000 + INS_OPTS_EVEX_nd_MASK = 0x40, // mask for APX-EVEX.nd related features + + INS_OPTS_EVEX_nd = 1 << 6, // NDD form for legacy instructions + + // One-bit: 0b1000_0000 + INS_OPTS_EVEX_nf_MASK = 0x80, // mask for APX-EVEX.nf related features + + INS_OPTS_EVEX_nf = 1 << 7, // NDD form for legacy instructions + + INS_OPTS_EVEX_NoApxPromotion = 1 << 8, // Do not promote to APX-EVEX + }; #elif defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h index 24be0ef3527b6a..f4c5df821190af 100644 --- a/src/coreclr/jit/instrsxarch.h +++ b/src/coreclr/jit/instrsxarch.h @@ -58,26 +58,26 @@ INST5(pop, "pop", IUM_WR, 0x00008E, BAD_CODE, INST5(push_hide, "push", IUM_RD, 0x0030FE, 0x000068, BAD_CODE, BAD_CODE, 0x000050, INS_TT_NONE, Encoding_REX2) INST5(pop_hide, "pop", IUM_WR, 0x00008E, BAD_CODE, BAD_CODE, BAD_CODE, 0x000058, INS_TT_NONE, Encoding_REX2) -INST5(inc, "inc", IUM_RW, 0x0000FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x000040, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | INS_FLAGS_Has_Wbit |Encoding_REX2) -INST5(inc_l, "inc", IUM_RW, 0x0000FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x00C0FE, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Encoding_REX2) -INST5(dec, "dec", IUM_RW, 0x0008FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x000048, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST5(dec_l, "dec", IUM_RW, 0x0008FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x00C8FE, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Encoding_REX2) +INST5(inc, "inc", IUM_RW, 0x0000FE, BAD_CODE, 0x0000FE, BAD_CODE, 0x000040, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST5(inc_l, "inc", IUM_RW, 0x0000FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x00C0FE, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Encoding_REX2 | INS_Flags_Has_NF) +INST5(dec, "dec", IUM_RW, 0x0008FE, BAD_CODE, 0x0008FE, BAD_CODE, 0x000048, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST5(dec_l, "dec", IUM_RW, 0x0008FE, BAD_CODE, BAD_CODE, BAD_CODE, 0x00C8FE, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Encoding_REX2 | INS_Flags_Has_NF) // Multi-byte opcodes without modrm are represented in mixed endian fashion. // See comment around quarter way through this file for more information. INST5(bswap, "bswap", IUM_RW, 0x0F00C8, BAD_CODE, BAD_CODE, BAD_CODE, 0x00C80F, INS_TT_NONE, Encoding_REX2) // id nm um mr mi rm a4 tt flags -INST4(add, "add", IUM_RW, 0x000000, 0x000080, 0x000002, 0x000004, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST4(or, "or", IUM_RW, 0x000008, 0x000880, 0x00000A, 0x00000C, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) +INST4(add, "add", IUM_RW, 0x000000, 0x000080, 0x000002, 0x000004, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST4(or, "or", IUM_RW, 0x000008, 0x000880, 0x00000A, 0x00000C, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) INST4(adc, "adc", IUM_RW, 0x000010, 0x001080, 0x000012, 0x000014, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | Reads_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) INST4(sbb, "sbb", IUM_RW, 0x000018, 0x001880, 0x00001A, 0x00001C, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | Reads_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST4(and, "and", IUM_RW, 0x000020, 0x002080, 0x000022, 0x000024, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST4(sub, "sub", IUM_RW, 0x000028, 0x002880, 0x00002A, 0x00002C, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) +INST4(and, "and", IUM_RW, 0x000020, 0x002080, 0x000022, 0x000024, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST4(sub, "sub", IUM_RW, 0x000028, 0x002880, 0x00002A, 0x00002C, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) // Does not affect the stack tracking in the emitter INST4(sub_hide, "sub", IUM_RW, 0x000028, 0x002880, 0x00002A, 0x00002C, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST4(xor, "xor", IUM_RW, 0x000030, 0x003080, 0x000032, 0x000034, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) +INST4(xor, "xor", IUM_RW, 0x000030, 0x003080, 0x000032, 0x000034, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) INST4(cmp, "cmp", IUM_RD, 0x000038, 0x003880, 0x00003A, 0x00003C, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_FLAGS_Has_Wbit | Encoding_REX2) INST4(test, "test", IUM_RD, 0x000084, 0x0000F6, 0x000084, 0x0000A8, INS_TT_NONE, Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Resets_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) INST4(mov, "mov", IUM_WR, 0x000088, 0x0000C6, 0x00008A, 0x0000B0, INS_TT_NONE, INS_FLAGS_Has_Wbit | Encoding_REX2) @@ -99,25 +99,25 @@ INST3(movsxd, "movsxd", IUM_WR, BAD_CODE, BAD_CODE, #endif INST3(movzx, "movzx", IUM_WR, BAD_CODE, BAD_CODE, 0x0F00B6, INS_TT_NONE, INS_FLAGS_Has_Wbit | Encoding_REX2) -INST3(cmovo, "cmovo", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0040, INS_TT_NONE, Reads_OF | Encoding_REX2) -INST3(cmovno, "cmovno", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0041, INS_TT_NONE, Reads_OF | Encoding_REX2) -INST3(cmovb, "cmovb", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0042, INS_TT_NONE, Reads_CF | Encoding_REX2) -INST3(cmovae, "cmovae", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0043, INS_TT_NONE, Reads_CF | Encoding_REX2) -INST3(cmove, "cmove", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0044, INS_TT_NONE, Reads_ZF | Encoding_REX2) -INST3(cmovne, "cmovne", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0045, INS_TT_NONE, Reads_ZF | Encoding_REX2) -INST3(cmovbe, "cmovbe", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0046, INS_TT_NONE, Reads_ZF | Reads_CF | Encoding_REX2) -INST3(cmova, "cmova", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0047, INS_TT_NONE, Reads_ZF | Reads_CF | Encoding_REX2) -INST3(cmovs, "cmovs", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0048, INS_TT_NONE, Reads_SF | Encoding_REX2) -INST3(cmovns, "cmovns", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0049, INS_TT_NONE, Reads_SF | Encoding_REX2) -INST3(cmovp, "cmovp", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004A, INS_TT_NONE, Reads_PF | Encoding_REX2) -INST3(cmovnp, "cmovnp", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004B, INS_TT_NONE, Reads_PF | Encoding_REX2) -INST3(cmovl, "cmovl", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004C, INS_TT_NONE, Reads_OF | Reads_SF | Encoding_REX2) -INST3(cmovge, "cmovge", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004D, INS_TT_NONE, Reads_OF | Reads_SF | Encoding_REX2) -INST3(cmovle, "cmovle", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004E, INS_TT_NONE, Reads_OF | Reads_SF | Reads_ZF | Encoding_REX2) -INST3(cmovg, "cmovg", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004F, INS_TT_NONE, Reads_OF | Reads_SF | Reads_ZF | Encoding_REX2) +INST3(cmovo, "cmovo", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0040, INS_TT_NONE, Reads_OF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovno, "cmovno", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0041, INS_TT_NONE, Reads_OF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovb, "cmovb", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0042, INS_TT_NONE, Reads_CF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovae, "cmovae", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0043, INS_TT_NONE, Reads_CF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmove, "cmove", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0044, INS_TT_NONE, Reads_ZF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovne, "cmovne", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0045, INS_TT_NONE, Reads_ZF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovbe, "cmovbe", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0046, INS_TT_NONE, Reads_ZF | Reads_CF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmova, "cmova", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0047, INS_TT_NONE, Reads_ZF | Reads_CF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovs, "cmovs", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0048, INS_TT_NONE, Reads_SF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovns, "cmovns", IUM_WR, BAD_CODE, BAD_CODE, 0x0F0049, INS_TT_NONE, Reads_SF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovp, "cmovp", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004A, INS_TT_NONE, Reads_PF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovnp, "cmovnp", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004B, INS_TT_NONE, Reads_PF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovl, "cmovl", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004C, INS_TT_NONE, Reads_OF | Reads_SF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovge, "cmovge", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004D, INS_TT_NONE, Reads_OF | Reads_SF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovle, "cmovle", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004E, INS_TT_NONE, Reads_OF | Reads_SF | Reads_ZF | Encoding_REX2 | INS_Flags_Has_NDD) +INST3(cmovg, "cmovg", IUM_WR, BAD_CODE, BAD_CODE, 0x0F004F, INS_TT_NONE, Reads_OF | Reads_SF | Reads_ZF | Encoding_REX2 | INS_Flags_Has_NDD) INST3(xchg, "xchg", IUM_RW, 0x000086, BAD_CODE, 0x000086, INS_TT_NONE, INS_FLAGS_Has_Wbit | Encoding_REX2) -INST3(imul, "imul", IUM_RW, 0x0F00AC, BAD_CODE, 0x0F00AF, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) +INST3(imul, "imul", IUM_RW, 0x0F00AC, BAD_CODE, 0x0F00AF, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NDD | INS_Flags_Has_NF) // id nm um mr mi rm tt flags @@ -125,25 +125,25 @@ INST3(imul, "imul", IUM_RW, 0x0F00AC, BAD_CODE, // as 2-operand instructions with the target register being implicit // implicit_reg = op1*op2_icon #define INSTMUL INST3 -INSTMUL(imul_AX, "imul", IUM_RD, BAD_CODE, 0x000068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_CX, "imul", IUM_RD, BAD_CODE, 0x000868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_DX, "imul", IUM_RD, BAD_CODE, 0x001068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_BX, "imul", IUM_RD, BAD_CODE, 0x001868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_SP, "imul", IUM_RD, BAD_CODE, BAD_CODE, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_BP, "imul", IUM_RD, BAD_CODE, 0x002868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_SI, "imul", IUM_RD, BAD_CODE, 0x003068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_DI, "imul", IUM_RD, BAD_CODE, 0x003868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) +INSTMUL(imul_AX, "imul", IUM_RD, BAD_CODE, 0x000068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_CX, "imul", IUM_RD, BAD_CODE, 0x000868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_DX, "imul", IUM_RD, BAD_CODE, 0x001068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_BX, "imul", IUM_RD, BAD_CODE, 0x001868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_SP, "imul", IUM_RD, BAD_CODE, BAD_CODE, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_BP, "imul", IUM_RD, BAD_CODE, 0x002868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_SI, "imul", IUM_RD, BAD_CODE, 0x003068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_DI, "imul", IUM_RD, BAD_CODE, 0x003868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) #ifdef TARGET_AMD64 -INSTMUL(imul_08, "imul", IUM_RD, BAD_CODE, 0x4400000068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_09, "imul", IUM_RD, BAD_CODE, 0x4400000868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_10, "imul", IUM_RD, BAD_CODE, 0x4400001068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_11, "imul", IUM_RD, BAD_CODE, 0x4400001868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_12, "imul", IUM_RD, BAD_CODE, 0x4400002068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_13, "imul", IUM_RD, BAD_CODE, 0x4400002868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_14, "imul", IUM_RD, BAD_CODE, 0x4400003068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) -INSTMUL(imul_15, "imul", IUM_RD, BAD_CODE, 0x4400003868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit) +INSTMUL(imul_08, "imul", IUM_RD, BAD_CODE, 0x4400000068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_09, "imul", IUM_RD, BAD_CODE, 0x4400000868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_10, "imul", IUM_RD, BAD_CODE, 0x4400001068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_11, "imul", IUM_RD, BAD_CODE, 0x4400001868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_12, "imul", IUM_RD, BAD_CODE, 0x4400002068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_13, "imul", IUM_RD, BAD_CODE, 0x4400002868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_14, "imul", IUM_RD, BAD_CODE, 0x4400003068, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) +INSTMUL(imul_15, "imul", IUM_RD, BAD_CODE, 0x4400003868, BAD_CODE, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Sbit | INS_Flags_Has_NF) #endif // TARGET_AMD64 @@ -593,11 +593,11 @@ INST3(LAST_AVXVNNI_INSTRUCTION, "LAST_AVXVNNI_INSTRUCTION", IUM_WR, BAD_CODE, BA INST3(FIRST_BMI_INSTRUCTION, "FIRST_BMI_INSTRUCTION", IUM_WR, BAD_CODE, BAD_CODE, BAD_CODE, INS_TT_NONE, INS_FLAGS_None) // BMI1 -INST3(andn, "andn", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF2), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF) // Logical AND NOT -INST3(bextr, "bextr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Undefined_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF) // Bit Field Extract -INST3(blsi, "blsi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF) // Extract Lowest Set Isolated Bit -INST3(blsmsk, "blsmsk", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Resets_ZF | Undefined_AF | Undefined_PF | Writes_CF) // Get Mask Up to Lowest Set Bit -INST3(blsr, "blsr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF) // Reset Lowest Set Bit +INST3(andn, "andn", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF2), INS_TT_NONE, REX_WX | Encoding_VEX | Encoding_EVEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF | INS_Flags_Has_NF) // Logical AND NOT +INST3(bextr, "bextr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_TT_NONE, REX_WX | Encoding_VEX | Encoding_EVEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Undefined_SF | Writes_ZF | Undefined_AF | Undefined_PF | Resets_CF | INS_Flags_Has_NF) // Bit Field Extract +INST3(blsi, "blsi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | Encoding_EVEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_Has_NF) // Extract Lowest Set Isolated Bit +INST3(blsmsk, "blsmsk", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | Encoding_EVEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Resets_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_Has_NF) // Get Mask Up to Lowest Set Bit +INST3(blsr, "blsr", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF3), INS_TT_NONE, REX_WX | Encoding_VEX | Encoding_EVEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_Has_NF) // Reset Lowest Set Bit // BMI2 INST3(bzhi, "bzhi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), INS_TT_NONE, REX_WX | Encoding_VEX | INS_Flags_IsDstDstSrcAVXInstruction | Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF) // Zero High Bits Starting with Specified Bit Position @@ -952,35 +952,43 @@ INST3(movbe, "movbe", IUM_WR, PCKMVB(0xF1), BAD_CODE, // POPCNT INST3(popcnt, "popcnt", IUM_WR, BAD_CODE, BAD_CODE, SSEFLT(0xB8), INS_TT_NONE, Resets_OF | Resets_SF | Writes_ZF | Resets_AF | Resets_PF | Resets_CF | Encoding_REX2) +#if defined(TARGET_AMD64) +INST3(tzcnt_apx, "tzcnt", IUM_WR, BAD_CODE, BAD_CODE, 0x0000F4, INS_TT_NONE, Undefined_OF | Undefined_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_Has_NF) // Count the Number of Trailing Zero Bits +INST3(lzcnt_apx, "lzcnt", IUM_WR, BAD_CODE, BAD_CODE, 0x0000F5, INS_TT_NONE, Undefined_OF | Undefined_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_Has_NF) +INST3(popcnt_apx, "popcnt", IUM_WR, BAD_CODE, BAD_CODE, 0x000088, INS_TT_NONE, Resets_OF | Resets_SF | Writes_ZF | Resets_AF | Resets_PF | Resets_CF | INS_Flags_Has_NF) +#endif // TARGET_AMD64 + +INST3(neg, "neg", IUM_RW, 0x0018F6, BAD_CODE, 0x0018F6, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(not, "not", IUM_RW, 0x0010F6, BAD_CODE, 0x0010F6, INS_TT_NONE, INS_FLAGS_None | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD) + +INST3(rol, "rol", IUM_RW, 0x0000D2, BAD_CODE, 0x0000D2, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rol_1, "rol", IUM_RW, 0x0000D0, 0x0000D0, 0x0000D0, INS_TT_NONE, Writes_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rol_N, "rol", IUM_RW, 0x0000C0, 0x0000C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(ror, "ror", IUM_RW, 0x0008D2, BAD_CODE, 0x0008D2, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(ror_1, "ror", IUM_RW, 0x0008D0, 0x0008D0, 0x0008D0, INS_TT_NONE, Writes_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(ror_N, "ror", IUM_RW, 0x0008C0, 0x0008C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) + +INST3(rcl, "rcl", IUM_RW, 0x0010D2, BAD_CODE, 0x0010D2, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rcl_1, "rcl", IUM_RW, 0x0010D0, 0x0010D0, 0x0010D0, INS_TT_NONE, Writes_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rcl_N, "rcl", IUM_RW, 0x0010C0, 0x0010C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rcr, "rcr", IUM_RW, 0x0018D2, BAD_CODE, 0x0018D2, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rcr_1, "rcr", IUM_RW, 0x0018D0, 0x0018D0, 0x0018D0, INS_TT_NONE, Writes_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(rcr_N, "rcr", IUM_RW, 0x0018C0, 0x0018C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shl, "shl", IUM_RW, 0x0020D2, BAD_CODE, 0x0020D2, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shl_1, "shl", IUM_RW, 0x0020D0, 0x0020D0, 0x0020D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shl_N, "shl", IUM_RW, 0x0020C0, 0x0020C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shr, "shr", IUM_RW, 0x0028D2, BAD_CODE, 0x0028D2, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shr_1, "shr", IUM_RW, 0x0028D0, 0x0028D0, 0x0028D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(shr_N, "shr", IUM_RW, 0x0028C0, 0x0028C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(sar, "sar", IUM_RW, 0x0038D2, BAD_CODE, 0x0038D2, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(sar_1, "sar", IUM_RW, 0x0038D0, 0x0038D0, 0x0038D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) +INST3(sar_N, "sar", IUM_RW, 0x0038C0, 0x0038C0, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) + // id nm um mr mi flags INST2(ret, "ret", IUM_RD, 0x0000C3, 0x0000C2, INS_TT_NONE, INS_FLAGS_None) INST2(loop, "loop", IUM_RD, BAD_CODE, 0x0000E2, INS_TT_NONE, INS_FLAGS_None) INST2(call, "call", IUM_RD, 0x0010FF, 0x0000E8, INS_TT_NONE, Encoding_REX2) -INST2(rol, "rol", IUM_RW, 0x0000D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rol_1, "rol", IUM_RW, 0x0000D0, 0x0000D0, INS_TT_NONE, Writes_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rol_N, "rol", IUM_RW, 0x0000C0, 0x0000C0, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(ror, "ror", IUM_RW, 0x0008D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(ror_1, "ror", IUM_RW, 0x0008D0, 0x0008D0, INS_TT_NONE, Writes_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(ror_N, "ror", IUM_RW, 0x0008C0, 0x0008C0, INS_TT_NONE, Undefined_OF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) - -INST2(rcl, "rcl", IUM_RW, 0x0010D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rcl_1, "rcl", IUM_RW, 0x0010D0, 0x0010D0, INS_TT_NONE, Writes_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rcl_N, "rcl", IUM_RW, 0x0010C0, 0x0010C0, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rcr, "rcr", IUM_RW, 0x0018D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rcr_1, "rcr", IUM_RW, 0x0018D0, 0x0018D0, INS_TT_NONE, Writes_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(rcr_N, "rcr", IUM_RW, 0x0018C0, 0x0018C0, INS_TT_NONE, Undefined_OF | Writes_CF | Reads_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shl, "shl", IUM_RW, 0x0020D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shl_1, "shl", IUM_RW, 0x0020D0, 0x0020D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shl_N, "shl", IUM_RW, 0x0020C0, 0x0020C0, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shr, "shr", IUM_RW, 0x0028D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shr_1, "shr", IUM_RW, 0x0028D0, 0x0028D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(shr_N, "shr", IUM_RW, 0x0028C0, 0x0028C0, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(sar, "sar", IUM_RW, 0x0038D2, BAD_CODE, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(sar_1, "sar", IUM_RW, 0x0038D0, 0x0038D0, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST2(sar_N, "sar", IUM_RW, 0x0038C0, 0x0038C0, INS_TT_NONE, Undefined_OF | Writes_SF | Writes_ZF | Undefined_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) - - // id nm um mr flags INST1(r_movsb, "rep movsb", IUM_RD, 0x00A4F3, INS_TT_NONE, Reads_DF | INS_FLAGS_Has_Wbit) INST1(r_movsd, "rep movsd", IUM_RD, 0x00A5F3, INS_TT_NONE, Reads_DF | INS_FLAGS_Has_Wbit) @@ -1012,15 +1020,12 @@ INST1(leave, "leave", IUM_RD, 0x0000C9, INST1(serialize, "serialize", IUM_RD, 0x0fe801, INS_TT_NONE, INS_FLAGS_None) -INST1(neg, "neg", IUM_RW, 0x0018F6, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST1(not, "not", IUM_RW, 0x0010F6, INS_TT_NONE, INS_FLAGS_None | INS_FLAGS_Has_Wbit | Encoding_REX2) - INST1(cwde, "cwde", IUM_RD, 0x000098, INS_TT_NONE, INS_FLAGS_None) INST1(cdq, "cdq", IUM_RD, 0x000099, INS_TT_NONE, INS_FLAGS_None) -INST1(idiv, "idiv", IUM_RD, 0x0038F6, INS_TT_NONE, Undefined_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Undefined_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST1(imulEAX, "imul", IUM_RD, 0x0028F6, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Wbit) -INST1(div, "div", IUM_RD, 0x0030F6, INS_TT_NONE, Undefined_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Undefined_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) -INST1(mulEAX, "mul", IUM_RD, 0x0020F6, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2) +INST1(idiv, "idiv", IUM_RD, 0x0038F6, INS_TT_NONE, Undefined_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Undefined_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NF) +INST1(imulEAX, "imul", IUM_RD, 0x0028F6, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Wbit | INS_Flags_Has_NF) +INST1(div, "div", IUM_RD, 0x0030F6, INS_TT_NONE, Undefined_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Undefined_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NF) +INST1(mulEAX, "mul", IUM_RD, 0x0020F6, INS_TT_NONE, Writes_OF | Undefined_SF | Undefined_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NF) INST1(sahf, "sahf", IUM_RD, 0x00009E, INS_TT_NONE, Restore_SF_ZF_AF_PF_CF) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 134d5ffc99ca00..ab8c6495027003 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -369,8 +369,9 @@ RELEASE_CONFIG_INTEGER(EnableMultiRegLocals, "EnableMultiRegLocals", 1) RELEASE_CONFIG_INTEGER(JitNoInline, "JitNoInline", 0) #if defined(DEBUG) -CONFIG_INTEGER(JitStressRex2Encoding, "JitStressRex2Encoding", 0) // Enable rex2 encoding for legacy instructions. -CONFIG_INTEGER(JitBypassAPXCheck, "JitBypassAPXCheck", 0) // Bypass APX CPUID check. +CONFIG_INTEGER(JitStressRex2Encoding, "JitStressRex2Encoding", 0) // Enable rex2 encoding for compatible instructions. +CONFIG_INTEGER(JitStressPromotedEvexEncoding, "JitStressPromotedEvexEncoding", 0) // Enable promoted EVEX encoding for + // compatible instructions. #endif // clang-format off @@ -440,6 +441,7 @@ RELEASE_CONFIG_INTEGER(EnableArm64Sve, "EnableArm64Sve", RELEASE_CONFIG_INTEGER(EnableEmbeddedBroadcast, "EnableEmbeddedBroadcast", 1) // Allows embedded broadcasts to be disabled RELEASE_CONFIG_INTEGER(EnableEmbeddedMasking, "EnableEmbeddedMasking", 1) // Allows embedded masking to be disabled +RELEASE_CONFIG_INTEGER(EnableApxNDD, "EnableApxNDD", 0) // Allows APX NDD feature to be disabled // clang-format on From aef8f42992ba3e28ec68b6bd983ce1e508bcec47 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Thu, 30 Jan 2025 10:12:22 -0800 Subject: [PATCH 28/31] JIT: Check for profile consistency throughout JIT backend (#111684) Part of #107749. --- src/coreclr/jit/compiler.cpp | 10 ----- src/coreclr/jit/helperexpansion.cpp | 40 ++++++++++-------- src/coreclr/jit/jiteh.cpp | 2 +- src/coreclr/jit/lower.cpp | 65 ++++++++++++++++++++++++++--- src/coreclr/jit/optimizer.cpp | 11 +++++ 5 files changed, 93 insertions(+), 35 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 274542e7823f56..a1e84a95c89a48 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -4872,11 +4872,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl DoPhase(this, PHASE_COMPUTE_DOMINATORS, &Compiler::fgComputeDominators); } - // Drop back to just checking profile likelihoods. - // - activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE; - activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS; - #ifdef DEBUG fgDebugCheckLinks(); #endif @@ -5157,11 +5152,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl DoPhase(this, PHASE_SWITCH_RECOGNITION, &Compiler::optSwitchRecognition); } - // Drop back to just checking profile likelihoods. - // - activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE; - activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS; - #ifdef DEBUG // Stash the current estimate of the function's size if necessary. if (verbose && opts.OptimizationEnabled()) diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index 8407fd469eff6c..1d1eb069819e75 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -744,7 +744,7 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St fastPathBb->inheritWeight(prevBb); // fallback will just execute first time - fallbackBb->bbSetRunRarely(); + fallbackBb->inheritWeightPercentage(tlsRootNullCondBB, 0); fgRedirectTargetEdge(prevBb, tlsRootNullCondBB); @@ -1180,7 +1180,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* fastPathBb->inheritWeight(prevBb); // fallback will just execute first time - fallbackBb->bbSetRunRarely(); + fallbackBb->inheritWeightPercentage(prevBb, 0); // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); @@ -1545,7 +1545,7 @@ bool Compiler::fgExpandStaticInitForCall(BasicBlock** pBlock, Statement* stmt, G block->inheritWeight(prevBb); isInitedBb->inheritWeight(prevBb); - helperCallBb->bbSetRunRarely(); + helperCallBb->inheritWeightPercentage(isInitedBb, 0); // All blocks are expected to be in the same EH region assert(BasicBlock::sameEHRegion(prevBb, block)); @@ -1847,6 +1847,7 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, // // Redirect prevBb to lengthCheckBb fgRedirectTargetEdge(prevBb, lengthCheckBb); + lengthCheckBb->inheritWeight(prevBb); assert(prevBb->JumpsToNext()); { @@ -1859,6 +1860,11 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, // review: we assume length check always succeeds?? trueEdge->setLikelihood(1.0); falseEdge->setLikelihood(0.0); + + if (lengthCheckBb->hasProfileWeight()) + { + fastpathBb->setBBProfileWeight(falseEdge->getLikelyWeight()); + } } { @@ -1869,10 +1875,8 @@ bool Compiler::fgVNBasedIntrinsicExpansionForCall_ReadUtf8(BasicBlock** pBlock, } // - // Re-distribute weights + // Ensure all flow out of prevBb converges into block // - lengthCheckBb->inheritWeight(prevBb); - fastpathBb->inheritWeight(lengthCheckBb); block->inheritWeight(prevBb); // All blocks are expected to be in the same EH region @@ -2551,11 +2555,18 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, trueEdge->setLikelihood(nullcheckTrueLikelihood); } + // Set nullcheckBb's weight here, so we can propagate it to its successors below + nullcheckBb->inheritWeight(firstBb); + if (typeCheckNotNeeded) { FlowEdge* const falseEdge = fgAddRefPred(fallbackBb, nullcheckBb); nullcheckBb->SetFalseEdge(falseEdge); falseEdge->setLikelihood(nullcheckFalseLikelihood); + fallbackBb->inheritWeight(nullcheckBb); + fallbackBb->scaleBBWeight(nullcheckFalseLikelihood); + lastBb->inheritWeight(nullcheckBb); + lastBb->scaleBBWeight(nullcheckTrueLikelihood); typeCheckSucceedBb = nullptr; } @@ -2631,7 +2642,6 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, // The same goes for inherited weights -- the block where we test for B will have // the weight of A times the likelihood that A's test fails, etc. // - nullcheckBb->inheritWeight(firstBb); weight_t sumOfPreviousLikelihood = 0; for (int candidateId = 0; candidateId < numOfCandidates; candidateId++) { @@ -2666,28 +2676,22 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt, sumOfPreviousLikelihood += likelihood; } - if (fallbackBb->KindIs(BBJ_THROW)) - { - fallbackBb->bbSetRunRarely(); - } - else + fallbackBb->inheritWeight(lastTypeCheckBb); + fallbackBb->scaleBBWeight(lastTypeCheckBb->GetFalseEdge()->getLikelihood()); + + if (fallbackBb->KindIs(BBJ_ALWAYS)) { - assert(fallbackBb->KindIs(BBJ_ALWAYS)); FlowEdge* const newEdge = fgAddRefPred(lastBb, fallbackBb); fallbackBb->SetTargetEdge(newEdge); - fallbackBb->inheritWeight(lastTypeCheckBb); - weight_t lastTypeCheckFailedLikelihood = lastTypeCheckBb->GetFalseEdge()->getLikelihood(); - fallbackBb->scaleBBWeight(lastTypeCheckFailedLikelihood); } if (!typeCheckNotNeeded) { typeCheckSucceedBb->inheritWeight(typeChecksBbs[0]); typeCheckSucceedBb->scaleBBWeight(sumOfPreviousLikelihood); + lastBb->inheritWeight(firstBb); } - lastBb->inheritWeight(firstBb); - // // Validate EH regions // diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index 60c830aad8d592..c833f2164fa0bc 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -2660,7 +2660,7 @@ bool Compiler::fgCreateFiltersForGenericExceptions() filterBb->bbCodeOffs = handlerBb->bbCodeOffs; filterBb->bbHndIndex = handlerBb->bbHndIndex; filterBb->bbTryIndex = handlerBb->bbTryIndex; - filterBb->bbSetRunRarely(); + filterBb->inheritWeightPercentage(handlerBb, 0); filterBb->SetFlags(BBF_INTERNAL | BBF_DONT_REMOVE); handlerBb->bbCatchTyp = BBCT_FILTER_HANDLER; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 484bf1454e5fd3..3b022ca56c857b 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -1071,11 +1071,11 @@ GenTree* Lowering::LowerSwitch(GenTree* node) for (unsigned i = 0; i < jumpCnt - 1; ++i) { assert(currentBlock != nullptr); - BasicBlock* const targetBlock = jumpTab[i]->getDestinationBlock(); // Remove the switch from the predecessor list of this case target's block. // We'll add the proper new predecessor edge later. - FlowEdge* const oldEdge = jumpTab[i]; + FlowEdge* const oldEdge = jumpTab[i]; + BasicBlock* const targetBlock = oldEdge->getDestinationBlock(); // Compute the likelihood that this test is successful. // Divide by number of cases still sharing this edge (reduces likelihood) @@ -1136,8 +1136,9 @@ GenTree* Lowering::LowerSwitch(GenTree* node) { BasicBlock* const newBlock = comp->fgNewBBafter(BBJ_ALWAYS, currentBlock, true); FlowEdge* const newEdge = comp->fgAddRefPred(newBlock, currentBlock); - currentBlock = newBlock; - currentBBRange = &LIR::AsRange(currentBlock); + newBlock->inheritWeight(currentBlock); + currentBlock = newBlock; + currentBBRange = &LIR::AsRange(currentBlock); afterDefaultCondBlock->SetKindAndTargetEdge(BBJ_ALWAYS, newEdge); } @@ -1212,6 +1213,25 @@ GenTree* Lowering::LowerSwitch(GenTree* node) currentBlock->RemoveFlags(BBF_DONT_REMOVE); comp->fgRemoveBlock(currentBlock, /* unreachable */ false); // It's an empty block. } + + // Update flow into switch targets + if (afterDefaultCondBlock->hasProfileWeight()) + { + bool profileInconsistent = false; + for (unsigned i = 0; i < jumpCnt - 1; i++) + { + BasicBlock* const targetBlock = jumpTab[i]->getDestinationBlock(); + targetBlock->setBBProfileWeight(targetBlock->computeIncomingWeight()); + profileInconsistent |= (targetBlock->NumSucc() > 0); + } + + if (profileInconsistent) + { + JITDUMP("Switch lowering: Flow out of " FMT_BB " needs to be propagated. Data %s inconsistent.\n", + afterDefaultCondBlock->bbNum, comp->fgPgoConsistent ? "is now" : "was already"); + comp->fgPgoConsistent = false; + } + } } else { @@ -1265,11 +1285,28 @@ GenTree* Lowering::LowerSwitch(GenTree* node) JITDUMP("Zero weight switch block " FMT_BB ", distributing likelihoods equally per case\n", afterDefaultCondBlock->bbNum); // jumpCnt-1 here because we peeled the default after copying this value. - weight_t const newLikelihood = 1.0 / (jumpCnt - 1); + weight_t const newLikelihood = 1.0 / (jumpCnt - 1); + bool profileInconsistent = false; for (unsigned i = 0; i < successors.numDistinctSuccs; i++) { - FlowEdge* const edge = successors.nonDuplicates[i]; + FlowEdge* const edge = successors.nonDuplicates[i]; + weight_t const oldEdgeWeight = edge->getLikelyWeight(); edge->setLikelihood(newLikelihood * edge->getDupCount()); + weight_t const newEdgeWeight = edge->getLikelyWeight(); + + if (afterDefaultCondBlock->hasProfileWeight()) + { + BasicBlock* const targetBlock = edge->getDestinationBlock(); + targetBlock->increaseBBProfileWeight(newEdgeWeight - oldEdgeWeight); + profileInconsistent |= (targetBlock->NumSucc() > 0); + } + } + + if (profileInconsistent) + { + JITDUMP("Switch lowering: Flow out of " FMT_BB " needs to be propagated. Data %s inconsistent.\n", + afterDefaultCondBlock->bbNum, comp->fgPgoConsistent ? "is now" : "was already"); + comp->fgPgoConsistent = false; } } else @@ -1452,6 +1489,22 @@ bool Lowering::TryLowerSwitchToBitTest(FlowEdge* jumpTable[], bbSwitch->SetCond(case1Edge, case0Edge); + // + // Update profile + // + if (bbSwitch->hasProfileWeight()) + { + bbCase0->setBBProfileWeight(bbCase0->computeIncomingWeight()); + bbCase1->setBBProfileWeight(bbCase1->computeIncomingWeight()); + + if ((bbCase0->NumSucc() > 0) || (bbCase1->NumSucc() > 0)) + { + JITDUMP("TryLowerSwitchToBitTest: Flow out of " FMT_BB " needs to be propagated. Data %s inconsistent.\n", + bbSwitch->bbNum, comp->fgPgoConsistent ? "is now" : "was already"); + comp->fgPgoConsistent = false; + } + } + var_types bitTableType = (bitCount <= (genTypeSize(TYP_INT) * 8)) ? TYP_INT : TYP_LONG; GenTree* bitTableIcon = comp->gtNewIconNode(bitTable, bitTableType); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index f427e3fc068d07..4642af455804a6 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -2240,6 +2240,8 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block) // bNewCond->inheritWeight(block); + const weight_t totalWeight = bTest->bbWeight; + if (haveProfileWeights) { bTest->decreaseBBProfileWeight(block->bbWeight); @@ -2300,6 +2302,15 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block) } } + const weight_t loopWeight = bTest->bbWeight; + const weight_t nonLoopWeight = bNewCond->bbWeight; + if (haveProfileWeights && !fgProfileWeightsConsistent(totalWeight, loopWeight + nonLoopWeight)) + { + JITDUMP("Redirecting flow from " FMT_BB " to " FMT_BB " introduced inconsistency. Data %s inconsistent.\n", + bTest->bbNum, bNewCond->bbNum, fgPgoConsistent ? "is now" : "was already"); + fgPgoConsistent = false; + } + #ifdef DEBUG if (verbose) { From 84f4c2ae9f5e3c10b994e1c6b8ccbf00c8c151cb Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Thu, 30 Jan 2025 10:15:04 -0800 Subject: [PATCH 29/31] JIT: Run 3-opt once across all regions (#111989) Part of #107749. 3-opt layout already refuses to align branches that cross EH regions, so there doesn't seem to be much utility in reordering each EH region independently. Thus, remove 3-opt's per-region ordering constraints, and run 3-opt once. --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/fgopt.cpp | 82 +++++++++++--------------------------- 2 files changed, 24 insertions(+), 61 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 473dc3dc47153a..df37e7ff272fde 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6326,7 +6326,6 @@ class Compiler BasicBlock** blockOrder; BasicBlock** tempOrder; unsigned numCandidateBlocks; - unsigned currEHRegion; #ifdef DEBUG weight_t GetLayoutCost(unsigned startPos, unsigned endPos); @@ -6341,7 +6340,7 @@ class Compiler void AddNonFallthroughPreds(unsigned blockPos); bool RunGreedyThreeOptPass(unsigned startPos, unsigned endPos); - bool RunThreeOptPass(BasicBlock* startBlock, BasicBlock* endBlock); + bool RunThreeOptPass(); public: ThreeOptLayout(Compiler* comp); diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 244d8bcd9efc1f..2c0ca450bc8243 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -4931,7 +4931,6 @@ Compiler::ThreeOptLayout::ThreeOptLayout(Compiler* comp) , blockOrder(nullptr) , tempOrder(nullptr) , numCandidateBlocks(0) - , currEHRegion(0) { } @@ -5125,7 +5124,7 @@ void Compiler::ThreeOptLayout::ConsiderEdge(FlowEdge* edge) BasicBlock* const dstBlk = edge->getDestinationBlock(); // Ignore cross-region branches - if ((srcBlk->bbTryIndex != currEHRegion) || (dstBlk->bbTryIndex != currEHRegion)) + if (!BasicBlock::sameTryRegion(srcBlk, dstBlk)) { return; } @@ -5224,8 +5223,7 @@ void Compiler::ThreeOptLayout::AddNonFallthroughPreds(unsigned blockPos) } //----------------------------------------------------------------------------- -// Compiler::ThreeOptLayout::Run: Runs 3-opt for each contiguous region of the block list -// we're interested in reordering. +// Compiler::ThreeOptLayout::Run: Runs 3-opt on the candidate span of hot blocks. // We skip reordering handler regions for now, as these are assumed to be cold. // void Compiler::ThreeOptLayout::Run() @@ -5271,41 +5269,9 @@ void Compiler::ThreeOptLayout::Run() // Repurpose 'bbPostorderNum' for the block's ordinal block->bbPostorderNum = numCandidateBlocks++; - - // While walking the span of blocks to reorder, - // remember where each try region ends within this span. - // We'll use this information to run 3-opt per region. - EHblkDsc* const HBtab = compiler->ehGetBlockTryDsc(block); - if (HBtab != nullptr) - { - HBtab->ebdTryLast = block; - } - } - - // Reorder try regions first - bool modified = false; - for (EHblkDsc* const HBtab : EHClauses(compiler)) - { - // If multiple region indices map to the same region, - // make sure we reorder its blocks only once - BasicBlock* const tryBeg = HBtab->ebdTryBeg; - if (tryBeg->getTryIndex() != currEHRegion++) - { - continue; - } - - // Only reorder try regions within the candidate span of blocks - if ((tryBeg->bbPostorderNum < numCandidateBlocks) && (blockOrder[tryBeg->bbPostorderNum] == tryBeg)) - { - JITDUMP("Running 3-opt for try region #%d\n", (currEHRegion - 1)); - modified |= RunThreeOptPass(tryBeg, HBtab->ebdTryLast); - } } - // Finally, reorder the main method body - currEHRegion = 0; - JITDUMP("Running 3-opt for main method body\n"); - modified |= RunThreeOptPass(compiler->fgFirstBB, blockOrder[numCandidateBlocks - 1]); + const bool modified = RunThreeOptPass(); if (modified) { @@ -5314,14 +5280,25 @@ void Compiler::ThreeOptLayout::Run() BasicBlock* const block = blockOrder[i - 1]; BasicBlock* const next = blockOrder[i]; + if (block->NextIs(next)) + { + continue; + } + // Only reorder within EH regions to maintain contiguity. - // TODO: Allow moving blocks in different regions when 'next' is the region entry. - // This would allow us to move entire regions up/down because of the contiguity requirement. - if (!block->NextIs(next) && BasicBlock::sameEHRegion(block, next)) + if (!BasicBlock::sameEHRegion(block, next)) + { + continue; + } + + // Don't move the entry of an EH region. + if (compiler->bbIsTryBeg(next) || compiler->bbIsHandlerBeg(next)) { - compiler->fgUnlinkBlock(next); - compiler->fgInsertBBafter(block, next); + continue; } + + compiler->fgUnlinkBlock(next); + compiler->fgInsertBBafter(block, next); } } } @@ -5466,12 +5443,6 @@ bool Compiler::ThreeOptLayout::RunGreedyThreeOptPass(unsigned startPos, unsigned continue; } - // Don't consider any cut points that would disturb other EH regions - if (!BasicBlock::sameEHRegion(s2Block, s3Block)) - { - continue; - } - // Compute the cost delta of this partition const weight_t currCost = currCostBase + GetCost(s3BlockPrev, s3Block); const weight_t newCost = @@ -5529,22 +5500,15 @@ bool Compiler::ThreeOptLayout::RunGreedyThreeOptPass(unsigned startPos, unsigned } //----------------------------------------------------------------------------- -// Compiler::ThreeOptLayout::RunThreeOptPass: Runs 3-opt for the given block range. -// -// Parameters: -// startBlock - The first block of the range to reorder -// endBlock - The last block (inclusive) of the range to reorder +// Compiler::ThreeOptLayout::RunThreeOptPass: Runs 3-opt on the candidate span of blocks. // // Returns: // True if we reordered anything, false otherwise // -bool Compiler::ThreeOptLayout::RunThreeOptPass(BasicBlock* startBlock, BasicBlock* endBlock) +bool Compiler::ThreeOptLayout::RunThreeOptPass() { - assert(startBlock != nullptr); - assert(endBlock != nullptr); - - const unsigned startPos = startBlock->bbPostorderNum; - const unsigned endPos = endBlock->bbPostorderNum; + const unsigned startPos = 0; + const unsigned endPos = numCandidateBlocks - 1; const unsigned numBlocks = (endPos - startPos + 1); assert(startPos <= endPos); From 56b7d0e939e5d4d5eab3c0a8b6337864c17de236 Mon Sep 17 00:00:00 2001 From: Rob Hague Date: Thu, 30 Jan 2025 20:10:28 +0100 Subject: [PATCH 30/31] Fix BigInteger outerloop test (#111841) * Fix BigInteger outerloop test This has presumably been failing since capping BigInteger to a maximum length * Update src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs * 2MB * convert ToString_ValidLargeFormat to inner loop --- .../tests/BigInteger/BigIntegerToStringTests.cs | 17 +++++++++-------- .../tests/BigInteger/GetBitLengthTests.cs | 6 ++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs index f0219a252787a6..8aec40e50d4e13 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/BigIntegerToStringTests.cs @@ -521,22 +521,23 @@ public static void ToString_InvalidFormat_ThrowsFormatException() Assert.Throws(() => b.ToString("G000001000000000")); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // Requires a lot of memory - [OuterLoop("Takes a long time, allocates a lot of memory")] - [SkipOnMono("Frequently throws OOM on Mono")] + [Fact] public static void ToString_ValidLargeFormat() { BigInteger b = new BigInteger(123456789000m); // Format precision limit is 999_999_999 (9 digits). Anything larger should throw. + // We use TryFormat rather than ToString to avoid excessive memory usage. - // Check ParseFormatSpecifier in FormatProvider.Number.cs with `E` format - b.ToString("E999999999"); // Should not throw - b.ToString("E00000999999999"); // Should not throw + // Check ParseFormatSpecifier in FormatProvider.Number.cs with `E` format. + // Currently disabled since these would still allocate a 2GB buffer before + // returning, leading to OOM in CI. + //Assert.False(b.TryFormat(Span.Empty, out _, format: "E999999999")); // Should not throw + //Assert.False(b.TryFormat(Span.Empty, out _, format: "E00000999999999")); // Should not throw // Check ParseFormatSpecifier in Number.BigInteger.cs with `G` format - b.ToString("G999999999"); // Should not throw - b.ToString("G00000999999999"); // Should not throw + Assert.False(b.TryFormat(Span.Empty, out _, format: "G999999999")); // Should not throw + Assert.False(b.TryFormat(Span.Empty, out _, format: "G00000999999999")); // Should not throw } private static void RunSimpleProviderToStringTests(Random random, string format, NumberFormatInfo provider, int precision, StringFormatter formatter) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs index ab02a31ad5b17f..f8422070565db3 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/GetBitLengthTests.cs @@ -44,8 +44,10 @@ public static void RunGetBitLengthTests() public static void RunGetBitLengthTestsLarge() { // Very large cases - VerifyGetBitLength(BigInteger.One << 32 << int.MaxValue, int.MaxValue + 32L + 1, 1); - VerifyGetBitLength(BigInteger.One << 64 << int.MaxValue, int.MaxValue + 64L + 1, 1); + // Values which are large but beneath the upper bound of + // (2^31) - 1 bits and which should not cause OOM in CI. + VerifyGetBitLength(BigInteger.One << 32 << (1 << 24), (1 << 24) + 32L + 1, 1); + VerifyGetBitLength(BigInteger.One << 64 << (1 << 24), (1 << 24) + 64L + 1, 1); } private static void VerifyLoopGetBitLength(Random random, bool isSmall) From 586e891dcd085befe279190591fa18613ab7854b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 30 Jan 2025 22:28:54 +0100 Subject: [PATCH 31/31] More native AOT Pri-1 test tree bring up (#111994) I've also dropped `RequiresProcessIsolation` from tests while I was looking at them. --- src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs | 2 ++ .../MissingHostPolicyTests/MissingHostPolicyTests.csproj | 2 ++ .../ResolvedFromDifferentContext.cs | 3 ++- .../ResolvedFromDifferentContext.csproj | 1 + src/tests/Loader/NativeLibs/FromNativePaths.csproj | 2 ++ .../classloader/MethodImpl/generics_override1.ilproj | 3 --- .../classloader/MethodImpl/override_override1.ilproj | 4 ++-- .../Loader/classloader/MethodImpl/self_override1.ilproj | 2 -- .../Loader/classloader/MethodImpl/self_override2.ilproj | 2 -- .../Loader/classloader/MethodImpl/self_override3.ilproj | 4 ++-- .../Loader/classloader/MethodImpl/self_override5.ilproj | 2 -- .../methodoverriding/regressions/576621/VSW576621.csproj | 2 -- src/tests/issues.targets | 9 +++++++++ 13 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs index f0118d60db3435..0a47a6a8ee9709 100644 --- a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs +++ b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs @@ -25,6 +25,8 @@ public static class PlatformDetection public static bool IsRareEnumsSupported => !Utilities.IsNativeAot; + public static bool IsCollectibleAssembliesSupported => !Utilities.IsNativeAot; + private static volatile Tuple s_lazyNonZeroLowerBoundArraySupported; public static bool IsNonZeroLowerBoundArraySupported { diff --git a/src/tests/Loader/AssemblyDependencyResolver/MissingHostPolicyTests/MissingHostPolicyTests.csproj b/src/tests/Loader/AssemblyDependencyResolver/MissingHostPolicyTests/MissingHostPolicyTests.csproj index 7d064de314ac85..8e2352001bc13e 100644 --- a/src/tests/Loader/AssemblyDependencyResolver/MissingHostPolicyTests/MissingHostPolicyTests.csproj +++ b/src/tests/Loader/AssemblyDependencyResolver/MissingHostPolicyTests/MissingHostPolicyTests.csproj @@ -3,6 +3,8 @@ true 1 + + true diff --git a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs index d15e33dc051c40..ae71a4ac5c6492 100644 --- a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.cs @@ -14,6 +14,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.IO; +using TestLibrary; using Xunit; class TestAssemblyLoadContext : AssemblyLoadContext @@ -207,7 +208,7 @@ private static int TestFullUnload(TestCase testCase) return 100; } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsCollectibleAssembliesSupported))] public static int TestEntryPoint() { int status = 100; diff --git a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj index d232fb7ec1c7e8..eddb28d22e5e91 100644 --- a/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj +++ b/src/tests/Loader/CollectibleAssemblies/ResolvedFromDifferentContext/ResolvedFromDifferentContext.csproj @@ -13,6 +13,7 @@ + diff --git a/src/tests/Loader/NativeLibs/FromNativePaths.csproj b/src/tests/Loader/NativeLibs/FromNativePaths.csproj index 64d8810f29a548..74250016f080bf 100644 --- a/src/tests/Loader/NativeLibs/FromNativePaths.csproj +++ b/src/tests/Loader/NativeLibs/FromNativePaths.csproj @@ -4,6 +4,8 @@ true 1 true + + true diff --git a/src/tests/Loader/classloader/MethodImpl/generics_override1.ilproj b/src/tests/Loader/classloader/MethodImpl/generics_override1.ilproj index 90f2ad5e53d574..8dc52a8a2682da 100644 --- a/src/tests/Loader/classloader/MethodImpl/generics_override1.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/generics_override1.ilproj @@ -1,8 +1,5 @@ - - true - true diff --git a/src/tests/Loader/classloader/MethodImpl/override_override1.ilproj b/src/tests/Loader/classloader/MethodImpl/override_override1.ilproj index 35d314f33711b0..c2baddccc78d10 100644 --- a/src/tests/Loader/classloader/MethodImpl/override_override1.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/override_override1.ilproj @@ -1,8 +1,8 @@ - - true 1 + + true diff --git a/src/tests/Loader/classloader/MethodImpl/self_override1.ilproj b/src/tests/Loader/classloader/MethodImpl/self_override1.ilproj index 8a17c63cb6398b..76f26ba97028d3 100644 --- a/src/tests/Loader/classloader/MethodImpl/self_override1.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/self_override1.ilproj @@ -1,7 +1,5 @@ - - true 1 diff --git a/src/tests/Loader/classloader/MethodImpl/self_override2.ilproj b/src/tests/Loader/classloader/MethodImpl/self_override2.ilproj index 02141708da0f6c..3cbb5dfc1190d0 100644 --- a/src/tests/Loader/classloader/MethodImpl/self_override2.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/self_override2.ilproj @@ -1,7 +1,5 @@ - - true 1 diff --git a/src/tests/Loader/classloader/MethodImpl/self_override3.ilproj b/src/tests/Loader/classloader/MethodImpl/self_override3.ilproj index 861e30351d3ca2..e895df0df22bfb 100644 --- a/src/tests/Loader/classloader/MethodImpl/self_override3.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/self_override3.ilproj @@ -1,8 +1,8 @@ - - true 1 + + true diff --git a/src/tests/Loader/classloader/MethodImpl/self_override5.ilproj b/src/tests/Loader/classloader/MethodImpl/self_override5.ilproj index b1e631f7d6436f..0eac764ff169fb 100644 --- a/src/tests/Loader/classloader/MethodImpl/self_override5.ilproj +++ b/src/tests/Loader/classloader/MethodImpl/self_override5.ilproj @@ -1,7 +1,5 @@ - - true 1 diff --git a/src/tests/Loader/classloader/methodoverriding/regressions/576621/VSW576621.csproj b/src/tests/Loader/classloader/methodoverriding/regressions/576621/VSW576621.csproj index 02624af16b85f9..706c87a3e6f35d 100644 --- a/src/tests/Loader/classloader/methodoverriding/regressions/576621/VSW576621.csproj +++ b/src/tests/Loader/classloader/methodoverriding/regressions/576621/VSW576621.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 3e5e3c5b88d8d7..3549319d01c23c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -653,6 +653,15 @@ https://github.com/dotnet/runtime/issues/72589 + + https://github.com/dotnet/runtime/issues/111991 + + + https://github.com/dotnet/runtime/issues/111991 + + + https://github.com/dotnet/runtime/issues/111991 + https://github.com/dotnet/runtimelab/issues/155: Compatible TypeLoadException for invalid inputs