From 709444f29ebc548c5d33e5993d4d7bb01a5d3520 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 17 May 2023 10:23:45 -0400 Subject: [PATCH 01/12] =?UTF-8?q?Special-case=20array/list=20in=20Dictiona?= =?UTF-8?q?ry(IEnumerable,=20=E2=80=A6)=20ctors=20(#86254)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Special-case array/list in Dictionary(IEnumerable, ...) ctors It's reasonably common to construct a dictionary from a `KeyValuePair[]` or a `List>`, and we can make both cheaper. --- .../System/Collections/Generic/Dictionary.cs | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index daaceaefef1ad..5291c2b6a7d00 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace System.Collections.Generic @@ -83,7 +85,7 @@ public Dictionary(int capacity, IEqualityComparer? comparer) public Dictionary(IDictionary dictionary) : this(dictionary, null) { } public Dictionary(IDictionary dictionary, IEqualityComparer? comparer) : - this(dictionary != null ? dictionary.Count : 0, comparer) + this(dictionary?.Count ?? 0, comparer) { if (dictionary == null) { @@ -106,15 +108,15 @@ public Dictionary(IEnumerable> collection, IEqualityC AddRange(collection); } - private void AddRange(IEnumerable> collection) + private void AddRange(IEnumerable> enumerable) { - // It is likely that the passed-in dictionary is Dictionary. When this is the case, + // It is likely that the passed-in enumerable is Dictionary. When this is the case, // avoid the enumerator allocation and overhead by looping through the entries array directly. // We only do this when dictionary is Dictionary and not a subclass, to maintain // back-compat with subclasses that may have overridden the enumerator behavior. - if (collection.GetType() == typeof(Dictionary)) + if (enumerable.GetType() == typeof(Dictionary)) { - Dictionary source = (Dictionary)collection; + Dictionary source = (Dictionary)enumerable; if (source.Count == 0) { @@ -150,8 +152,29 @@ private void AddRange(IEnumerable> collection) return; } - // Fallback path for IEnumerable that isn't a non-subclassed Dictionary. - foreach (KeyValuePair pair in collection) + // We similarly special-case KVP<>[] and List>, as they're commonly used to seed dictionaries, and + // we want to avoid the enumerator costs (e.g. allocation) for them as well. Extract a span if possible. + ReadOnlySpan> span; + if (enumerable is KeyValuePair[] array) + { + span = array; + } + else if (enumerable.GetType() == typeof(List>)) + { + span = CollectionsMarshal.AsSpan((List>)enumerable); + } + else + { + // Fallback path for all other enumerables + foreach (KeyValuePair pair in enumerable) + { + Add(pair.Key, pair.Value); + } + return; + } + + // We got a span. Add the elements to the dictionary. + foreach (KeyValuePair pair in span) { Add(pair.Key, pair.Value); } From 95c83fc389cb1cf9618636da94106070eceda884 Mon Sep 17 00:00:00 2001 From: LateApexEarlySpeed <72254037+lateapexearlyspeed@users.noreply.github.com> Date: Wed, 17 May 2023 22:25:35 +0800 Subject: [PATCH 02/12] Add ToDictionary() overloads for KeyValuePair and ValueTuple kv. (#85811) * Initial commit to add ToDictionary() overloads for KeyValuePair and ValueTuple kv. * Add xml doc; add tests. * Fix comments: use Dictionary's ctor for IEnumerable> overload; API signature changed to nullable comparer. * Throw ArgumentNullExp with 'source' parameter name. --- src/libraries/System.Linq/ref/System.Linq.cs | 4 + .../src/System/Linq/ToCollection.cs | 63 +++++++++ .../System.Linq/tests/ToDictionaryTests.cs | 130 +++++++++++++++++- 3 files changed, 195 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Linq/ref/System.Linq.cs b/src/libraries/System.Linq/ref/System.Linq.cs index a35e99ba3c232..e49127be6c737 100644 --- a/src/libraries/System.Linq/ref/System.Linq.cs +++ b/src/libraries/System.Linq/ref/System.Linq.cs @@ -203,6 +203,10 @@ public static System.Collections.Generic.IEnumerable< public static System.Linq.IOrderedEnumerable ThenBy(this System.Linq.IOrderedEnumerable source, System.Func keySelector) { throw null; } public static System.Linq.IOrderedEnumerable ThenBy(this System.Linq.IOrderedEnumerable source, System.Func keySelector, System.Collections.Generic.IComparer? comparer) { throw null; } public static TSource[] ToArray(this System.Collections.Generic.IEnumerable source) { throw null; } + public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable> source) where TKey : notnull { throw null; } + public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable> source, System.Collections.Generic.IEqualityComparer? comparer) where TKey : notnull { throw null; } + public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable<(TKey Key, TValue Value)> source) where TKey : notnull { throw null; } + public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable<(TKey Key, TValue Value)> source, System.Collections.Generic.IEqualityComparer? comparer) where TKey : notnull { throw null; } public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable source, System.Func keySelector) where TKey : notnull { throw null; } public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Collections.Generic.IEqualityComparer? comparer) where TKey : notnull { throw null; } public static System.Collections.Generic.Dictionary ToDictionary(this System.Collections.Generic.IEnumerable source, System.Func keySelector, System.Func elementSelector) where TKey : notnull { throw null; } diff --git a/src/libraries/System.Linq/src/System/Linq/ToCollection.cs b/src/libraries/System.Linq/src/System/Linq/ToCollection.cs index 038a47e93160c..280ced4d34f62 100644 --- a/src/libraries/System.Linq/src/System/Linq/ToCollection.cs +++ b/src/libraries/System.Linq/src/System/Linq/ToCollection.cs @@ -29,6 +29,69 @@ public static List ToList(this IEnumerable source) return source is IIListProvider listProvider ? listProvider.ToList() : new List(source); } + /// + /// Creates a from an according to the default comparer for the key type. + /// + /// The type of the keys from elements of + /// The type of the values from elements of + /// The to create a from. + /// A that contains keys and values from and uses default comparer for the key type. + /// is a null reference. + /// contains one or more duplicate keys. + public static Dictionary ToDictionary(this IEnumerable> source) where TKey : notnull => + source.ToDictionary(null); + + /// + /// Creates a from an according to specified key comparer. + /// + /// The type of the keys from elements of + /// The type of the values from elements of + /// The to create a from. + /// An to compare keys. + /// A that contains keys and values from . + /// is a null reference. + /// contains one or more duplicate keys. + /// + /// If is null, the default equality comparer is used to compare keys. + /// + public static Dictionary ToDictionary(this IEnumerable> source, IEqualityComparer? comparer) where TKey : notnull + { + if (source is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + } + + return new(source, comparer); + } + + /// + /// Creates a from an according to the default comparer for the key type. + /// + /// The type of the keys from elements of + /// The type of the values from elements of + /// The to create a from. + /// A that contains keys and values from and uses default comparer for the key type. + /// is a null reference. + /// contains one or more duplicate keys. + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) where TKey : notnull => + source.ToDictionary(null); + + /// + /// Creates a from an according to specified key comparer. + /// + /// The type of the keys from elements of + /// The type of the values from elements of + /// The to create a from. + /// An to compare keys. + /// A that contains keys and values from . + /// is a null reference. + /// contains one or more duplicate keys. + /// + /// If is null, the default equality comparer is used to compare keys. + /// + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, IEqualityComparer? comparer) where TKey : notnull => + source.ToDictionary(vt => vt.Key, vt => vt.Value, comparer); + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector) where TKey : notnull => ToDictionary(source, keySelector, null); diff --git a/src/libraries/System.Linq/tests/ToDictionaryTests.cs b/src/libraries/System.Linq/tests/ToDictionaryTests.cs index c3dcc7a6e42db..5991b28e9920f 100644 --- a/src/libraries/System.Linq/tests/ToDictionaryTests.cs +++ b/src/libraries/System.Linq/tests/ToDictionaryTests.cs @@ -20,6 +20,11 @@ public void ToDictionary_AlwaysCreateACopy() Assert.NotSame(source, result); Assert.Equal(source, result); + + result = source.ToDictionary(); + + Assert.NotSame(source, result); + Assert.Equal(source, result); } @@ -37,6 +42,24 @@ private void RunToDictionaryOnAllCollectionTypes(T[] items, Action(items).ToDictionary(key => key, value => value)); } + private void RunToDictionaryFromKvOnAllCollectionTypes(T[] items, Action> validation) where T : notnull + { + KeyValuePair[] kvps = items.Select(item => new KeyValuePair(item, item)).ToArray(); + + validation(kvps.ToDictionary()); + validation(new List>(kvps).ToDictionary()); + validation(new TestEnumerable>(kvps).ToDictionary()); + validation(new TestReadOnlyCollection>(kvps).ToDictionary()); + validation(new TestCollection>(kvps).ToDictionary()); + + (T, T)[] vts = items.Select(item => (item, item)).ToArray(); + + validation(vts.ToDictionary()); + validation(new List<(T, T)>(vts).ToDictionary()); + validation(new TestEnumerable<(T, T)>(vts).ToDictionary()); + validation(new TestReadOnlyCollection<(T, T)>(vts).ToDictionary()); + validation(new TestCollection<(T, T)>(vts).ToDictionary()); + } [Fact] public void ToDictionary_WorkWithEmptyCollection() @@ -49,6 +72,16 @@ public void ToDictionary_WorkWithEmptyCollection() }); } + [Fact] + public void ToDictionaryFromKv_WorkWithEmptyCollection() + { + RunToDictionaryFromKvOnAllCollectionTypes(Array.Empty(), + resultDictionary => + { + Assert.NotNull(resultDictionary); + Assert.Empty(resultDictionary); + }); + } [Fact] public void ToDictionary_ProduceCorrectDictionary() @@ -72,12 +105,44 @@ public void ToDictionary_ProduceCorrectDictionary() }); } + [Fact] + public void ToDictionaryFromKv_ProduceCorrectDictionary() + { + int[] sourceArray = new[] { 1, 2, 3, 4, 5, 6, 7 }; + RunToDictionaryFromKvOnAllCollectionTypes(sourceArray, + resultDictionary => + { + Assert.Equal(sourceArray.Length, resultDictionary.Count); + Assert.Equal(sourceArray, resultDictionary.Keys); + Assert.Equal(sourceArray, resultDictionary.Values); + }); + + string[] sourceStringArray = new[] { "1", "2", "3", "4", "5", "6", "7", "8" }; + RunToDictionaryFromKvOnAllCollectionTypes(sourceStringArray, + resultDictionary => + { + Assert.Equal(sourceStringArray.Length, resultDictionary.Count); + foreach (string item in sourceStringArray) + { + Assert.Same(item, resultDictionary[item]); + } + }); + } + [Fact] public void RunOnce() { Assert.Equal( new Dictionary {{1, "0"}, {2, "1"}, {3, "2"}, {4, "3"}}, Enumerable.Range(0, 4).RunOnce().ToDictionary(i => i + 1, i => i.ToString())); + + Assert.Equal( + new Dictionary { { 0, "0" }, { 1, "1" }, { 2, "2" }, { 3, "3" } }, + Enumerable.Range(0, 4).Select(i => new KeyValuePair(i, i.ToString())).RunOnce().ToDictionary()); + + Assert.Equal( + new Dictionary { { 0, "0" }, { 1, "1" }, { 2, "2" }, { 3, "3" } }, + Enumerable.Range(0, 4).Select(i => (i, i.ToString())).RunOnce().ToDictionary()); } [Fact] @@ -91,6 +156,12 @@ public void ToDictionary_PassCustomComparer() Dictionary result2 = collection.ToDictionary(key => key, val => val, comparer); Assert.Same(comparer, result2.Comparer); + + Dictionary result3 = collection.Select(i => new KeyValuePair(i, i)).ToDictionary(comparer); + Assert.Same(comparer, result3.Comparer); + + Dictionary result4 = collection.Select(i => (i, i)).ToDictionary(comparer); + Assert.Same(comparer, result4.Comparer); } [Fact] @@ -105,6 +176,24 @@ public void ToDictionary_UseDefaultComparerOnNull() Assert.Same(EqualityComparer.Default, result2.Comparer); } + [Fact] + public void ToDictionary_UseDefaultComparer() + { + TestCollection collection = new TestCollection(new[] { 1, 2, 3, 4, 5, 6 }); + + Dictionary result1 = collection.ToDictionary(key => key); + Assert.Same(EqualityComparer.Default, result1.Comparer); + + Dictionary result2 = collection.ToDictionary(key => key, val => val); + Assert.Same(EqualityComparer.Default, result2.Comparer); + + Dictionary result3 = collection.Select(i => new KeyValuePair(i, i)).ToDictionary(); + Assert.Same(EqualityComparer.Default, result3.Comparer); + + Dictionary result4 = collection.Select(i => (i, i)).ToDictionary(); + Assert.Same(EqualityComparer.Default, result4.Comparer); + } + [Fact] public void ToDictionary_KeyValueSelectorsWork() { @@ -120,8 +209,9 @@ public void ToDictionary_KeyValueSelectorsWork() [Fact] public void ToDictionary_ThrowArgumentNullExceptionWhenSourceIsNull() { - int[] source = null; - AssertExtensions.Throws("source", () => source.ToDictionary(key => key)); + AssertExtensions.Throws("source", () => ((IEnumerable)null).ToDictionary(key => key)); + AssertExtensions.Throws("source", () => ((IEnumerable>)null).ToDictionary()); + AssertExtensions.Throws("source", () => ((IEnumerable<(int, int)>)null).ToDictionary()); } @@ -226,6 +316,24 @@ public void ThrowsOnNullKey() }; AssertExtensions.Throws("key", () => source.ToDictionary(e => e.Name)); + + var source2 = new KeyValuePair[] + { + new("Chris", 50), + new("Bob", 95), + new(default, 55) + }; + + AssertExtensions.Throws("key", () => source2.ToDictionary()); + + var source3 = new[] + { + ("Chris", 50), + ("Bob", 95), + (default, 55) + }; + + AssertExtensions.Throws("key", () => source3.ToDictionary()); } [Fact] @@ -305,6 +413,24 @@ public void ThrowsOnDuplicateKeys() }; AssertExtensions.Throws(null, () => source.ToDictionary(e => e.Name, e => e, new AnagramEqualityComparer())); + + var source2 = new KeyValuePair[] + { + new("Chris", 50), + new("Bob", 95), + new("Bob", 55) + }; + + AssertExtensions.Throws(null, () => source2.ToDictionary(new AnagramEqualityComparer())); + + var source3 = new[] + { + ("Chris", 50), + ("Bob", 95), + ("Bob", 55) + }; + + AssertExtensions.Throws(null, () => source3.ToDictionary(new AnagramEqualityComparer())); } private static void AssertMatches(IEnumerable keys, IEnumerable values, Dictionary dict) From a776b0a42ac89fa035c646d3918e69eaa7d5b704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 17 May 2023 23:50:46 +0900 Subject: [PATCH 03/12] Include Native AOT pdbs in Helix payload (#86367) --- src/tests/Common/helixpublishwitharcade.proj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tests/Common/helixpublishwitharcade.proj b/src/tests/Common/helixpublishwitharcade.proj index f5bee870b1762..bd6f60135829e 100644 --- a/src/tests/Common/helixpublishwitharcade.proj +++ b/src/tests/Common/helixpublishwitharcade.proj @@ -262,8 +262,9 @@ - + This is for performance reasons to reduce our helix payload size + Note: for Native AOT the pdb is the native PDB. !analyze wouldn't work without it at all so we keep it --> + From 7692f981f825bb9aec8d6eba7e12054c6dcdfaee Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 17 May 2023 16:58:32 +0200 Subject: [PATCH 04/12] JIT: Stop specifying general JitStress in OSR/largefuncletframe (#86380) Otherwise new stress modes can change what the test is testing. I verified that these stress modes reproduced the original issue back when the test was introduced. Fix #86185 --- src/tests/JIT/opt/OSR/largefuncletframe.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/opt/OSR/largefuncletframe.csproj b/src/tests/JIT/opt/OSR/largefuncletframe.csproj index 9777246ee0815..20b843f9edbea 100644 --- a/src/tests/JIT/opt/OSR/largefuncletframe.csproj +++ b/src/tests/JIT/opt/OSR/largefuncletframe.csproj @@ -14,6 +14,6 @@ - + From 445dac9e8e541b2364deea000dde8487ea1ec20e Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Thu, 18 May 2023 01:10:39 +1000 Subject: [PATCH 05/12] Initial build configuration for Haiku (#86303) * Haiku: Initial configuration support Initially recognize Haiku as a supported platform in Directory.Build.props and all scripts in eng/**. * Drop some unused configure values Drop some unused configure values from tryrun.cmake, configure.cmake and config.h.in. * Simplify error message in build.sh Simplified an error message for the `--os` option to avoid maintaining a duplicate list of OSes. Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- Directory.Build.props | 6 +- eng/build.sh | 6 +- eng/native/configurecompiler.cmake | 9 ++- eng/native/configureplatform.cmake | 11 ++++ eng/native/init-distro-rid.sh | 3 + eng/native/init-os-and-arch.sh | 2 +- eng/native/tryrun.cmake | 24 +++----- eng/native/tryrun_ios_tvos.cmake | 4 -- eng/versioning.targets | 1 + src/coreclr/pal/src/config.h.in | 4 -- src/coreclr/pal/src/configure.cmake | 90 ----------------------------- 11 files changed, 41 insertions(+), 119 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cab8c8df24eb3..902f793b971d2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,7 @@ <_hostOS Condition="$([MSBuild]::IsOSPlatform('NETBSD'))">netbsd <_hostOS Condition="$([MSBuild]::IsOSPlatform('ILLUMOS'))">illumos <_hostOS Condition="$([MSBuild]::IsOSPlatform('SOLARIS'))">solaris + <_hostOS Condition="$([MSBuild]::IsOSPlatform('HAIKU'))">haiku <_hostOS Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">windows $(_hostOS) browser @@ -257,6 +258,7 @@ true true true + true true true true @@ -271,7 +273,7 @@ true true true - true + true @@ -401,7 +403,7 @@ '$(UsingMicrosoftNoTargetsSdk)' != 'true' and '$(UsingMicrosoftTraversalSdk)' != 'true'">true - + false diff --git a/eng/build.sh b/eng/build.sh index 469fd9d06714b..bfe3d351e2a62 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -32,7 +32,7 @@ usage() echo " [Default: Debug]" echo " --os Target operating system: windows, linux, freebsd, osx, maccatalyst, tvos," echo " tvossimulator, ios, iossimulator, android, browser, wasi, netbsd, illumos, solaris" - echo " linux-musl or linux-bionic." + echo " linux-musl, linux-bionic or haiku." echo " [Default: Your machine's OS.]" echo " --outputrid Optional argument that overrides the target rid name." echo " --projects Project or solution file(s) to build." @@ -296,9 +296,11 @@ while [[ $# > 0 ]]; do os="linux" __PortableTargetOS=linux-musl ;; + haiku) + os="haiku" ;; *) echo "Unsupported target OS '$2'." - echo "The allowed values are windows, linux, freebsd, osx, maccatalyst, tvos, tvossimulator, ios, iossimulator, android, browser, wasi, illumos and solaris." + echo "Try 'build.sh --help' for values supported by '--os'." exit 1 ;; esac diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index fbe60f71f5237..ee8e324db96f8 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -214,6 +214,9 @@ elseif(CLR_CMAKE_HOST_SUNOS) elseif(CLR_CMAKE_HOST_OSX AND NOT CLR_CMAKE_HOST_MACCATALYST AND NOT CLR_CMAKE_HOST_IOS AND NOT CLR_CMAKE_HOST_TVOS) add_definitions(-D_XOPEN_SOURCE) add_linker_flag("-Wl,-bind_at_load") +elseif(CLR_CMAKE_HOST_HAIKU) + add_compile_options($<$:-Wa,--noexecstack>) + add_linker_flag("-Wl,--no-undefined") endif() #------------------------------------ @@ -332,6 +335,8 @@ if (CLR_CMAKE_HOST_UNIX) message("Detected NetBSD amd64") elseif(CLR_CMAKE_HOST_SUNOS) message("Detected SunOS amd64") + elseif(CLR_CMAKE_HOST_HAIKU) + message("Detected Haiku x86_64") endif(CLR_CMAKE_HOST_OSX OR CLR_CMAKE_HOST_MACCATALYST) endif(CLR_CMAKE_HOST_UNIX) @@ -531,7 +536,7 @@ if (CLR_CMAKE_HOST_UNIX) # We mark the function which needs exporting with DLLEXPORT add_compile_options(-fvisibility=hidden) - + # Separate functions so linker can remove them. add_compile_options(-ffunction-sections) @@ -610,6 +615,8 @@ if(CLR_CMAKE_TARGET_UNIX) if(CLR_CMAKE_TARGET_OS_ILLUMOS) add_compile_definitions($<$>>:TARGET_ILLUMOS>) endif() + elseif(CLR_CMAKE_TARGET_HAIKU) + add_compile_definitions($<$>>:TARGET_HAIKU>) endif() elseif(CLR_CMAKE_TARGET_WASI) add_compile_definitions($<$>>:TARGET_WASI>) diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index 2c477097dd0c0..2d6e1425ac3fe 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -214,6 +214,12 @@ if(CLR_CMAKE_HOST_OS STREQUAL sunos) endif(SUNOS_KERNEL_KIND STREQUAL illumos OR CMAKE_CROSSCOMPILING) endif(CLR_CMAKE_HOST_OS STREQUAL sunos) +if(CLR_CMAKE_HOST_OS STREQUAL haiku) + set(CLR_CMAKE_HOST_UNIX 1) + set(CLR_CMAKE_HOST_UNIX_AMD64 1) + set(CLR_CMAKE_HOST_HAIKU 1) +endif(CLR_CMAKE_HOST_OS STREQUAL haiku) + if(CLR_CMAKE_HOST_OS STREQUAL windows) set(CLR_CMAKE_HOST_WIN32 1) endif(CLR_CMAKE_HOST_OS STREQUAL windows) @@ -423,6 +429,11 @@ if(CLR_CMAKE_TARGET_OS STREQUAL sunos) set(CLR_CMAKE_TARGET_SUNOS 1) endif(CLR_CMAKE_TARGET_OS STREQUAL sunos) +if(CLR_CMAKE_TARGET_OS STREQUAL haiku) + set(CLR_CMAKE_TARGET_UNIX 1) + set(CLR_CMAKE_TARGET_HAIKU 1) +endif(CLR_CMAKE_TARGET_OS STREQUAL haiku) + if(CLR_CMAKE_TARGET_OS STREQUAL emscripten) set(CLR_CMAKE_TARGET_UNIX 1) set(CLR_CMAKE_TARGET_LINUX 1) diff --git a/eng/native/init-distro-rid.sh b/eng/native/init-distro-rid.sh index 70503dd3e0bc0..db0e2a5ff57d2 100644 --- a/eng/native/init-distro-rid.sh +++ b/eng/native/init-distro-rid.sh @@ -66,6 +66,9 @@ getNonPortableDistroRid() __uname_version=$(uname -v) __solaris_major_version=$(echo "${__uname_version%.*}") nonPortableRid=solaris."$__solaris_major_version"-"$targetArch" + elif [ "$targetOs" = "haiku" ]; then + __uname_release=$(uname -r) + nonPortableRid=haiku.r"$__uname_release"-"$targetArch" fi echo "$(echo $nonPortableRid | tr '[:upper:]' '[:lower:]')" diff --git a/eng/native/init-os-and-arch.sh b/eng/native/init-os-and-arch.sh index 897e895e5a1a8..e693617a6c2b6 100644 --- a/eng/native/init-os-and-arch.sh +++ b/eng/native/init-os-and-arch.sh @@ -8,7 +8,7 @@ if command -v getprop && getprop ro.product.system.model 2>&1 | grep -qi android fi case "$OSName" in -freebsd|linux|netbsd|openbsd|sunos|android) +freebsd|linux|netbsd|openbsd|sunos|android|haiku) os="$OSName" ;; darwin) os=osx ;; diff --git a/eng/native/tryrun.cmake b/eng/native/tryrun.cmake index 1535b7a387f78..04c6d1a98fdae 100644 --- a/eng/native/tryrun.cmake +++ b/eng/native/tryrun.cmake @@ -27,12 +27,14 @@ elseif(EXISTS /System/Library/CoreServices) set(DARWIN 1) elseif(EXISTS ${CROSS_ROOTFS}/etc/tizen-release) set(TIZEN 1) +elseif(EXISTS ${CROSS_ROOTFS}/boot/system/develop/headers/config/HaikuConfig.h) + set(HAIKU 1) + set(CLR_CMAKE_TARGET_OS haiku) endif() if(DARWIN) if(TARGET_ARCH_NAME MATCHES "^(arm64|x64)$") set_cache_value(FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE 1) - set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 1) set_cache_value(HAS_POSIX_SEMAPHORES_EXITCODE 1) set_cache_value(HAVE_BROKEN_FIFO_KEVENT_EXITCODE 1) set_cache_value(HAVE_BROKEN_FIFO_SELECT_EXITCODE 1) @@ -54,8 +56,6 @@ if(DARWIN) set_cache_value(HAVE_LARGE_SNPRINTF_SUPPORT_EXITCODE 0) set_cache_value(HAVE_MMAP_DEV_ZERO_EXITCODE 1) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 1) - set_cache_value(HAVE_PROCFS_MAPS_EXITCODE 1) - set_cache_value(HAVE_PROCFS_STATUS_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) set_cache_value(HAVE_SCHED_GETCPU_EXITCODE 1) set_cache_value(HAVE_SCHED_GET_PRIORITY_EXITCODE 0) @@ -68,16 +68,14 @@ if(DARWIN) set_cache_value(PTHREAD_CREATE_MODIFIES_ERRNO_EXITCODE 1) set_cache_value(REALPATH_SUPPORTS_NONEXISTENT_FILES_EXITCODE 1) set_cache_value(SEM_INIT_MODIFIES_ERRNO_EXITCODE 1) - set_cache_value(SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE 1) set_cache_value(SSCANF_SUPPORT_ll_EXITCODE 0) set_cache_value(UNGETC_NOT_RETURN_EOF_EXITCODE 1) set_cache_value(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP_EXITCODE 1) else() message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only arm64 or x64 is supported for OSX cross build!") endif() -elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s390x|ppc64le|x86|x64)$" OR FREEBSD OR ILLUMOS OR TIZEN) +elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s390x|ppc64le|x86|x64)$" OR FREEBSD OR ILLUMOS OR TIZEN OR HAIKU) set_cache_value(FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE 1) - set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 0) set_cache_value(HAS_POSIX_SEMAPHORES_EXITCODE 0) set_cache_value(HAVE_CLOCK_MONOTONIC_COARSE_EXITCODE 0) set_cache_value(HAVE_CLOCK_MONOTONIC_EXITCODE 0) @@ -94,8 +92,6 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s39 set_cache_value(HAVE_LARGE_SNPRINTF_SUPPORT_EXITCODE 0) set_cache_value(HAVE_MMAP_DEV_ZERO_EXITCODE 0) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 1) - set_cache_value(HAVE_PROCFS_MAPS_EXITCODE 0) - set_cache_value(HAVE_PROCFS_STATUS_EXITCODE 0) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 0) set_cache_value(HAVE_SCHED_GETCPU_EXITCODE 0) set_cache_value(HAVE_SCHED_GET_PRIORITY_EXITCODE 0) @@ -112,12 +108,10 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s39 if(ALPINE_LINUX) set_cache_value(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP_EXITCODE 1) - set_cache_value(SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE 0) set_cache_value(SSCANF_SUPPORT_ll_EXITCODE 1) set_cache_value(UNGETC_NOT_RETURN_EOF_EXITCODE 1) else() set_cache_value(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP_EXITCODE 0) - set_cache_value(SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE 1) set_cache_value(SSCANF_SUPPORT_ll_EXITCODE 0) set_cache_value(UNGETC_NOT_RETURN_EOF_EXITCODE 0) endif() @@ -127,16 +121,12 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s39 set_cache_value(HAVE_CLOCK_MONOTONIC 1) set_cache_value(HAVE_CLOCK_REALTIME 1) set_cache_value(HAVE_BROKEN_FIFO_KEVENT_EXITCODE 1) - set_cache_value(HAVE_PROCFS_MAPS 0) set_cache_value(HAVE_PROCFS_STAT 0) - set_cache_value(HAVE_PROCFS_STATUS_EXITCODE 1) - set_cache_value(GETPWUID_R_SETS_ERRNO 0) set_cache_value(UNGETC_NOT_RETURN_EOF 0) set_cache_value(HAVE_COMPATIBLE_ILOGBNAN 1) set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) set_cache_value(HAVE_TERMIOS2_EXITCODE 1) elseif(ILLUMOS) - set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 1) set_cache_value(HAVE_COMPATIBLE_ACOS_EXITCODE 1) set_cache_value(HAVE_COMPATIBLE_ASIN_EXITCODE 1) set_cache_value(HAVE_COMPATIBLE_ATAN2_EXITCODE 1) @@ -146,7 +136,6 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s39 set_cache_value(HAVE_COMPATIBLE_LOG_EXITCODE 1) set_cache_value(HAVE_LARGE_SNPRINTF_SUPPORT_EXITCODE 1) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 0) - set_cache_value(SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE 1) set_cache_value(SSCANF_SUPPORT_ll_EXITCODE 1) set_cache_value(UNGETC_NOT_RETURN_EOF_EXITCODE 0) set_cache_value(COMPILER_SUPPORTS_W_CLASS_MEMACCESS 1) @@ -156,6 +145,11 @@ elseif(TARGET_ARCH_NAME MATCHES "^(armel|arm|armv6|arm64|loongarch64|riscv64|s39 set_cache_value(HAVE_TERMIOS2_EXITCODE 1) elseif (TIZEN) set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) + elseif(HAIKU) + set_cache_value(HAVE_CLOCK_MONOTONIC_COARSE_EXITCODE 1) + set_cache_value(HAVE_COMPATIBLE_EXP_EXITCODE 0) + set_cache_value(HAVE_COMPATIBLE_ILOGBNAN_EXITCODE 0) + set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) endif() else() message(FATAL_ERROR "Unsupported platform. OS: ${CMAKE_SYSTEM_NAME}, arch: ${TARGET_ARCH_NAME}") diff --git a/eng/native/tryrun_ios_tvos.cmake b/eng/native/tryrun_ios_tvos.cmake index 46769ca69daa3..378af96da1151 100644 --- a/eng/native/tryrun_ios_tvos.cmake +++ b/eng/native/tryrun_ios_tvos.cmake @@ -11,7 +11,6 @@ set_cache_value(HAVE_CLOCK_MONOTONIC_EXITCODE 0) # TODO: these are taken from macOS, check these whether they're correct for iOS # some of them are probably not used by what we use from NativeAOT so could be reduced set_cache_value(FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL_EXITCODE 1) -set_cache_value(GETPWUID_R_SETS_ERRNO_EXITCODE 1) set_cache_value(HAS_POSIX_SEMAPHORES_EXITCODE 1) set_cache_value(HAVE_BROKEN_FIFO_KEVENT_EXITCODE 1) set_cache_value(HAVE_BROKEN_FIFO_SELECT_EXITCODE 1) @@ -31,8 +30,6 @@ set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 1) set_cache_value(HAVE_LARGE_SNPRINTF_SUPPORT_EXITCODE 0) set_cache_value(HAVE_MMAP_DEV_ZERO_EXITCODE 1) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 1) -set_cache_value(HAVE_PROCFS_MAPS_EXITCODE 1) -set_cache_value(HAVE_PROCFS_STATUS_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) set_cache_value(HAVE_SCHED_GET_PRIORITY_EXITCODE 0) set_cache_value(HAVE_VALID_NEGATIVE_INF_POW_EXITCODE 0) @@ -44,7 +41,6 @@ set_cache_value(ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS_EXITCODE 1) set_cache_value(PTHREAD_CREATE_MODIFIES_ERRNO_EXITCODE 1) set_cache_value(REALPATH_SUPPORTS_NONEXISTENT_FILES_EXITCODE 1) set_cache_value(SEM_INIT_MODIFIES_ERRNO_EXITCODE 1) -set_cache_value(SSCANF_CANNOT_HANDLE_MISSING_EXPONENT_EXITCODE 1) set_cache_value(SSCANF_SUPPORT_ll_EXITCODE 0) set_cache_value(UNGETC_NOT_RETURN_EOF_EXITCODE 1) set_cache_value(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP_EXITCODE 1) diff --git a/eng/versioning.targets b/eng/versioning.targets index 47d8cb570dcdd..7746e94594a97 100644 --- a/eng/versioning.targets +++ b/eng/versioning.targets @@ -89,6 +89,7 @@ + #include -int main() -{ - int ret; - float f = 0; - char * strin = \"12.34e\"; - - ret = sscanf (strin, \"%e\", &f); - if (ret <= 0) - exit (0); - exit(1); -}" SSCANF_CANNOT_HANDLE_MISSING_EXPONENT) -check_cxx_source_runs(" -#include -#include - int main(void) { char buf[256] = { 0 }; snprintf(buf, 0x7fffffff, \"%#x\", 0x12345678); @@ -633,30 +618,6 @@ check_cxx_source_runs(" #include #include -int main(void) { - int fd; -#ifdef PATH_MAX - char path[PATH_MAX]; -#elif defined(MAXPATHLEN) - char path[MAXPATHLEN]; -#else - char path[1024]; -#endif - - sprintf(path, \"/proc/%u/maps\", getpid()); - fd = open(path, O_RDONLY); - if (fd == -1) { - exit(1); - } - exit(0); -}" HAVE_PROCFS_MAPS) -set(CMAKE_REQUIRED_LIBRARIES) -check_cxx_source_runs(" -#include -#include -#include -#include - int main(void) { int fd; #ifdef PATH_MAX @@ -675,29 +636,6 @@ int main(void) { exit(0); }" HAVE_PROCFS_STAT) set(CMAKE_REQUIRED_LIBRARIES) -check_cxx_source_runs(" -#include -#include -#include -#include - -int main(void) { - int fd; -#ifdef PATH_MAX - char path[PATH_MAX]; -#elif defined(MAXPATHLEN) - char path[MAXPATHLEN]; -#else - char path[1024]; -#endif - - sprintf(path, \"/proc/%u/status\", getpid()); - fd = open(path, O_RDONLY); - if (fd == -1) { - exit(1); - } - exit(0); -}" HAVE_PROCFS_STATUS) set(CMAKE_REQUIRED_LIBRARIES m) check_cxx_source_runs(" #include @@ -948,34 +886,6 @@ int main() { }" HAS_POSIX_SEMAPHORES) set(CMAKE_REQUIRED_LIBRARIES) check_cxx_source_runs(" -#include -#include -#include -#include -#include - -int main(void) -{ - struct passwd sPasswd; - struct passwd *pPasswd; - char buf[1]; - int bufLen = sizeof(buf)/sizeof(buf[0]); - int euid = geteuid(); - int ret = 0; - - errno = 0; // clear errno - ret = getpwuid_r(euid, &sPasswd, buf, bufLen, &pPasswd); - if (0 != ret) - { - if (ERANGE == errno) - { - return 0; - } - } - - return 1; // assume errno is NOT set for all other cases -}" GETPWUID_R_SETS_ERRNO) -check_cxx_source_runs(" #include #include From af1171b7c9ba985c93141b107f64e5dd31ab2d37 Mon Sep 17 00:00:00 2001 From: Qiao Pengcheng Date: Wed, 17 May 2023 23:11:06 +0800 Subject: [PATCH 06/12] [LoongArch64] amend the crossgen2/r2rdump for LA64. (#85038) --- .../ReadyToRun/ArgIterator.cs | 10 +- .../Target_LoongArch64/ImportThunk.cs | 2 +- .../ReadyToRun/TransitionBlock.cs | 16 +- .../Amd64/GcInfo.cs | 8 + .../Amd64/GcSlotTable.cs | 3 + .../Amd64/GcTransition.cs | 4 + .../DebugInfo.cs | 2 + .../GCInfoTypes.cs | 10 ++ .../LoongArch64/Registers.cs | 45 +++++ .../LoongArch64/UnwindInfo.cs | 163 ++++++++++++++++++ .../ReadyToRunMethod.cs | 10 +- .../ReadyToRunReader.cs | 1 + .../TransitionBlock.cs | 7 +- src/coreclr/tools/r2rdump/CoreDisTools.cs | 15 +- src/coreclr/tools/r2rdump/R2RDump.csproj | 2 +- 15 files changed, 270 insertions(+), 28 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/Registers.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/UnwindInfo.cs diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 70e258d8d297d..78dfc95c757be 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1732,16 +1732,8 @@ private void ForceSigWalk() int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters; Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0); pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize; + pLoc.m_cFloatReg = 1; - if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHomogeneousAggregate()) - { - int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize(); - pLoc.m_cFloatReg = GetArgSize() / haElementSize; - } - else - { - pLoc.m_cFloatReg = 1; - } return pLoc; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs index 5e0a8751d472b..8d11f284f39bc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/Target_LoongArch64/ImportThunk.cs @@ -28,7 +28,7 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter ins if (!relocsOnly) { - // movz T0=R12, #index + // ori T0=R12, R0, #index int index = _containingImportSection.IndexFromBeginningOfArray; instructionEncoder.EmitMOV(Register.R12, checked((ushort)index)); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 2228cb248e760..09372f143813c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -651,8 +651,8 @@ private class LoongArch64TransitionBlock : TransitionBlock public override int NumCalleeSavedRegisters => 12; // Callee-saves, argument registers public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; - public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; - public override int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters; + public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters; + public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot; // F0..F7 public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double); @@ -671,19 +671,11 @@ public override bool IsArgPassedByRef(TypeHandle th) } else { - int numIntroducedFields = 0; - foreach (FieldDesc field in th.GetRuntimeTypeHandle().GetFields()) - { - if (!field.IsStatic) - { - numIntroducedFields++; - } - } - return ((numIntroducedFields == 0) || (numIntroducedFields > 2)); + return false; } } - public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters; + public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0); public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcInfo.cs index 52da7f3e91339..cd578333425b7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcInfo.cs @@ -259,6 +259,14 @@ public override string ToString() sb.AppendLine($" Has Tailcalls: {_wantsReportOnlyLeaf}"); } + else if (_machine == Machine.LoongArch64) + { + if (StackBaseRegister != 0xffffffff) + { + sb.AppendLine($" StackBaseRegister: {(LoongArch64.Registers)StackBaseRegister}"); + } + sb.AppendLine($" Has Tailcalls: {_wantsReportOnlyLeaf}"); + } sb.AppendLine($" Size of parameter area: 0x{SizeOfStackOutgoingAndScratchArea:X}"); if (SizeOfEditAndContinuePreservedArea != 0xffffffff) diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs index 153b19d377258..8355a1a0976dc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs @@ -68,6 +68,9 @@ private static string GetRegisterName(int registerNumber, Machine machine) case Machine.Arm64: return ((Arm64.Registers)registerNumber).ToString(); + case Machine.LoongArch64: + return ((LoongArch64.Registers)registerNumber).ToString(); + default: throw new NotImplementedException(machine.ToString()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs index 3c2bb7c8df9d8..ecea8cc1dfa6f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs @@ -65,6 +65,10 @@ public string GetSlotState(GcSlotTable slotTable, Machine machine) regType = typeof(Amd64.Registers); break; + case Machine.LoongArch64: + regType = typeof(LoongArch64.Registers); + break; + default: throw new NotImplementedException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs index c619198d31fb6..03110197b5877 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs @@ -72,6 +72,8 @@ public static string GetPlatformSpecificRegister(Machine machine, int regnum) return ((Arm.Registers)regnum).ToString(); case Machine.Arm64: return ((Arm64.Registers)regnum).ToString(); + case Machine.LoongArch64: + return ((LoongArch64.Registers)regnum).ToString(); default: throw new NotImplementedException($"No implementation for machine type {machine}."); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs index 38e85a7896909..9356bce3ccccd 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs @@ -149,6 +149,11 @@ internal GcInfoTypes(Machine machine) NUM_UNTRACKED_SLOTS_ENCBASE = 5; REGISTER_DELTA_ENCBASE = 3; break; + case Machine.LoongArch64: + SIZE_OF_RETURN_KIND_FAT = 4; + STACK_BASE_REGISTER_ENCBASE = 2; + NUM_REGISTERS_ENCBASE = 3; + break; } } @@ -159,6 +164,7 @@ internal int DenormalizeCodeLength(int x) case Machine.ArmThumb2: return (x << 1); case Machine.Arm64: + case Machine.LoongArch64: return (x << 2); } return x; @@ -173,6 +179,7 @@ internal int DenormalizeStackSlot(int x) case Machine.ArmThumb2: return (x << 2); case Machine.Arm64: + case Machine.LoongArch64: return (x << 3); } return x; @@ -188,6 +195,8 @@ internal uint DenormalizeStackBaseRegister(uint x) return ((x ^ 7) + 4); case Machine.Arm64: return (x ^ 29); + case Machine.LoongArch64: + return ((x ^ 22) & 0x3); } return x; } @@ -201,6 +210,7 @@ internal uint DenormalizeSizeOfStackArea(uint x) case Machine.ArmThumb2: return (x << 2); case Machine.Arm64: + case Machine.LoongArch64: return (x << 3); } return x; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/Registers.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/Registers.cs new file mode 100644 index 0000000000000..23b88ed478855 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/Registers.cs @@ -0,0 +1,45 @@ +// 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.Collections.Generic; +using System.Text; + +namespace ILCompiler.Reflection.ReadyToRun.LoongArch64 +{ + public enum Registers + { + R0, + Ra, + Tp, + Sp, + A0, + A1, + A2, + A3, + A4, + A5, + A6, + A7, + T0, + T1, + T2, + T3, + T4, + T5, + T6, + T7, + T8, + X0, + Fp, + S0, + S1, + S2, + S3, + S4, + S5, + S6, + S7, + S8 + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/UnwindInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/UnwindInfo.cs new file mode 100644 index 0000000000000..98fe6cbfdde3e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/LoongArch64/UnwindInfo.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; + +namespace ILCompiler.Reflection.ReadyToRun.LoongArch64 +{ + public class Epilog + { + public int Index { get; set; } + + public uint EpilogStartOffset { get; set; } + public uint Res { get; set; } + public uint Condition { get; set; } + public uint EpilogStartIndex { get; set; } + public uint EpilogStartOffsetFromMainFunctionBegin { get; set; } + + public Epilog() { } + + public Epilog(int index, int dw, uint startOffset) + { + Index = index; + + EpilogStartOffset = UnwindInfo.ExtractBits(dw, 0, 18); + Res = UnwindInfo.ExtractBits(dw, 18, 4); + Condition = UnwindInfo.ExtractBits(dw, 20, 4); + EpilogStartIndex = UnwindInfo.ExtractBits(dw, 22, 10); + + // Note that epilogStartOffset for a funclet is the offset from the beginning + // of the current funclet, not the offset from the beginning of the main function. + // To help find it when looking through JitDump output, also show the offset from + // the beginning of the main function. + EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 4 + startOffset; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine($" Epilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 4:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}"); + sb.AppendLine($" Condition: {Condition} (0x{Condition:X})" + ((Condition == 0xE) ? " (always)" : "")); + sb.Append($" Epilog Start Index: {EpilogStartIndex} (0x{EpilogStartIndex:X})"); + return sb.ToString(); + } + } + + public class UnwindCode + { + public int Index { get; set; } + + public UnwindCode() { } + + public UnwindCode(int index) + { + Index = index; + + } + } + + /// + /// based on src/jit/unwindloongarch64.cpp DumpUnwindInfo + /// + public class UnwindInfo : BaseUnwindInfo + { + public uint CodeWords { get; set; } + public uint EpilogCount { get; set; } + public uint EBit { get; set; } + public uint XBit { get; set; } + public uint Vers { get; set; } + public uint FunctionLength { get; set; } + + public uint ExtendedCodeWords { get; set; } + public uint ExtendedEpilogCount { get; set; } + + public Epilog[] Epilogs { get; set; } + + public UnwindInfo() { } + + public UnwindInfo(byte[] image, int offset) + { + uint startOffset = (uint)offset; + + int dw = NativeReader.ReadInt32(image, ref offset); + CodeWords = ExtractBits(dw, 27, 5); + EpilogCount = ExtractBits(dw, 22, 5); + EBit = ExtractBits(dw, 21, 1); + XBit = ExtractBits(dw, 20, 1); + Vers = ExtractBits(dw, 18, 2); + FunctionLength = ExtractBits(dw, 0, 18) * 4; + + if (CodeWords == 0 && EpilogCount == 0) + { + // We have an extension word specifying a larger number of Code Words or Epilog Counts + // than can be specified in the header word. + dw = NativeReader.ReadInt32(image, ref offset); + ExtendedCodeWords = ExtractBits(dw, 16, 8); + ExtendedEpilogCount = ExtractBits(dw, 0, 16); + } + + bool[] epilogStartAt = new bool[1024]; // One byte per possible epilog start index; initialized to false + + if (EBit == 0) + { + Epilogs = new Epilog[EpilogCount]; + if (EpilogCount != 0) + { + for (int scope = 0; scope < EpilogCount; scope++) + { + dw = NativeReader.ReadInt32(image, ref offset); + Epilogs[scope] = new Epilog(scope, dw, startOffset); + epilogStartAt[Epilogs[scope].EpilogStartIndex] = true; // an epilog starts at this offset in the unwind codes + } + } + } + else + { + Epilogs = new Epilog[0]; + epilogStartAt[EpilogCount] = true; // the one and only epilog starts its unwind codes at this offset + } + + + + Size = offset - (int)startOffset + (int)CodeWords * 4; + int alignmentPad = ((Size + sizeof(int) - 1) & ~(sizeof(int) - 1)) - Size; + Size += (alignmentPad + sizeof(uint)); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine($" CodeWords: {CodeWords}"); + sb.AppendLine($" EpilogCount: {EpilogCount}"); + sb.AppendLine($" EBit: {EBit}"); + sb.AppendLine($" XBit: {XBit}"); + sb.AppendLine($" Vers: {Vers}"); + sb.AppendLine($" FunctionLength: {FunctionLength}"); + if (CodeWords == 0 && EpilogCount == 0) + { + sb.AppendLine(" ---- Extension word ----"); + sb.AppendLine($" Extended Code Words: {CodeWords}"); + sb.AppendLine($" Extended Epilog Count: {EpilogCount}"); + } + if (EpilogCount == 0) + { + sb.AppendLine(" No epilogs"); + } + else + { + for (int i = 0; i < Epilogs.Length; i++) + { + sb.AppendLine(" -------------------------"); + sb.AppendLine(Epilogs[i].ToString()); + sb.AppendLine(" -------------------------"); + } + } + return sb.ToString(); + } + + internal static uint ExtractBits(int dw, int start, int length) + { + return (uint)((dw >> start) & ((1 << length) - 1)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs index ca9bc4996880a..dee60ea5daf3f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs @@ -225,6 +225,10 @@ private int GetSize() { return (int)arm64Info.FunctionLength; } + else if (UnwindInfo is LoongArch64.UnwindInfo loongarch64Info) + { + return (int)loongarch64Info.FunctionLength; + } else if (Method.GcInfo != null) { return Method.GcInfo.CodeLength; @@ -488,7 +492,7 @@ private void EnsureInitialized() } else { - // Arm and Arm64 use the same GcInfo format as Amd64 + // Arm, Arm64 and LoongArch64 use the same GcInfo format as Amd64 _gcInfo = new Amd64.GcInfo(_readyToRunReader.Image, gcInfoOffset, _readyToRunReader.Machine, _readyToRunReader.ReadyToRunHeader.MajorVersion); } } @@ -604,6 +608,10 @@ private void ParseRuntimeFunctions(bool partial) { unwindInfo = new Arm64.UnwindInfo(_readyToRunReader.Image, unwindOffset); } + else if (_readyToRunReader.Machine == Machine.LoongArch64) + { + unwindInfo = new LoongArch64.UnwindInfo(_readyToRunReader.Image, unwindOffset); + } if (i == 0 && unwindInfo != null) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 6f256e98c62f5..c46bcc6707d5d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1415,6 +1415,7 @@ private void EnsureImportSections() case Machine.Amd64: case Machine.Arm64: + case Machine.LoongArch64: entrySize = 8; break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs index cd66d5a660b7c..dcd1f531443bb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs @@ -31,6 +31,9 @@ public static TransitionBlock FromReader(ReadyToRunReader reader) case Machine.Arm64: return Arm64TransitionBlock.Instance; + case Machine.LoongArch64: + return LoongArch64TransitionBlock.Instance; + default: throw new NotImplementedException(); } @@ -163,8 +166,8 @@ private sealed class LoongArch64TransitionBlock : TransitionBlock public override int NumCalleeSavedRegisters => 12; // Callee-saves, padding, argument registers public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; - public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; - public override int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters; + public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters; + public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot; } } } diff --git a/src/coreclr/tools/r2rdump/CoreDisTools.cs b/src/coreclr/tools/r2rdump/CoreDisTools.cs index 739a564814309..eda94336b6722 100644 --- a/src/coreclr/tools/r2rdump/CoreDisTools.cs +++ b/src/coreclr/tools/r2rdump/CoreDisTools.cs @@ -21,7 +21,8 @@ public enum TargetArch Target_X86, Target_X64, Target_Thumb, - Target_Arm64 + Target_Arm64, + Target_LoongArch64 }; [DllImport(_dll, CallingConvention = CallingConvention.Cdecl)] @@ -73,6 +74,9 @@ public static IntPtr GetDisasm(Machine machine) case Machine.ArmThumb2: target = TargetArch.Target_Thumb; break; + case Machine.LoongArch64: + target = TargetArch.Target_LoongArch64; + break; default: Program.WriteWarning($"{machine} not supported on CoreDisTools"); return IntPtr.Zero; @@ -184,6 +188,9 @@ private void SetIndentations() // Instructions are dumped as 4-byte hexadecimal integers Machine.Arm64 => 4 * 2 + 1, + // Instructions are dumped as 4-byte hexadecimal integers + Machine.LoongArch64 => 4 * 2 + 1, + _ => throw new NotImplementedException() }; @@ -253,7 +260,7 @@ public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, o } else { - if (_reader.Machine == Machine.Arm64) + if ((_reader.Machine == Machine.Arm64) || (_reader.Machine == Machine.LoongArch64)) { // Replace " hh hh hh hh " byte dump with " hhhhhhhh ". // CoreDisTools should be fixed to dump bytes this way for ARM64. @@ -334,6 +341,10 @@ public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, o ProbeArm64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine); break; + case Machine.LoongArch64: + //TODO-LoongArch64: maybe should add ProbeLoongArch64Quirks. At least it's unused now. + break; + case Machine.ArmThumb2: break; diff --git a/src/coreclr/tools/r2rdump/R2RDump.csproj b/src/coreclr/tools/r2rdump/R2RDump.csproj index 999cb7c4714d6..f0a60be4274e4 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.csproj +++ b/src/coreclr/tools/r2rdump/R2RDump.csproj @@ -4,7 +4,7 @@ 1.0.0.0 true Exe - x64;x86;arm64;arm + x64;x86;arm64;arm;loongarch64 Open true $(NetCoreAppToolCurrent) From 1a598b70022ec95d230acf21304fa2a58343215d Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 17 May 2023 09:20:00 -0700 Subject: [PATCH 07/12] Fix DAC syncblk API so the SOS tests stop failing intermittently (#86339) Fix DAC syncblk API Only fill in the syncblk data if there are blocks Add SBNumber index validation to API --- src/coreclr/debug/daccess/request.cpp | 59 +++++++++++++++------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 29a0266f47a82..868593fae4651 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -3667,46 +3667,51 @@ ClrDataAccess::GetSyncBlockData(unsigned int SBNumber, struct DacpSyncBlockData ZeroMemory(pSyncBlockData,sizeof(DacpSyncBlockData)); pSyncBlockData->SyncBlockCount = (SyncBlockCache::s_pSyncBlockCache->m_FreeSyncTableIndex) - 1; - PTR_SyncTableEntry ste = PTR_SyncTableEntry(dac_cast(g_pSyncTable)+(sizeof(SyncTableEntry) * SBNumber)); - pSyncBlockData->bFree = ((dac_cast(ste->m_Object.Load())) & 1); + pSyncBlockData->bFree = TRUE; - if (pSyncBlockData->bFree == FALSE) + if (pSyncBlockData->SyncBlockCount > 0 && SBNumber <= pSyncBlockData->SyncBlockCount) { - pSyncBlockData->Object = (CLRDATA_ADDRESS)dac_cast(ste->m_Object.Load()); + PTR_SyncTableEntry ste = PTR_SyncTableEntry(dac_cast(g_pSyncTable)+(sizeof(SyncTableEntry) * SBNumber)); + pSyncBlockData->bFree = ((dac_cast(ste->m_Object.Load())) & 1); - if (ste->m_SyncBlock != NULL) + if (pSyncBlockData->bFree == FALSE) { - SyncBlock *pBlock = PTR_SyncBlock(ste->m_SyncBlock); - pSyncBlockData->SyncBlockPointer = HOST_CDADDR(pBlock); -#ifdef FEATURE_COMINTEROP - if (pBlock->m_pInteropInfo) + pSyncBlockData->Object = (CLRDATA_ADDRESS)dac_cast(ste->m_Object.Load()); + + if (ste->m_SyncBlock != NULL) { - pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->DacGetRawRCW() != 0) ? SYNCBLOCKDATA_COMFLAGS_RCW : 0; - pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetCCW() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CCW : 0; + SyncBlock *pBlock = PTR_SyncBlock(ste->m_SyncBlock); + pSyncBlockData->SyncBlockPointer = HOST_CDADDR(pBlock); +#ifdef FEATURE_COMINTEROP + if (pBlock->m_pInteropInfo) + { + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->DacGetRawRCW() != 0) ? SYNCBLOCKDATA_COMFLAGS_RCW : 0; + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetCCW() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CCW : 0; #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetComClassFactory() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CF : 0; + pSyncBlockData->COMFlags |= (pBlock->m_pInteropInfo->GetComClassFactory() != NULL) ? SYNCBLOCKDATA_COMFLAGS_CF : 0; #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - } + } #endif // FEATURE_COMINTEROP - pSyncBlockData->MonitorHeld = pBlock->m_Monitor.GetMonitorHeldStateVolatile(); - pSyncBlockData->Recursion = pBlock->m_Monitor.GetRecursionLevel(); - pSyncBlockData->HoldingThread = HOST_CDADDR(pBlock->m_Monitor.GetHoldingThread()); - pSyncBlockData->appDomainPtr = PTR_HOST_TO_TADDR(AppDomain::GetCurrentDomain()); + pSyncBlockData->MonitorHeld = pBlock->m_Monitor.GetMonitorHeldStateVolatile(); + pSyncBlockData->Recursion = pBlock->m_Monitor.GetRecursionLevel(); + pSyncBlockData->HoldingThread = HOST_CDADDR(pBlock->m_Monitor.GetHoldingThread()); + pSyncBlockData->appDomainPtr = PTR_HOST_TO_TADDR(AppDomain::GetCurrentDomain()); - // TODO: Microsoft, implement the wait list - pSyncBlockData->AdditionalThreadCount = 0; + // TODO: Microsoft, implement the wait list + pSyncBlockData->AdditionalThreadCount = 0; - if (pBlock->m_Link.m_pNext != NULL) - { - PTR_SLink pLink = pBlock->m_Link.m_pNext; - do + if (pBlock->m_Link.m_pNext != NULL) { - pSyncBlockData->AdditionalThreadCount++; - pLink = pBlock->m_Link.m_pNext; + PTR_SLink pLink = pBlock->m_Link.m_pNext; + do + { + pSyncBlockData->AdditionalThreadCount++; + pLink = pBlock->m_Link.m_pNext; + } + while ((pLink != NULL) && + (pSyncBlockData->AdditionalThreadCount < 1000)); } - while ((pLink != NULL) && - (pSyncBlockData->AdditionalThreadCount < 1000)); } } } From bda1f6c691c6517259a75e969f408e242dbc8103 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 17 May 2023 17:23:45 +0100 Subject: [PATCH 08/12] Revert RequiredAttribute.DisallowAllDefaultValues (#86378) --- .../ref/System.ComponentModel.Annotations.cs | 1 - .../DataAnnotations/RequiredAttribute.cs | 58 ---------- .../DataAnnotations/RequiredAttributeTests.cs | 108 ++---------------- 3 files changed, 8 insertions(+), 159 deletions(-) diff --git a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs index a46192e9dd95b..542845070d0c3 100644 --- a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs +++ b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs @@ -281,7 +281,6 @@ public partial class RequiredAttribute : System.ComponentModel.DataAnnotations.V { public RequiredAttribute() { } public bool AllowEmptyStrings { get { throw null; } set { } } - public bool DisallowAllDefaultValues { get { throw null; } set { } } public override bool IsValid(object? value) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple=false)] diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs index 16e8f839171ff..b7f2ef057d1e3 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RequiredAttribute.cs @@ -1,10 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - namespace System.ComponentModel.DataAnnotations { /// @@ -31,11 +27,6 @@ public RequiredAttribute() /// public bool AllowEmptyStrings { get; set; } - /// - /// Gets or sets a flag indicating whether the attribute should also disallow non-null default values. - /// - public bool DisallowAllDefaultValues { get; set; } - /// /// Override of /// @@ -43,65 +34,16 @@ public RequiredAttribute() /// /// Returns if the is null or an empty string. /// If then is returned for empty strings. - /// If then is returned for values - /// that are equal to the of the declared type. /// public override bool IsValid(object? value) - => IsValidCore(value, validationContext: null); - - /// - protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) - { - return IsValidCore(value, validationContext) - ? ValidationResult.Success - : CreateFailedValidationResult(validationContext); - } - - private bool IsValidCore(object? value, ValidationContext? validationContext) { if (value is null) { return false; } - if (DisallowAllDefaultValues) - { - // To determine the default value of non-nullable types we need the declaring type of the value. - // This is the property type in a validation context falling back to the runtime type for root values. - Type declaringType = validationContext?.MemberType ?? value.GetType(); - if (GetDefaultValueForNonNullableValueType(declaringType) is object defaultValue) - { - return !defaultValue.Equals(value); - } - } - // only check string length if empty strings are not allowed return AllowEmptyStrings || value is not string stringValue || !string.IsNullOrWhiteSpace(stringValue); } - - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", - Justification = "GetUninitializedObject is only called struct types. You can always create an instance of a struct.")] - private object? GetDefaultValueForNonNullableValueType(Type type) - { - object? defaultValue = _defaultValueCache; - - if (defaultValue != null && defaultValue.GetType() == type) - { - Debug.Assert(type.IsValueType && Nullable.GetUnderlyingType(type) is null); - } - else if (type.IsValueType && Nullable.GetUnderlyingType(type) is null) - { - _defaultValueCache = defaultValue = RuntimeHelpers.GetUninitializedObject(type); - } - else - { - defaultValue = null; - } - - return defaultValue; - } - - private object? _defaultValueCache; } } diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RequiredAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RequiredAttributeTests.cs index 7abcdd0effa9c..0e482b2227e93 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RequiredAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RequiredAttributeTests.cs @@ -16,7 +16,7 @@ protected override IEnumerable ValidValues() yield return new TestCase(new RequiredAttribute() { AllowEmptyStrings = true }, " \t \r \n "); yield return new TestCase(new RequiredAttribute(), new object()); - // default value types with DisallowAllDefaultValues turned off + // default value types are always valid var requiredAttribute = new RequiredAttribute(); yield return new TestCase(requiredAttribute, false); yield return new TestCase(requiredAttribute, 0); @@ -25,8 +25,7 @@ protected override IEnumerable ValidValues() yield return new TestCase(requiredAttribute, default(DateTime)); yield return new TestCase(requiredAttribute, default(Guid)); - // non-default value types with DisallowAllDefaultValues turned on - requiredAttribute = new RequiredAttribute { DisallowAllDefaultValues = true }; + // non-default value types are always valid yield return new TestCase(requiredAttribute, true); yield return new TestCase(requiredAttribute, 1); yield return new TestCase(requiredAttribute, 0.1); @@ -34,16 +33,12 @@ protected override IEnumerable ValidValues() yield return new TestCase(requiredAttribute, DateTime.MaxValue); yield return new TestCase(requiredAttribute, Guid.Parse("c3436566-4083-4bbe-8b56-f9c278162c4b")); - // reference types with DisallowAllDefaultValues turned on - requiredAttribute = new RequiredAttribute { DisallowAllDefaultValues = true }; - yield return new TestCase(requiredAttribute, "SomeString"); - yield return new TestCase(requiredAttribute, new object()); - - // reference types with DisallowAllDefaultValues and AllowEmptyStrings turned on - requiredAttribute = new RequiredAttribute { DisallowAllDefaultValues = true, AllowEmptyStrings = true }; - yield return new TestCase(requiredAttribute, "SomeString"); - yield return new TestCase(requiredAttribute, string.Empty); - yield return new TestCase(requiredAttribute, new object()); + // Populated System.Nullable values are always valid + yield return new TestCase(new RequiredAttribute(), (bool?)false); + yield return new TestCase(new RequiredAttribute(), (int?)0); + yield return new TestCase(new RequiredAttribute(), (Guid?)Guid.Empty); + yield return new TestCase(new RequiredAttribute(), (DateTime?)default(DateTime)); + yield return new TestCase(new RequiredAttribute(), (TimeSpan?)default(TimeSpan)); } protected override IEnumerable InvalidValues() @@ -51,62 +46,6 @@ protected override IEnumerable InvalidValues() yield return new TestCase(new RequiredAttribute(), null); yield return new TestCase(new RequiredAttribute() { AllowEmptyStrings = false }, string.Empty); yield return new TestCase(new RequiredAttribute() { AllowEmptyStrings = false }, " \t \r \n "); - - // default values with DisallowAllDefaultValues turned on - var requiredAttribute = new RequiredAttribute { DisallowAllDefaultValues = true }; - yield return new TestCase(requiredAttribute, null); - yield return new TestCase(requiredAttribute, false); - yield return new TestCase(requiredAttribute, 0); - yield return new TestCase(requiredAttribute, 0d); - yield return new TestCase(requiredAttribute, default(TimeSpan)); - yield return new TestCase(requiredAttribute, default(DateTime)); - yield return new TestCase(requiredAttribute, default(Guid)); - yield return new TestCase(requiredAttribute, default(StructWithTrivialEquality)); - // Structs that are not default but *equal* default should also fail validation. - yield return new TestCase(requiredAttribute, new StructWithTrivialEquality { Value = 42 }); - - // default value properties with DisallowDefaultValues turned on - requiredAttribute = new RequiredAttribute { DisallowAllDefaultValues = true }; - yield return new TestCase(requiredAttribute, null, CreatePropertyContext()); - yield return new TestCase(requiredAttribute, null, CreatePropertyContext()); - yield return new TestCase(requiredAttribute, false, CreatePropertyContext()); - yield return new TestCase(requiredAttribute, 0, CreatePropertyContext()); - yield return new TestCase(requiredAttribute, 0d, CreatePropertyContext()); - yield return new TestCase(requiredAttribute, default(TimeSpan), CreatePropertyContext()); - yield return new TestCase(requiredAttribute, default(DateTime), CreatePropertyContext()); - yield return new TestCase(requiredAttribute, default(Guid), CreatePropertyContext()); - yield return new TestCase(requiredAttribute, default(ImmutableArray), CreatePropertyContext>()); - yield return new TestCase(requiredAttribute, default(StructWithTrivialEquality), CreatePropertyContext()); - // Structs that are not default but *equal* default should also fail validation. - yield return new TestCase(requiredAttribute, new StructWithTrivialEquality { Value = 42 }, CreatePropertyContext()); - } - - [Theory] - [MemberData(nameof(GetNonNullDefaultValues))] - public void DefaultValueTypes_OnPolymorphicProperties_SucceedValidation(object defaultValue) - { - var attribute = new RequiredAttribute { DisallowAllDefaultValues = true }; - Assert.False(attribute.IsValid(defaultValue)); // Fails validation when no contexts present - - // Polymorphic contexts should succeed validation - var polymorphicContext = CreatePropertyContext(); - attribute.Validate(defaultValue, polymorphicContext); - Assert.Equal(ValidationResult.Success, attribute.GetValidationResult(defaultValue, polymorphicContext)); - } - - public static IEnumerable GetNonNullDefaultValues() - { - // default value types on polymorphic properties with DisallowDefaultValues turned on - - yield return new object[] { false }; - yield return new object[] { 0 }; - yield return new object[] { 0d }; - yield return new object[] { default(TimeSpan) }; - yield return new object[] { default(DateTime) }; - yield return new object[] { default(Guid) }; - yield return new object[] { default(ImmutableArray) }; - yield return new object[] { default(StructWithTrivialEquality) }; - yield return new object[] { new StructWithTrivialEquality { Value = 42 } }; } [Fact] @@ -119,36 +58,5 @@ public void AllowEmptyStrings_GetSet_ReturnsExpectected() attribute.AllowEmptyStrings = false; Assert.False(attribute.AllowEmptyStrings); } - - [Fact] - public void DisallowAllowAllDefaultValues_GetSet_ReturnsExpectected() - { - var attribute = new RequiredAttribute(); - Assert.False(attribute.DisallowAllDefaultValues); - attribute.DisallowAllDefaultValues = true; - Assert.True(attribute.DisallowAllDefaultValues); - attribute.DisallowAllDefaultValues = false; - Assert.False(attribute.DisallowAllDefaultValues); - } - - private static ValidationContext CreatePropertyContext() - => new ValidationContext(new GenericPoco()) { MemberName = nameof(GenericPoco.Value) }; - - public class GenericPoco - { - public T Value { get; set; } - } - - /// - /// Defines a struct where all values are equal. - /// - public readonly struct StructWithTrivialEquality : IEquatable - { - public int Value { get; init; } - - public bool Equals(StructWithTrivialEquality _) => true; - public override bool Equals(object other) => other is StructWithTrivialEquality; - public override int GetHashCode() => 0; - } } } From 83f71b53d7f08700fd059191859f7931cf5712f4 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 17 May 2023 20:00:40 +0200 Subject: [PATCH 09/12] Improve constant folding for some frozen objects (#86318) --- src/coreclr/jit/valuenum.cpp | 42 ++++++++++--------- src/coreclr/jit/valuenum.h | 5 ++- .../src/System/Boolean.cs | 11 ++++- .../src/System/ThrowHelper.cs | 6 +++ 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 820057b9f0b1b..d3434639bfb6b 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -5796,6 +5796,11 @@ bool ValueNumStore::IsVNConstant(ValueNum vn) } } +bool ValueNumStore::IsVNConstantNonHandle(ValueNum vn) +{ + return IsVNConstant(vn) && !IsVNHandle(vn); +} + bool ValueNumStore::IsVNInt32Constant(ValueNum vn) { if (!IsVNConstant(vn)) @@ -10390,38 +10395,37 @@ static bool GetObjectHandleAndOffset(ValueNumStore* vnStore, ssize_t* byteOffset, CORINFO_OBJECT_HANDLE* pObj) { - if (!tree->gtVNPair.BothEqual()) { return false; } - ValueNum treeVN = tree->gtVNPair.GetLiberal(); - VNFuncApp funcApp; - if (vnStore->GetVNFunc(treeVN, &funcApp) && (funcApp.m_func == (VNFunc)GT_ADD)) + ValueNum treeVN = tree->gtVNPair.GetLiberal(); + VNFuncApp funcApp; + target_ssize_t offset = 0; + while (vnStore->GetVNFunc(treeVN, &funcApp) && (funcApp.m_func == (VNFunc)GT_ADD)) { - // [objHandle + offset] - if (vnStore->IsVNObjHandle(funcApp.m_args[0]) && vnStore->IsVNConstant(funcApp.m_args[1])) + if (vnStore->IsVNConstantNonHandle(funcApp.m_args[0]) && (vnStore->TypeOfVN(funcApp.m_args[0]) == TYP_I_IMPL)) { - *pObj = vnStore->ConstantObjHandle(funcApp.m_args[0]); - *byteOffset = vnStore->ConstantValue(funcApp.m_args[1]); - return true; + offset += vnStore->ConstantValue(funcApp.m_args[0]); + treeVN = funcApp.m_args[1]; } - - // [offset + objHandle] - // TODO: Introduce a general helper to accumulate offsets for - // shapes such as (((X + CNS1) + CNS2) + CNS3) etc. - if (vnStore->IsVNObjHandle(funcApp.m_args[1]) && vnStore->IsVNConstant(funcApp.m_args[0])) + else if (vnStore->IsVNConstantNonHandle(funcApp.m_args[1]) && + (vnStore->TypeOfVN(funcApp.m_args[1]) == TYP_I_IMPL)) { - *pObj = vnStore->ConstantObjHandle(funcApp.m_args[1]); - *byteOffset = vnStore->ConstantValue(funcApp.m_args[0]); - return true; + offset += vnStore->ConstantValue(funcApp.m_args[1]); + treeVN = funcApp.m_args[0]; + } + else + { + return false; } } - else if (vnStore->IsVNObjHandle(treeVN)) + + if (vnStore->IsVNObjHandle(treeVN)) { *pObj = vnStore->ConstantObjHandle(treeVN); - *byteOffset = 0; + *byteOffset = offset; return true; } return false; diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index d015500818225..5d598523c2e8a 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -858,9 +858,12 @@ class ValueNumStore // Returns BasicBlock::MAX_LOOP_NUM if the given value number's loop nest is unknown or ill-defined. BasicBlock::loopNumber LoopOfVN(ValueNum vn); - // Returns true iff the VN represents a (non-handle) constant. + // Returns true iff the VN represents a constant. bool IsVNConstant(ValueNum vn); + // Returns true iff the VN represents a (non-handle) constant. + bool IsVNConstantNonHandle(ValueNum vn); + // Returns true iff the VN represents an integer constant. bool IsVNInt32Constant(ValueNum vn); diff --git a/src/libraries/System.Private.CoreLib/src/System/Boolean.cs b/src/libraries/System.Private.CoreLib/src/System/Boolean.cs index 144157c259402..358d7c8db3acc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Boolean.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Boolean.cs @@ -236,8 +236,14 @@ public static bool Parse(string value) return Parse(value.AsSpan()); } - public static bool Parse(ReadOnlySpan value) => - TryParse(value, out bool result) ? result : throw new FormatException(SR.Format(SR.Format_BadBoolean, new string(value))); + public static bool Parse(ReadOnlySpan value) + { + if (!TryParse(value, out bool result)) + { + ThrowHelper.ThrowFormatException_BadBoolean(value); + } + return result; + } // Determines whether a String represents true or false. // @@ -267,6 +273,7 @@ public static bool TryParse(ReadOnlySpan value, out bool result) return TryParseUncommon(value, out result); + [MethodImpl(MethodImplOptions.NoInlining)] static bool TryParseUncommon(ReadOnlySpan value, out bool result) { // With "true" being 4 characters, even if we trim something from <= 4 chars, diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 95c49059600b6..3f4ae02f9befc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -539,6 +539,12 @@ internal static void ThrowFormatException_NeedSingleChar() throw new FormatException(SR.Format_NeedSingleChar); } + [DoesNotReturn] + internal static void ThrowFormatException_BadBoolean(ReadOnlySpan value) + { + throw new FormatException(SR.Format(SR.Format_BadBoolean, new string(value))); + } + [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { From 4de58bf05e71e02ed3d1c790657d78b10b5f2d8d Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 17 May 2023 11:28:21 -0700 Subject: [PATCH 10/12] [mono][jit] Implement JIT support for the arm64 Crc and Dp intrinsics sets. (#86106) Also implement hw capacity detection for apple+arm64 platforms. --- src/mono/mono/arch/arm64/arm64-codegen.h | 14 ++++ src/mono/mono/arch/arm64/codegen-test.c | 10 +++ src/mono/mono/mini/cpu-arm64.mdesc | 4 ++ src/mono/mono/mini/mini-arm64.c | 81 +++++++++++++++++++++++- src/mono/mono/mini/mini-ops.h | 2 + src/mono/mono/mini/mini.c | 8 ++- src/mono/mono/mini/simd-intrinsics.c | 24 +++++-- src/mono/mono/utils/mono-hwcap-arm64.c | 54 ++++++++++++++++ src/mono/mono/utils/mono-hwcap-vars.h | 7 ++ 9 files changed, 196 insertions(+), 8 deletions(-) diff --git a/src/mono/mono/arch/arm64/arm64-codegen.h b/src/mono/mono/arch/arm64/arm64-codegen.h index 0a69031867854..154fd10d85f63 100644 --- a/src/mono/mono/arch/arm64/arm64-codegen.h +++ b/src/mono/mono/arch/arm64/arm64-codegen.h @@ -950,6 +950,20 @@ arm_encode_arith_imm (int imm, guint32 *shift) #define arm_autibsp(p) arm_format_autib ((p), 0b0011, 0b111) +/* CRC32 */ + +#define arm_format_crc32(p, sf, C, sz, rm, rn, rd) arm_emit ((p), ((sf) << 31) | (0b11010110 << 21) | (rm) << 16 | (0b010 << 13) | ((C) << 12) | ((sz) << 10) | ((rn) << 5) | ((rd) << 0)) + +#define arm_crc32b(p, rd, rn, rm) arm_format_crc32 ((p), 0, 0, 0b00, (rm), (rn), (rd)) +#define arm_crc32h(p, rd, rn, rm) arm_format_crc32 ((p), 0, 0, 0b01, (rm), (rn), (rd)) +#define arm_crc32w(p, rd, rn, rm) arm_format_crc32 ((p), 0, 0, 0b10, (rm), (rn), (rd)) +#define arm_crc32x(p, rd, rn, rm) arm_format_crc32 ((p), 1, 0, 0b11, (rm), (rn), (rd)) + +#define arm_crc32cb(p, rd, rn, rm) arm_format_crc32 ((p), 0, 1, 0b00, (rm), (rn), (rd)) +#define arm_crc32ch(p, rd, rn, rm) arm_format_crc32 ((p), 0, 1, 0b01, (rm), (rn), (rd)) +#define arm_crc32cw(p, rd, rn, rm) arm_format_crc32 ((p), 0, 1, 0b10, (rm), (rn), (rd)) +#define arm_crc32cx(p, rd, rn, rm) arm_format_crc32 ((p), 1, 1, 0b11, (rm), (rn), (rd)) + /* C4.1.69 NEON vector ISA */ // Opcode naming convention is arm_neon__[_] diff --git a/src/mono/mono/arch/arm64/codegen-test.c b/src/mono/mono/arch/arm64/codegen-test.c index b50a6432cf252..7644708aeb2b8 100644 --- a/src/mono/mono/arch/arm64/codegen-test.c +++ b/src/mono/mono/arch/arm64/codegen-test.c @@ -482,6 +482,16 @@ main (int argc, char *argv []) arm_neon_addp (code, VREG_FULL, TYPE_I8, ARMREG_R0, ARMREG_R1, ARMREG_R2); arm_neon_faddp (code, VREG_FULL, TYPE_F32, ARMREG_R0, ARMREG_R1, ARMREG_R2); + // crc32 + arm_crc32b (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32h (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32w (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32x (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32cb (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32ch (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32cw (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + arm_crc32cx (code, ARMREG_R1, ARMREG_R2, ARMREG_R3); + for (i = 0; i < code - buf; ++i) printf (".byte %d\n", buf [i]); printf ("\n"); diff --git a/src/mono/mono/mini/cpu-arm64.mdesc b/src/mono/mono/mini/cpu-arm64.mdesc index 5d582da5a1bc8..57e31d0561eff 100644 --- a/src/mono/mono/mini/cpu-arm64.mdesc +++ b/src/mono/mono/mini/cpu-arm64.mdesc @@ -473,6 +473,9 @@ lscnt32: dest:i src1:i len:4 lscnt64: dest:i src1:i len:4 xop_i8_i8: dest:i src1:i len:4 xop_i4_i4: dest:i src1:i len:4 +xop_i4_i4_i4: dest:i src1:i src2:i len:4 +xop_i4_i4_i8: dest:i src1:i src2:i len:4 +xop_ovr_x_x_x_x: dest:x src1:x src2:x src3:x len:4 clob:1 arm64_smulh: dest:i src1:i src2:i len:4 arm64_umulh: dest:i src1:i src2:i len:4 arm64_hint: len:4 @@ -554,6 +557,7 @@ arm64_ushl: dest:x src1:x src2:x len:4 arm64_ext_imm: dest:x src1:x src2:x len:4 xinsert_i8: dest:x src1:x src2:i src3:i len:20 xinsert_r8: dest:x src1:x src2:f src3:i len:20 +arm64_broadcast_elem: dest:x src1:x len:16 generic_class_init: src1:a len:44 clob:c gc_safe_point: src1:i len:12 clob:c diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index f163a76f50508..c31af98b63b0d 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include "llvm-intrinsics-types.h" @@ -3835,6 +3836,28 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) } break; } + case OP_XOP_OVR_X_X_X_X: { + IntrinsicId iid = (IntrinsicId) ins->inst_c0; + g_assert (dreg == sreg1); + g_assert (mono_class_value_size (ins->klass, NULL) == 16); + switch (iid) { + case INTRINS_AARCH64_ADV_SIMD_SDOT: + arm_neon_sdot_4s (code, dreg, sreg2, sreg3); + break; + case INTRINS_AARCH64_ADV_SIMD_UDOT: + arm_neon_udot_4s (code, dreg, sreg2, sreg3); + break; + default: + g_assert_not_reached (); + break; + } + break; + } + case OP_ARM64_BROADCAST_ELEM: + arm_neon_smov (code, TYPE_I32, ARMREG_IP0, sreg1, ins->inst_c0); + arm_neon_dup_g_4s (code, dreg, ARMREG_IP0); + break; + case OP_XZERO: arm_neon_eor_16b (code, dreg, dreg, dreg); break; @@ -5383,7 +5406,46 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) g_assert (ins->inst_c0 == INTRINS_BITREVERSE_I32); arm_rbitw (code, dreg, sreg1); break; - + case OP_XOP_I4_I4_I4: { + switch (ins->inst_c0) { + case INTRINS_AARCH64_CRC32B: + arm_crc32b (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32H: + arm_crc32h (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32W: + arm_crc32w (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32CB: + arm_crc32cb (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32CH: + arm_crc32ch (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32CW: + arm_crc32cw (code, dreg, sreg1, sreg2); + break; + default: + g_assert_not_reached (); + break; + } + break; + } + case OP_XOP_I4_I4_I8: { + switch (ins->inst_c0) { + case INTRINS_AARCH64_CRC32X: + arm_crc32x (code, dreg, sreg1, sreg2); + break; + case INTRINS_AARCH64_CRC32CX: + arm_crc32cx (code, dreg, sreg1, sreg2); + break; + default: + g_assert_not_reached (); + break; + } + break; + } case OP_ARM64_HINT: g_assert (ins->inst_c0 <= ARMHINT_SEVL); arm_hint (code, ins->inst_c0); @@ -6382,3 +6444,20 @@ mono_arm_emit_brx (guint8 *code, int reg) { return emit_brx (code, reg); } + +MonoCPUFeatures +mono_arch_get_cpu_features (void) +{ + guint64 features = MONO_CPU_INITED; + + if (mono_hwcap_arm64_has_crc32) + features |= MONO_CPU_ARM64_CRC; + if (mono_hwcap_arm64_has_dot) + features |= MONO_CPU_ARM64_DP; + if (mono_hwcap_arm64_has_rdm) + features |= MONO_CPU_ARM64_RDM; + if (mono_hwcap_arm64_has_sha1 && mono_hwcap_arm64_has_sha256 && mono_hwcap_arm64_has_aes) + features |= MONO_CPU_ARM64_CRYPTO; + + return features; +} diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index df1731f0e6801..120b884495bdc 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1722,6 +1722,8 @@ MINI_OP(OP_ARM64_SQXTUN2, "arm64_sqxtun2", XREG, XREG, XREG) MINI_OP(OP_ARM64_SELECT_SCALAR, "arm64_select_scalar", XREG, XREG, IREG) MINI_OP(OP_ARM64_SELECT_QUAD, "arm64_select_quad", XREG, XREG, IREG) +/* Take a word elem of sreg1 identified by inst_c0 and broadcast it to all elements of dreg */ +MINI_OP(OP_ARM64_BROADCAST_ELEM, "arm64_broadcast_elem", XREG, XREG, NONE) MINI_OP(OP_ARM64_FCVTN, "arm64_fcvtn", XREG, XREG, NONE) MINI_OP(OP_ARM64_FCVTN2, "arm64_fcvtn2", XREG, XREG, XREG) diff --git a/src/mono/mono/mini/mini.c b/src/mono/mono/mini/mini.c index 89f6ebdb46701..95d121f522ebe 100644 --- a/src/mono/mono/mini/mini.c +++ b/src/mono/mono/mini/mini.c @@ -4455,9 +4455,11 @@ mini_get_cpu_features (MonoCompile* cfg) #if !defined(MONO_CROSS_COMPILE) if (!cfg->compile_aot || cfg->use_current_cpu) { // detect current CPU features if we are in JIT mode or AOT with use_current_cpu flag. -#if defined(ENABLE_LLVM) - features = mono_llvm_get_cpu_features (); // llvm has a nice built-in API to detect features -#elif defined(TARGET_AMD64) || defined(TARGET_X86) +#if defined(ENABLE_LLVM) && !(defined(TARGET_ARM64) && defined(TARGET_OSX)) + // llvm has a nice built-in API to detect features + // it is not implemented on some platforms like apple arm64 + features = mono_llvm_get_cpu_features (); +#elif defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM64) features = mono_arch_get_cpu_features (); #endif } diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index 9de5495e07707..ac933551cb120 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -3589,8 +3589,8 @@ static const IntrinGroup supported_arm_intrinsics [] = { { "AdvSimd", MONO_CPU_ARM64_NEON, advsimd_methods, sizeof (advsimd_methods) }, { "Aes", MONO_CPU_ARM64_CRYPTO, crypto_aes_methods, sizeof (crypto_aes_methods) }, { "ArmBase", MONO_CPU_ARM64_BASE, armbase_methods, sizeof (armbase_methods), TRUE }, - { "Crc32", MONO_CPU_ARM64_CRC, crc32_methods, sizeof (crc32_methods) }, - { "Dp", MONO_CPU_ARM64_DP, dp_methods, sizeof (dp_methods) }, + { "Crc32", MONO_CPU_ARM64_CRC, crc32_methods, sizeof (crc32_methods), TRUE }, + { "Dp", MONO_CPU_ARM64_DP, dp_methods, sizeof (dp_methods), TRUE }, { "Rdm", MONO_CPU_ARM64_RDM, rdm_methods, sizeof (rdm_methods) }, { "Sha1", MONO_CPU_ARM64_CRYPTO, sha1_methods, sizeof (sha1_methods) }, { "Sha256", MONO_CPU_ARM64_CRYPTO, sha256_methods, sizeof (sha256_methods) }, @@ -3976,8 +3976,24 @@ emit_arm64_intrinsics ( MonoClass *quad_klass = mono_class_from_mono_type_internal (fsig->params [2]); gboolean is_unsigned = type_is_unsigned (fsig->ret); int iid = is_unsigned ? INTRINS_AARCH64_ADV_SIMD_UDOT : INTRINS_AARCH64_ADV_SIMD_SDOT; - MonoInst *quad = emit_simd_ins (cfg, arg_klass, OP_ARM64_SELECT_QUAD, args [2]->dreg, args [3]->dreg); - quad->data.op [1].klass = quad_klass; + + MonoInst *quad; + if (!COMPILE_LLVM (cfg)) { + if (mono_class_value_size (arg_klass, NULL) != 16 || mono_class_value_size (quad_klass, NULL) != 16) + return NULL; + // FIXME: The c# api has ConstantExpected(Max = (byte)(15)), but the hw only supports + // selecting one of the 4 32 bit words + if (args [3]->opcode != OP_ICONST || args [3]->inst_c0 < 0 || args [3]->inst_c0 > 3) { + // FIXME: Throw the right exception ? + mono_emit_jit_icall (cfg, mono_throw_platform_not_supported, NULL); + return NULL; + } + quad = emit_simd_ins (cfg, klass, OP_ARM64_BROADCAST_ELEM, args [2]->dreg, -1); + quad->inst_c0 = args [3]->inst_c0; + } else { + quad = emit_simd_ins (cfg, arg_klass, OP_ARM64_SELECT_QUAD, args [2]->dreg, args [3]->dreg); + quad->data.op [1].klass = quad_klass; + } MonoInst *ret = emit_simd_ins (cfg, ret_klass, OP_XOP_OVR_X_X_X_X, args [0]->dreg, args [1]->dreg); ret->sreg3 = quad->dreg; ret->inst_c0 = iid; diff --git a/src/mono/mono/utils/mono-hwcap-arm64.c b/src/mono/mono/utils/mono-hwcap-arm64.c index f5cddcf25dc11..f701d26c7511b 100644 --- a/src/mono/mono/utils/mono-hwcap-arm64.c +++ b/src/mono/mono/utils/mono-hwcap-arm64.c @@ -6,9 +6,63 @@ * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ +#ifdef __APPLE__ +#include +#include +#endif + #include "mono/utils/mono-hwcap.h" void mono_hwcap_arch_init (void) { +#ifdef __APPLE__ + const char *prop; + guint val [16]; + size_t val_len; + int res; + + val_len = sizeof (val); + prop = "hw.optional.armv8_crc32"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_crc32 = *(int*)val; + + val_len = sizeof (val); + prop = "hw.optional.arm.FEAT_RDM"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_rdm = *(int*)val; + + val_len = sizeof (val); + prop = "hw.optional.arm.FEAT_DotProd"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_dot = *(int*)val; + + val_len = sizeof (val); + prop = "hw.optional.arm.FEAT_SHA1"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_sha1 = *(int*)val; + + val_len = sizeof (val); + prop = "hw.optional.arm.FEAT_SHA256"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_sha256 = *(int*)val; + + val_len = sizeof (val); + prop = "hw.optional.arm.FEAT_AES"; + res = sysctlbyname (prop, val, &val_len, NULL, 0); + g_assert (res == 0); + g_assert (val_len == 4); + mono_hwcap_arm64_has_aes = *(int*)val; + +#endif } diff --git a/src/mono/mono/utils/mono-hwcap-vars.h b/src/mono/mono/utils/mono-hwcap-vars.h index dcd192912198d..e972f7e36d1e1 100644 --- a/src/mono/mono/utils/mono-hwcap-vars.h +++ b/src/mono/mono/utils/mono-hwcap-vars.h @@ -17,6 +17,13 @@ MONO_HWCAP_VAR(arm_has_thumb2) #elif defined (TARGET_ARM64) +MONO_HWCAP_VAR(arm64_has_crc32) +MONO_HWCAP_VAR(arm64_has_dot) +MONO_HWCAP_VAR(arm64_has_rdm) +MONO_HWCAP_VAR(arm64_has_sha1) +MONO_HWCAP_VAR(arm64_has_sha256) +MONO_HWCAP_VAR(arm64_has_aes) + // Nothing here yet. #elif defined (TARGET_POWERPC) || defined (TARGET_POWERPC64) From d288f3b31d62870ac108f5da290d8509d1cd43cd Mon Sep 17 00:00:00 2001 From: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> Date: Wed, 17 May 2023 22:47:38 +0300 Subject: [PATCH 11/12] Delete the verification type system (#86179) * Delete "verIsByRefLike" * Decouple SIMD class recognition from 'typeInfo' * Remove struct handles from 'typeInfo' * Delete the verification type system * Delete 'verType' comments --- src/coreclr/jit/CMakeLists.txt | 1 - src/coreclr/jit/_typeinfo.h | 347 ++-------------------- src/coreclr/jit/compiler.cpp | 8 +- src/coreclr/jit/compiler.h | 22 -- src/coreclr/jit/emit.cpp | 4 +- src/coreclr/jit/hwintrinsic.cpp | 3 +- src/coreclr/jit/importer.cpp | 466 ++++++++++-------------------- src/coreclr/jit/importercalls.cpp | 48 ++- src/coreclr/jit/inline.h | 12 +- src/coreclr/jit/inlinepolicy.cpp | 9 +- src/coreclr/jit/lsra.cpp | 2 +- src/coreclr/jit/titypes.h | 14 - src/coreclr/jit/typelist.h | 49 ++-- src/coreclr/jit/utils.cpp | 6 +- src/coreclr/jit/vartypesdef.h | 2 +- 15 files changed, 234 insertions(+), 759 deletions(-) delete mode 100644 src/coreclr/jit/titypes.h diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index 383b45f59e85e..d1af5c54515e9 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -374,7 +374,6 @@ set( JIT_HEADERS targetarm.h targetarm64.h tinyarray.h - titypes.h treelifeupdater.h typelist.h unwind.h diff --git a/src/coreclr/jit/_typeinfo.h b/src/coreclr/jit/_typeinfo.h index a149bcfe87d76..42526eeb8de4b 100644 --- a/src/coreclr/jit/_typeinfo.h +++ b/src/coreclr/jit/_typeinfo.h @@ -16,113 +16,17 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX in the NT SDK ******************************************************************************/ -/*****************************************************************************/ #ifndef _TYPEINFO_H_ #define _TYPEINFO_H_ -/*****************************************************************************/ -enum ti_types -{ -#define DEF_TI(ti, nm) ti, -#include "titypes.h" -#undef DEF_TI - TI_ONLY_ENUM = TI_METHOD, // Enum values with greater value are completely described by the enumeration. -}; - -#if defined(TARGET_64BIT) -#define TI_I_IMPL TI_LONG -#else -#define TI_I_IMPL TI_INT -#endif - -#ifdef _MSC_VER -namespace -{ -#endif // _MSC_VER -const ti_types g_jit_types_map[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) verType, -#include "typelist.h" -#undef DEF_TP -}; -#ifdef _MSC_VER -} -#endif // _MSC_VER - -// typeInfo does not care about distinction between signed/unsigned -// This routine converts all unsigned types to signed ones -inline ti_types varType2tiType(var_types type) -{ - assert(g_jit_types_map[TYP_BYTE] == TI_BYTE); - assert(g_jit_types_map[TYP_INT] == TI_INT); - assert(g_jit_types_map[TYP_UINT] == TI_INT); - assert(g_jit_types_map[TYP_FLOAT] == TI_FLOAT); - assert(g_jit_types_map[TYP_BYREF] == TI_ERROR); - assert(g_jit_types_map[type] != TI_ERROR); - return g_jit_types_map[type]; -} - -#ifdef _MSC_VER -namespace -{ -#endif // _MSC_VER -const ti_types g_ti_types_map[CORINFO_TYPE_COUNT] = { - // see the definition of enum CorInfoType in file inc/corinfo.h - TI_ERROR, // CORINFO_TYPE_UNDEF = 0x0, - TI_ERROR, // CORINFO_TYPE_VOID = 0x1, - TI_BYTE, // CORINFO_TYPE_BOOL = 0x2, - TI_SHORT, // CORINFO_TYPE_CHAR = 0x3, - TI_BYTE, // CORINFO_TYPE_BYTE = 0x4, - TI_BYTE, // CORINFO_TYPE_UBYTE = 0x5, - TI_SHORT, // CORINFO_TYPE_SHORT = 0x6, - TI_SHORT, // CORINFO_TYPE_USHORT = 0x7, - TI_INT, // CORINFO_TYPE_INT = 0x8, - TI_INT, // CORINFO_TYPE_UINT = 0x9, - TI_LONG, // CORINFO_TYPE_LONG = 0xa, - TI_LONG, // CORINFO_TYPE_ULONG = 0xb, - TI_I_IMPL, // CORINFO_TYPE_NATIVEINT = 0xc, - TI_I_IMPL, // CORINFO_TYPE_NATIVEUINT = 0xd, - TI_FLOAT, // CORINFO_TYPE_FLOAT = 0xe, - TI_DOUBLE, // CORINFO_TYPE_DOUBLE = 0xf, - TI_REF, // CORINFO_TYPE_STRING = 0x10, - TI_ERROR, // CORINFO_TYPE_PTR = 0x11, - TI_ERROR, // CORINFO_TYPE_BYREF = 0x12, - TI_STRUCT, // CORINFO_TYPE_VALUECLASS = 0x13, - TI_REF, // CORINFO_TYPE_CLASS = 0x14, - TI_STRUCT, // CORINFO_TYPE_REFANY = 0x15, - TI_REF, // CORINFO_TYPE_VAR = 0x16, -}; -#ifdef _MSC_VER -} -#endif // _MSC_VER - -// Convert the type returned from the VM to a ti_type. - -inline ti_types JITtype2tiType(CorInfoType type) -{ - // spot check to make certain enumerations have not changed - - assert(g_ti_types_map[CORINFO_TYPE_CLASS] == TI_REF); - assert(g_ti_types_map[CORINFO_TYPE_BYREF] == TI_ERROR); - assert(g_ti_types_map[CORINFO_TYPE_DOUBLE] == TI_DOUBLE); - assert(g_ti_types_map[CORINFO_TYPE_VALUECLASS] == TI_STRUCT); - assert(g_ti_types_map[CORINFO_TYPE_STRING] == TI_REF); - - type = CorInfoType(type & CORINFO_TYPE_MASK); // strip off modifiers - - assert(type < CORINFO_TYPE_COUNT); - - assert(g_ti_types_map[type] != TI_ERROR || type == CORINFO_TYPE_VOID); - return g_ti_types_map[type]; -}; - -/***************************************************************************** -* Captures information about a method pointer -* -* m_token is the CORINFO_RESOLVED_TOKEN from the IL, potentially with a more -* precise method handle from getCallInfo -* m_tokenConstraint is the constraint if this was a constrained ldftn. -* -*/ +// +// Captures information about a method pointer +// +// m_token is the CORINFO_RESOLVED_TOKEN from the IL, potentially with a more +// precise method handle from getCallInfo +// m_tokenConstraint is the constraint if this was a constrained ldftn. +// +// class methodPointerInfo { public: @@ -130,271 +34,70 @@ class methodPointerInfo mdToken m_tokenConstraint; }; -/***************************************************************************** - * Declares the typeInfo class, which represents the type of an entity on the - * stack, in a local variable or an argument. - * - * Flags: LLLLLLLLLLLLLLLLffffffffffTTTTTT - * - * L = unused - * f = flags - * T = type - * - * The lower bits are used to store the type component, and may be one of: - * - * TI_* (primitive) - see tyelist.h for enumeration (BYTE, SHORT, INT..) - * TI_REF - OBJREF / ARRAY use m_cls for the type - * (including arrays and null objref) - * TI_STRUCT - VALUE type, use m_cls for the actual type - * - * NOTE carefully that BYREF info is not stored here. You will never see a - * TI_BYREF in this component. For example, the type component - * of a "byref TI_INT" is TI_FLAG_BYREF | TI_INT. - * - */ - -#define TI_FLAG_DATA_BITS 6 -#define TI_FLAG_DATA_MASK ((1 << TI_FLAG_DATA_BITS) - 1) - -// Flag indicating this item is a byref -#define TI_FLAG_BYREF 0x00000080 -#define TI_ALL_BYREF_FLAGS (TI_FLAG_BYREF) - -/***************************************************************************** - * A typeInfo can be one of several types: - * - A primitive type (I4,I8,R4,R8,I) - * - A type (ref, array, value type) (m_cls describes the type) - * - An array (m_cls describes the array type) - * - A byref (byref flag set, otherwise the same as the above), - * - A Function Pointer (m_methodPointerInfo) - */ - +// Declares the typeInfo class, which represents the type of an entity on the stack. +// class typeInfo { - private: - union { - struct - { - ti_types type : TI_FLAG_DATA_BITS; - unsigned : 1; // unused - unsigned byref : 1; // used - unsigned : 1; // unused - unsigned : 1; // unused - unsigned : 1; // unused - unsigned : 1; // unused - unsigned : 1; // unused - unsigned : 1; // unused - unsigned : 1; // unused - } m_bits; - - DWORD m_flags; - }; + var_types m_type; union { - CORINFO_CLASS_HANDLE m_cls; - // Valid only for type TI_METHOD - methodPointerInfo* m_methodPointerInfo; + CORINFO_CLASS_HANDLE m_cls; // Valid, but not always available, for TYP_REFs. + methodPointerInfo* m_methodPointerInfo; // Valid only for function pointers. }; - template - static bool isInvalidHandle(const T handle) - { - static_assert(std::is_same::value || std::is_same::value, - ""); -#ifdef HOST_64BIT - return handle == reinterpret_cast(0xcccccccccccccccc); -#else - return handle == reinterpret_cast(0xcccccccc); -#endif - } - public: - typeInfo() : m_flags(TI_ERROR) + typeInfo() : m_type(TYP_UNDEF), m_cls(NO_CLASS_HANDLE) { - m_cls = NO_CLASS_HANDLE; } - typeInfo(ti_types tiType) + typeInfo(var_types type) : m_type(type), m_cls(NO_CLASS_HANDLE) { - assert((tiType >= TI_BYTE) && (tiType <= TI_NULL)); - - m_flags = (DWORD)tiType; - m_cls = NO_CLASS_HANDLE; } - typeInfo(var_types varType) + typeInfo(CORINFO_CLASS_HANDLE cls) : m_type(TYP_REF), m_cls(cls) { - m_flags = (DWORD)varType2tiType(varType); - m_cls = NO_CLASS_HANDLE; } - typeInfo(ti_types tiType, CORINFO_CLASS_HANDLE cls) - { - assert(tiType == TI_STRUCT || tiType == TI_REF); - assert(cls != nullptr && !isInvalidHandle(cls)); - m_flags = tiType; - m_cls = cls; - } - - typeInfo(methodPointerInfo* methodPointerInfo) + typeInfo(methodPointerInfo* methodPointerInfo) : m_type(TYP_I_IMPL), m_methodPointerInfo(methodPointerInfo) { assert(methodPointerInfo != nullptr); assert(methodPointerInfo->m_token.hMethod != nullptr); - assert(!isInvalidHandle(methodPointerInfo->m_token.hMethod)); - m_flags = TI_METHOD; - m_methodPointerInfo = methodPointerInfo; } public: - ///////////////////////////////////////////////////////////////////////// - // Operations - ///////////////////////////////////////////////////////////////////////// - - typeInfo& MakeByRef() - { - assert(!IsByRef()); - m_flags |= TI_FLAG_BYREF; - return *this; - } - - // I1,I2 --> I4 - // FLOAT --> DOUBLE - // objref, arrays, byrefs, value classes are unchanged - // - typeInfo& NormaliseForStack() - { - switch (GetType()) - { - case TI_BYTE: - case TI_SHORT: - m_flags = TI_INT; - break; - - case TI_FLOAT: - m_flags = TI_DOUBLE; - break; - default: - break; - } - return (*this); - } - - ///////////////////////////////////////////////////////////////////////// - // Getters - ///////////////////////////////////////////////////////////////////////// - - CORINFO_CLASS_HANDLE GetClassHandle() const - { - return m_cls; - } - - CORINFO_CLASS_HANDLE GetClassHandleForValueClass() const - { - assert(IsType(TI_STRUCT)); - assert(m_cls != NO_CLASS_HANDLE); - return m_cls; - } - CORINFO_CLASS_HANDLE GetClassHandleForObjRef() const { - assert(IsType(TI_REF)); - assert(m_cls != NO_CLASS_HANDLE); + assert((m_type == TYP_REF) || (m_type == TYP_UNDEF)); return m_cls; } CORINFO_METHOD_HANDLE GetMethod() const { - assert(GetType() == TI_METHOD); + assert(IsMethod()); return m_methodPointerInfo->m_token.hMethod; } methodPointerInfo* GetMethodPointerInfo() const { - assert(GetType() == TI_METHOD); + assert(IsMethod()); return m_methodPointerInfo; } - // Get this item's type - // If primitive, returns the primitive type (TI_*) - // If not primitive, returns: - // - TI_ERROR if a byref anything - // - TI_REF if a class or array or null or a generic type variable - // - TI_STRUCT if a value class - ti_types GetType() const - { - if (m_flags & TI_FLAG_BYREF) - { - return TI_ERROR; - } - - // objref/array/null (objref), value class, ptr, primitive - return (ti_types)(m_flags & TI_FLAG_DATA_MASK); - } - - bool IsType(ti_types type) const + var_types GetType() const { - assert(type != TI_ERROR); - return (m_flags & (TI_FLAG_DATA_MASK | TI_FLAG_BYREF)) == DWORD(type); + return m_type; } - // Returns whether this is a by-ref - bool IsByRef() const + bool IsType(var_types type) const { - return (m_flags & TI_FLAG_BYREF); + return m_type == type; } // Returns whether this is a method desc bool IsMethod() const { - return GetType() == TI_METHOD; + return IsType(TYP_I_IMPL) && (m_methodPointerInfo != nullptr); } - - bool IsStruct() const - { - return IsType(TI_STRUCT); - } - - // A byref value class is NOT a value class - bool IsValueClass() const - { - return (IsStruct() || IsPrimitiveType()); - } - - // Returns whether this is a primitive type (not a byref, objref, - // array, null, value class, invalid value) - // May Need to normalise first (m/r/I4 --> I4) - bool IsPrimitiveType() const - { - DWORD Type = GetType(); - - // boolean, char, u1,u2 never appear on the operand stack - return (Type == TI_BYTE || Type == TI_SHORT || Type == TI_INT || Type == TI_LONG || Type == TI_FLOAT || - Type == TI_DOUBLE); - } - -private: - // used to make functions that return typeinfo efficient. - typeInfo(DWORD flags, CORINFO_CLASS_HANDLE cls) - { - m_cls = cls; - m_flags = flags; - } - - friend typeInfo ByRef(const typeInfo& ti); - friend typeInfo NormaliseForStack(const typeInfo& ti); }; - -inline typeInfo NormaliseForStack(const typeInfo& ti) -{ - return typeInfo(ti).NormaliseForStack(); -} - -// given ti make a byref to that type. -inline typeInfo ByRef(const typeInfo& ti) -{ - return typeInfo(ti).MakeByRef(); -} -/*****************************************************************************/ #endif // _TYPEINFO_H_ -/*****************************************************************************/ diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c83d3dd765860..46edbb2945ae6 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -106,25 +106,25 @@ inline bool _our_GetThreadCycles(unsigned __int64* cycleOut) #endif // which host OS const BYTE genTypeSizes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) sz, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) sz, #include "typelist.h" #undef DEF_TP }; const BYTE genTypeAlignments[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) al, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) al, #include "typelist.h" #undef DEF_TP }; const BYTE genTypeStSzs[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) st, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) st, #include "typelist.h" #undef DEF_TP }; const BYTE genActualTypes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) jitType, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) jitType, #include "typelist.h" #undef DEF_TP }; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 8b2b06ce11555..2525f17c4bf60 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3817,7 +3817,6 @@ class Compiler void impResolveToken(const BYTE* addr, CORINFO_RESOLVED_TOKEN* pResolvedToken, CorInfoTokenKind kind); void impPushOnStack(GenTree* tree, typeInfo ti); - void impPushNullObjRefOnStack(); StackEntry impPopStack(); void impPopStack(unsigned n); StackEntry& impStackTop(unsigned n = 0); @@ -4309,7 +4308,6 @@ class Compiler BYTE impSpillCliqueGetMember(SpillCliqueDir predOrSucc, BasicBlock* blk); void impSpillCliqueSetMember(SpillCliqueDir predOrSucc, BasicBlock* blk, BYTE val); - void impPushVar(GenTree* op, typeInfo tiRetVal); GenTreeLclVar* impCreateLocalNode(unsigned lclNum DEBUGARG(IL_OFFSET offset)); void impLoadVar(unsigned lclNum, IL_OFFSET offset); void impLoadArg(unsigned ilArgNum, IL_OFFSET offset); @@ -8589,11 +8587,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX return false; } - bool isSIMDClass(typeInfo* pTypeInfo) - { - return pTypeInfo->IsStruct() && isSIMDClass(pTypeInfo->GetClassHandleForValueClass()); - } - bool isHWSIMDClass(CORINFO_CLASS_HANDLE clsHnd) { #ifdef FEATURE_HW_INTRINSICS @@ -8607,25 +8600,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX return false; } - bool isHWSIMDClass(typeInfo* pTypeInfo) - { -#ifdef FEATURE_HW_INTRINSICS - return pTypeInfo->IsStruct() && isHWSIMDClass(pTypeInfo->GetClassHandleForValueClass()); -#else - return false; -#endif - } - bool isSIMDorHWSIMDClass(CORINFO_CLASS_HANDLE clsHnd) { return isSIMDClass(clsHnd) || isHWSIMDClass(clsHnd); } - bool isSIMDorHWSIMDClass(typeInfo* pTypeInfo) - { - return isSIMDClass(pTypeInfo) || isHWSIMDClass(pTypeInfo); - } - // Get the base (element) type and size in bytes for a SIMD type. Returns CORINFO_TYPE_UNDEF // if it is not a SIMD type or is an unsupported base JIT type. CorInfoType getBaseJitTypeAndSizeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd, unsigned* sizeBytes = nullptr); @@ -10476,7 +10455,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX CORINFO_CLASS_HANDLE clsHnd); // converts from jit type representation to typeInfo typeInfo verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args); - bool verIsByRefLike(const typeInfo& ti); bool verCheckTailCallConstraint(OPCODE opcode, CORINFO_RESOLVED_TOKEN* pResolvedToken, diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 91e6276321d4b..11407c9cabb1d 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -507,13 +507,13 @@ void emitterStats(FILE* fout) /*****************************************************************************/ const unsigned short emitTypeSizes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) sze, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) sze, #include "typelist.h" #undef DEF_TP }; const unsigned short emitTypeActSz[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) asze, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) asze, #include "typelist.h" #undef DEF_TP }; diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 24ac9f7975c4c..35d1b6aa249bd 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -784,8 +784,7 @@ GenTree* Compiler::getArgForHWIntrinsic(var_types argType, // push newobj result on type stack unsigned lclNum = arg->AsLclVarCommon()->GetLclNum(); - impPushOnStack(gtNewLclvNode(lclNum, lvaGetRealType(lclNum)), - verMakeTypeInfo(argClass).NormaliseForStack()); + impPushOnStack(gtNewLclvNode(lclNum, lvaGetRealType(lclNum)), verMakeTypeInfo(argClass)); } } else diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 14866e1e171fb..6002a39dc7bb1 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -44,16 +44,6 @@ void Compiler::impPushOnStack(GenTree* tree, typeInfo ti) BADCODE("stack overflow"); } -#ifdef DEBUG - // If we are pushing a struct, make certain we know the precise type! - if (tree->TypeGet() == TYP_STRUCT) - { - assert(ti.IsType(TI_STRUCT)); - CORINFO_CLASS_HANDLE clsHnd = ti.GetClassHandle(); - assert(clsHnd != NO_CLASS_HANDLE); - } -#endif // DEBUG - verCurrentState.esStack[verCurrentState.esStackDepth].seTypeInfo = ti; verCurrentState.esStack[verCurrentState.esStackDepth++].val = tree; @@ -67,11 +57,6 @@ void Compiler::impPushOnStack(GenTree* tree, typeInfo ti) } } -void Compiler::impPushNullObjRefOnStack() -{ - impPushOnStack(gtNewIconNode(0, TYP_REF), typeInfo(TI_NULL)); -} - // helper function that will tell us if the IL instruction at the addr passed // by param consumes an address at the top of the stack. We use it to save // us lvAddrTaken @@ -1707,7 +1692,7 @@ bool Compiler::impSpillStackEntry(unsigned level, // If temp is newly introduced and a ref type, grab what type info we can. if (lvaTable[tnum].lvType == TYP_REF) { - CORINFO_CLASS_HANDLE stkHnd = verCurrentState.esStack[level].seTypeInfo.GetClassHandle(); + CORINFO_CLASS_HANDLE stkHnd = verCurrentState.esStack[level].seTypeInfo.GetClassHandleForObjRef(); lvaSetClass(tnum, tree, stkHnd); } @@ -1931,7 +1916,7 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H { tree = gtNewLclvNode(tree->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum(), TYP_REF); - impPushOnStack(tree, typeInfo(TI_REF, clsHnd)); + impPushOnStack(tree, typeInfo(clsHnd)); return hndBlk->bbNext; } @@ -1998,7 +1983,7 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H fgInsertStmtAtEnd(newBlk, argStmt); } - impPushOnStack(arg, typeInfo(TI_REF, clsHnd)); + impPushOnStack(arg, typeInfo(clsHnd)); return hndBlk; } @@ -2496,14 +2481,9 @@ typeInfo Compiler::verMakeTypeInfoForLocal(unsigned lclNum) { LclVarDsc* varDsc = lvaGetDesc(lclNum); - if (varDsc->TypeGet() == TYP_BYREF) - { - // Pretend all byrefs are pointing to bytes. - return typeInfo(TI_BYTE).MakeByRef(); - } - if (varTypeIsStruct(varDsc)) + if (varDsc->TypeGet() == TYP_REF) { - return typeInfo(TI_STRUCT, varDsc->GetLayout()->GetClassHandle()); + return typeInfo(varDsc->lvClassHnd); } return typeInfo(varDsc->TypeGet()); @@ -2511,107 +2491,20 @@ typeInfo Compiler::verMakeTypeInfoForLocal(unsigned lclNum) typeInfo Compiler::verMakeTypeInfo(CorInfoType ciType, CORINFO_CLASS_HANDLE clsHnd) { - assert(ciType < CORINFO_TYPE_COUNT); - - typeInfo tiResult; - switch (ciType) + if (ciType == CORINFO_TYPE_CLASS) { - case CORINFO_TYPE_STRING: - case CORINFO_TYPE_CLASS: - tiResult = verMakeTypeInfo(clsHnd); - if (!tiResult.IsType(TI_REF)) - { // type must be consistent with element type - return typeInfo(); - } - break; - - case CORINFO_TYPE_VALUECLASS: - case CORINFO_TYPE_REFANY: - tiResult = verMakeTypeInfo(clsHnd); - // type must be constant with element type; - if (!tiResult.IsValueClass()) - { - return typeInfo(); - } - break; - case CORINFO_TYPE_VAR: - return verMakeTypeInfo(clsHnd); - - case CORINFO_TYPE_PTR: // for now, pointers are treated as an error - case CORINFO_TYPE_VOID: - return typeInfo(); - break; - - case CORINFO_TYPE_BYREF: - { - CORINFO_CLASS_HANDLE childClassHandle; - CorInfoType childType = info.compCompHnd->getChildType(clsHnd, &childClassHandle); - return ByRef(verMakeTypeInfo(childType, childClassHandle)); - } - break; - - default: - if (clsHnd) - { // If we have more precise information, use it - return typeInfo(TI_STRUCT, clsHnd); - } - else - { - return typeInfo(JITtype2tiType(ciType)); - } + return typeInfo(clsHnd); } - return tiResult; -} -/******************************************************************************/ + return typeInfo(JITtype2varType(ciType)); +} typeInfo Compiler::verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd) { - if (clsHnd == NO_CLASS_HANDLE) - { - return typeInfo(); - } - - // Byrefs should only occur in method and local signatures, which are accessed - // using ICorClassInfo and ICorClassInfo.getChildType. - // So findClass() and getClassAttribs() should not be called for byrefs - - if (JITtype2varType(info.compCompHnd->asCorInfoType(clsHnd)) == TYP_BYREF) - { - assert(!"Did findClass() return a Byref?"); - return typeInfo(); - } - - unsigned attribs = info.compCompHnd->getClassAttribs(clsHnd); - - if (attribs & CORINFO_FLG_VALUECLASS) - { - CorInfoType t = info.compCompHnd->getTypeForPrimitiveValueClass(clsHnd); - - // Meta-data validation should ensure that CORINF_TYPE_BYREF should - // not occur here, so we may want to change this to an assert instead. - if (t == CORINFO_TYPE_VOID || t == CORINFO_TYPE_BYREF || t == CORINFO_TYPE_PTR) - { - return typeInfo(); - } - - if (t != CORINFO_TYPE_UNDEF) - { - return (typeInfo(JITtype2tiType(t))); - } - else - { - return (typeInfo(TI_STRUCT, clsHnd)); - } - } - else - { - return (typeInfo(TI_REF, clsHnd)); - } + assert(clsHnd != NO_CLASS_HANDLE); + return verMakeTypeInfo(info.compCompHnd->asCorInfoType(clsHnd), clsHnd); } -/***************************************************************************** - */ typeInfo Compiler::verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args) { CORINFO_CLASS_HANDLE classHandle; @@ -2633,19 +2526,6 @@ typeInfo Compiler::verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_L return verMakeTypeInfo(ciType, classHandle); } -bool Compiler::verIsByRefLike(const typeInfo& ti) -{ - if (ti.IsByRef()) - { - return true; - } - if (!ti.IsType(TI_STRUCT)) - { - return false; - } - return info.compCompHnd->getClassAttribs(ti.GetClassHandleForValueClass()) & CORINFO_FLG_BYREF_LIKE; -} - /***************************************************************************** * * Check if a TailCall is legal. @@ -2712,19 +2592,18 @@ bool Compiler::verCheckTailCallConstraint(OPCODE opcode, args = sig.args; while (argCount--) { - typeInfo tiDeclared = verParseArgSigToTypeInfo(&sig, args).NormaliseForStack(); - - // Check that the argument is not a byref for tailcalls. - if (verIsByRefLike(tiDeclared)) - { - return false; - } - // For unsafe code, we might have parameters containing pointer to the stack location. // Disallow the tailcall for this kind. CORINFO_CLASS_HANDLE classHandle; CorInfoType ciType = strip(info.compCompHnd->getArgType(&sig, args, &classHandle)); - if (ciType == CORINFO_TYPE_PTR) + if ((ciType == CORINFO_TYPE_PTR) || (ciType == CORINFO_TYPE_BYREF) || (ciType == CORINFO_TYPE_REFANY)) + { + return false; + } + + // Check that the argument is not a byref-like for tailcalls. + if ((ciType == CORINFO_TYPE_VALUECLASS) && + ((info.compCompHnd->getClassAttribs(classHandle) & CORINFO_FLG_BYREF_LIKE) != 0)) { return false; } @@ -2739,33 +2618,20 @@ bool Compiler::verCheckTailCallConstraint(OPCODE opcode, if (!(mflags & CORINFO_FLG_STATIC)) { // Always update the popCount. This is crucial for the stack calculation to be correct. - typeInfo tiThis = impStackTop(popCount).seTypeInfo; popCount++; if (opcode == CEE_CALLI) { - // For CALLI, we don't know the methodClassHnd. Therefore, let's check the "this" object - // on the stack. - if (tiThis.IsValueClass()) - { - tiThis.MakeByRef(); - } - - if (verIsByRefLike(tiThis)) + // For CALLI, we don't know the methodClassHnd. Therefore, let's check the "this" object on the stack. + if (impStackTop(popCount).val->TypeGet() != TYP_REF) { return false; } } else { - // Check type compatibility of the this argument - typeInfo tiDeclaredThis = verMakeTypeInfo(methodClassHnd); - if (tiDeclaredThis.IsValueClass()) - { - tiDeclaredThis.MakeByRef(); - } - - if (verIsByRefLike(tiDeclaredThis)) + // Check that the "this" argument is not a byref. + if (TypeHandleToVarType(methodClassHnd) != TYP_REF) { return false; } @@ -3024,7 +2890,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, result = gtNewOperNode(GT_COMMA, TYP_INT, nullcheck, result); } - impPushOnStack(result, typeInfo(TI_INT)); + impPushOnStack(result, typeInfo(TYP_INT)); return 0; } } @@ -3080,7 +2946,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, impPopStack(); impPushOnStack(gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0), - typeInfo(TI_INT)); + typeInfo(TYP_INT)); // Skip the next isinst instruction return 1 + sizeof(mdToken); @@ -3109,7 +2975,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, objToBox = impGetStructAddr(objToBox, CHECK_SPILL_ALL, true); static_assert_no_msg(OFFSETOF__CORINFO_NullableOfT__hasValue == 0); - impPushOnStack(gtNewIndir(TYP_BOOL, objToBox), typeInfo(TI_INT)); + impPushOnStack(gtNewIndir(TYP_BOOL, objToBox), typeInfo(TYP_INT)); JITDUMP("\n Importing BOX; ISINST; BR_TRUE/FALSE as nullableVT.hasValue\n"); return 1 + sizeof(mdToken); @@ -3117,7 +2983,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, else if (castResult == TypeCompareState::MustNot) { impPopStack(); - impPushOnStack(gtNewIconNode(0), typeInfo(TI_INT)); + impPushOnStack(gtNewIconNode(0), typeInfo(TYP_INT)); JITDUMP("\n Importing BOX; ISINST; BR_TRUE/FALSE as constant (false)\n"); return 1 + sizeof(mdToken); } @@ -3211,11 +3077,10 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) impSpillSpecialSideEff(); // Get get the expression to box from the stack. - GenTree* op1 = nullptr; - GenTree* op2 = nullptr; - StackEntry se = impPopStack(); - CORINFO_CLASS_HANDLE operCls = se.seTypeInfo.GetClassHandle(); - GenTree* exprToBox = se.val; + GenTree* op1 = nullptr; + GenTree* op2 = nullptr; + StackEntry se = impPopStack(); + GenTree* exprToBox = se.val; // Look at what helper we should use. CorInfoHelpFunc boxHelper = info.compCompHnd->getBoxHelper(pResolvedToken->hClass); @@ -3234,7 +3099,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) // structs is cheap. JITDUMP("\nCompiler::impImportAndPushBox -- handling BOX(value class) via"); bool canExpandInline = (boxHelper == CORINFO_HELP_BOX); - bool optForSize = !exprToBox->IsCall() && (operCls != nullptr) && opts.OptimizationDisabled(); + bool optForSize = !exprToBox->IsCall() && varTypeIsStruct(exprToBox) && opts.OptimizationDisabled(); bool expandInline = canExpandInline && !optForSize; if (expandInline) @@ -3385,7 +3250,6 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) // if (varTypeIsStruct(exprToBox)) { - assert(info.compCompHnd->getClassSize(pResolvedToken->hClass) == info.compCompHnd->getClassSize(operCls)); op1 = impAssignStructPtr(op1, exprToBox, CHECK_SPILL_ALL); } else @@ -3448,7 +3312,6 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) { // Don't optimize, just call the helper and be done with it. JITDUMP(" helper call because: %s\n", canExpandInline ? "optimizing for size" : "nullable"); - assert(operCls != nullptr); // Ensure that the value class is restored op2 = impTokenToHandle(pResolvedToken, nullptr, true /* mustRestoreHandle */); @@ -3463,8 +3326,8 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) } /* Push the result back on the stack, */ - /* even if clsHnd is a value class we want the TI_REF */ - typeInfo tiRetVal = typeInfo(TI_REF, info.compCompHnd->getTypeForBox(pResolvedToken->hClass)); + /* even if clsHnd is a value class we want the TYP_REF */ + typeInfo tiRetVal = typeInfo(info.compCompHnd->getTypeForBox(pResolvedToken->hClass)); impPushOnStack(op1, tiRetVal); } @@ -3547,7 +3410,7 @@ void Compiler::impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI // Remember that this function contains 'new' of a MD array. optMethodFlags |= OMF_HAS_MDNEWARRAY; - impPushOnStack(node, typeInfo(TI_REF, pResolvedToken->hClass)); + impPushOnStack(node, typeInfo(pResolvedToken->hClass)); } //------------------------------------------------------------------------ @@ -6442,7 +6305,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) break; case CEE_LDNULL: - impPushNullObjRefOnStack(); + impPushOnStack(gtNewIconNode(0, TYP_REF), typeInfo(TYP_REF)); break; case CEE_LDC_I4_M1: @@ -6467,25 +6330,25 @@ void Compiler::impImportBlockCode(BasicBlock* block) goto PUSH_I4CON; PUSH_I4CON: JITDUMP(" %d", cval.intVal); - impPushOnStack(gtNewIconNode(cval.intVal), typeInfo(TI_INT)); + impPushOnStack(gtNewIconNode(cval.intVal), typeInfo(TYP_INT)); break; case CEE_LDC_I8: cval.lngVal = getI8LittleEndian(codeAddr); JITDUMP(" 0x%016llx", cval.lngVal); - impPushOnStack(gtNewLconNode(cval.lngVal), typeInfo(TI_LONG)); + impPushOnStack(gtNewLconNode(cval.lngVal), typeInfo(TYP_LONG)); break; case CEE_LDC_R8: cval.dblVal = getR8LittleEndian(codeAddr); JITDUMP(" %#.17g", cval.dblVal); - impPushOnStack(gtNewDconNode(cval.dblVal), typeInfo(TI_DOUBLE)); + impPushOnStack(gtNewDconNode(cval.dblVal), typeInfo(TYP_DOUBLE)); break; case CEE_LDC_R4: cval.dblVal = getR4LittleEndian(codeAddr); JITDUMP(" %#.17g", cval.dblVal); - impPushOnStack(gtNewDconNode(cval.dblVal, TYP_FLOAT), typeInfo(TI_DOUBLE)); + impPushOnStack(gtNewDconNode(cval.dblVal, TYP_FLOAT), typeInfo(TYP_DOUBLE)); break; case CEE_LDSTR: @@ -6627,7 +6490,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) { StackEntry se = impPopStack(); - clsHnd = se.seTypeInfo.GetClassHandle(); op1 = se.val; tiRetVal = se.seTypeInfo; } @@ -6669,7 +6531,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (isSingleDefLocal && hasSingleReachingDef) { - lvaUpdateClass(lclNum, op1, clsHnd); + lvaUpdateClass(lclNum, op1, tiRetVal.GetClassHandleForObjRef()); } } @@ -6789,9 +6651,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) _PUSH_ADRVAR: assert(op1->IsLclVarAddr()); - - tiRetVal = typeInfo(TI_BYTE).MakeByRef(); - impPushOnStack(op1, tiRetVal); + impPushOnStack(op1, typeInfo(TYP_BYREF)); break; case CEE_ARGLIST: @@ -6991,7 +6851,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) ldelemClsHnd = resolvedToken.hClass; lclTyp = TypeHandleToVarType(ldelemClsHnd); tiRetVal = verMakeTypeInfo(ldelemClsHnd); - tiRetVal.NormaliseForStack(); goto ARR_LD; case CEE_LDELEM_I1: @@ -8091,10 +7950,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CEE_POP: { /* Pull the top value from the stack */ - - StackEntry se = impPopStack(); - clsHnd = se.seTypeInfo.GetClassHandle(); - op1 = se.val; + op1 = impPopStack().val; /* Get hold of the type of the value being duplicated */ @@ -8116,9 +7972,10 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Calls with large struct return value have to go through this. // Helper calls with small struct return value also have to go // through this since they do not follow Unix calling convention. - if (op1->gtOper != GT_CALL || - !IsMultiRegReturnedType(clsHnd, op1->AsCall()->GetUnmanagedCallConv()) || - op1->AsCall()->gtCallType == CT_HELPER) + if (!op1->IsCall() || + !IsMultiRegReturnedType(op1->AsCall()->gtRetClsHnd, + op1->AsCall()->GetUnmanagedCallConv()) || + op1->IsHelperCall()) #endif // UNIX_AMD64_ABI { // If the value being produced comes from loading @@ -8215,7 +8072,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Propagate type info to the temp from the stack and the original tree if (type == TYP_REF) { - lvaSetClass(tmpNum, tree, tiRetVal.GetClassHandle()); + lvaSetClass(tmpNum, tree, tiRetVal.GetClassHandleForObjRef()); } op1 = gtNewLclvNode(tmpNum, type); @@ -8956,16 +8813,12 @@ void Compiler::impImportBlockCode(BasicBlock* block) JITDUMP(" %08X", resolvedToken.token); - int aflags = isLoadAddress ? CORINFO_ACCESS_ADDRESS : CORINFO_ACCESS_GET; - - GenTree* obj = nullptr; - CORINFO_CLASS_HANDLE objType = nullptr; // used for fields + int aflags = isLoadAddress ? CORINFO_ACCESS_ADDRESS : CORINFO_ACCESS_GET; + GenTree* obj = nullptr; if ((opcode == CEE_LDFLD) || (opcode == CEE_LDFLDA)) { - StackEntry se = impPopStack(); - objType = se.seTypeInfo.GetClassHandle(); - obj = se.val; + obj = impPopStack().val; if (impIsThis(obj)) { @@ -9027,14 +8880,13 @@ void Compiler::impImportBlockCode(BasicBlock* block) } } - tiRetVal = verMakeTypeInfo(fieldInfo.fieldType, clsHnd); if (isLoadAddress) { - tiRetVal.MakeByRef(); + tiRetVal = typeInfo(TYP_BYREF); } else { - tiRetVal.NormaliseForStack(); + tiRetVal = verMakeTypeInfo(fieldInfo.fieldType, clsHnd); } // Perform this check always to ensure that we get field access exceptions even with @@ -9075,10 +8927,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { BADCODE3("Unexpected opcode (has to be LDFLD)", ": %02X", (int)opcode); } - if (objType == nullptr) - { - BADCODE("top of stack must be a value type"); - } + obj = impGetStructAddr(obj, CHECK_SPILL_ALL, true); } @@ -9346,9 +9195,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) } // Pull the value from the stack. - StackEntry se = impPopStack(); - op2 = se.val; - clsHnd = se.seTypeInfo.GetClassHandle(); + op2 = impPopStack().val; if (opcode == CEE_STFLD) { @@ -9894,7 +9741,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) op1->AsCall()->InitializeStructReturnType(this, classHandle, op1->AsCall()->GetUnmanagedCallConv()); #endif - tiRetVal = typeInfo(TI_STRUCT, classHandle); + tiRetVal = typeInfo(TYP_STRUCT); impPushOnStack(op1, tiRetVal); } break; @@ -10198,7 +10045,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) // If non register passable struct we have it materialized in the RetBuf. assert(op1->gtType == TYP_STRUCT); tiRetVal = verMakeTypeInfo(resolvedToken.hClass); - assert(tiRetVal.IsValueClass()); } } @@ -10666,13 +10512,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) #pragma warning(pop) #endif -// Push a local/argument treeon the operand stack -void Compiler::impPushVar(GenTree* op, typeInfo tiRetVal) -{ - tiRetVal.NormaliseForStack(); - impPushOnStack(op, tiRetVal); -} - //------------------------------------------------------------------------ // impCreateLocal: create a GT_LCL_VAR node to access a local that might need to be normalized on load // @@ -10703,7 +10542,7 @@ GenTreeLclVar* Compiler::impCreateLocalNode(unsigned lclNum DEBUGARG(IL_OFFSET o // lclNum is an index into lvaTable *NOT* the arg/lcl index in the IL void Compiler::impLoadVar(unsigned lclNum, IL_OFFSET offset) { - impPushVar(impCreateLocalNode(lclNum DEBUGARG(offset)), verMakeTypeInfoForLocal(lclNum)); + impPushOnStack(impCreateLocalNode(lclNum DEBUGARG(offset)), verMakeTypeInfoForLocal(lclNum)); } // Load an argument on the operand stack @@ -10720,8 +10559,18 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset) return; } - impPushVar(impInlineFetchArg(ilArgNum, impInlineInfo->inlArgInfo, impInlineInfo->lclVarInfo), - impInlineInfo->lclVarInfo[ilArgNum].lclVerTypeInfo); + var_types type = impInlineInfo->lclVarInfo[ilArgNum].lclTypeInfo; + typeInfo tiRetVal; + if (type == TYP_REF) + { + tiRetVal = typeInfo(impInlineInfo->lclVarInfo[ilArgNum].lclTypeHandle); + } + else + { + tiRetVal = typeInfo(type); + } + + impPushOnStack(impInlineFetchArg(ilArgNum, impInlineInfo->inlArgInfo, impInlineInfo->lclVarInfo), tiRetVal); } else { @@ -10747,6 +10596,7 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset) // It will be mapped to the correct lvaTable index void Compiler::impLoadLoc(unsigned ilLclNum, IL_OFFSET offset) { + unsigned lclNum; if (compIsForInlining()) { if (ilLclNum >= info.compMethodInfo->locals.numArgs) @@ -10755,21 +10605,8 @@ void Compiler::impLoadLoc(unsigned ilLclNum, IL_OFFSET offset) return; } - // Get the local type - var_types lclTyp = impInlineInfo->lclVarInfo[ilLclNum + impInlineInfo->argCnt].lclTypeInfo; - - typeInfo tiRetVal = impInlineInfo->lclVarInfo[ilLclNum + impInlineInfo->argCnt].lclVerTypeInfo; - - /* Have we allocated a temp for this local? */ - - unsigned lclNum = impInlineFetchLocal(ilLclNum DEBUGARG("Inline ldloc first use temp")); - - // All vars of inlined methods should be !lvNormalizeOnLoad() - - assert(!lvaTable[lclNum].lvNormalizeOnLoad()); - lclTyp = genActualType(lclTyp); - - impPushVar(gtNewLclvNode(lclNum, lclTyp), tiRetVal); + // Have we allocated a temp for this local? + lclNum = impInlineFetchLocal(ilLclNum DEBUGARG("Inline ldloc first use temp")); } else { @@ -10778,10 +10615,10 @@ void Compiler::impLoadLoc(unsigned ilLclNum, IL_OFFSET offset) BADCODE("Bad IL"); } - unsigned lclNum = info.compArgsCount + ilLclNum; - - impLoadVar(lclNum, offset); + lclNum = info.compArgsCount + ilLclNum; } + + impLoadVar(lclNum, offset); } //------------------------------------------------------------------------ @@ -10848,15 +10685,12 @@ bool Compiler::impReturnInstruction(int prefixFlags, OPCODE& opcode) } #endif // DEBUG - GenTree* op2 = nullptr; - GenTree* op1 = nullptr; - CORINFO_CLASS_HANDLE retClsHnd = nullptr; + GenTree* op2 = nullptr; + GenTree* op1 = nullptr; if (info.compRetType != TYP_VOID) { - StackEntry se = impPopStack(); - retClsHnd = se.seTypeInfo.GetClassHandle(); - op2 = se.val; + op2 = impPopStack().val; if (!compIsForInlining()) { @@ -13124,7 +12958,12 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) CallArg* thisArg = call->gtArgs.GetThisArg(); if (thisArg != nullptr) { - lclVarInfo[0].lclVerTypeInfo = verMakeTypeInfo(pInlineInfo->inlineCandidateInfo->clsHandle); + bool isValueClassThis = ((clsAttr & CORINFO_FLG_VALUECLASS) != 0); + var_types sigType = isValueClassThis ? TYP_BYREF : TYP_REF; + CORINFO_CLASS_HANDLE sigThisClass = pInlineInfo->inlineCandidateInfo->clsHandle; + + lclVarInfo[0].lclTypeInfo = sigType; + lclVarInfo[0].lclTypeHandle = isValueClassThis ? NO_CLASS_HANDLE : sigThisClass; lclVarInfo[0].lclHasLdlocaOp = false; #ifdef FEATURE_SIMD @@ -13132,22 +12971,19 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) // the inlining multiplier) for anything in that assembly. // But we only need to normalize it if it is a TYP_STRUCT // (which we need to do even if we have already set foundSIMDType). - if (!foundSIMDType && isSIMDorHWSIMDClass(&(lclVarInfo[0].lclVerTypeInfo))) + if (!foundSIMDType && isValueClassThis && isSIMDorHWSIMDClass(sigThisClass)) { foundSIMDType = true; } #endif // FEATURE_SIMD - var_types sigType = ((clsAttr & CORINFO_FLG_VALUECLASS) != 0) ? TYP_BYREF : TYP_REF; - lclVarInfo[0].lclTypeInfo = sigType; - GenTree* thisArgNode = thisArg->GetEarlyNode(); assert(varTypeIsGC(thisArgNode->TypeGet()) || // "this" is managed ((thisArgNode->TypeGet() == TYP_I_IMPL) && // "this" is unmgd but the method's class doesnt care - (clsAttr & CORINFO_FLG_VALUECLASS))); + isValueClassThis)); - if (genActualType(thisArgNode->TypeGet()) != genActualType(sigType)) + if (genActualType(thisArgNode) != genActualType(sigType)) { if (sigType == TYP_REF) { @@ -13159,9 +12995,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) /* This can only happen with byrefs <-> ints/shorts */ assert(sigType == TYP_BYREF); - assert((genActualType(thisArgNode->TypeGet()) == TYP_I_IMPL) || (thisArgNode->TypeGet() == TYP_BYREF)); - - lclVarInfo[0].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); + assert((genActualType(thisArgNode) == TYP_I_IMPL) || (thisArgNode->TypeGet() == TYP_BYREF)); } } @@ -13176,27 +13010,30 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) unsigned i; for (i = (thisArg ? 1 : 0); i < ilArgCnt; i++, argLst = info.compCompHnd->getArgNext(argLst)) { - var_types sigType = (var_types)eeGetArgType(argLst, &methInfo->args); - - lclVarInfo[i].lclVerTypeInfo = verParseArgSigToTypeInfo(&methInfo->args, argLst); + CORINFO_CLASS_HANDLE argSigClass; + CorInfoType argSigJitType = strip(info.compCompHnd->getArgType(&methInfo->args, argLst, &argSigClass)); + var_types sigType = TypeHandleToVarType(argSigJitType, argSigClass); #ifdef FEATURE_SIMD - if ((!foundSIMDType || (sigType == TYP_STRUCT)) && isSIMDorHWSIMDClass(&(lclVarInfo[i].lclVerTypeInfo))) + // If this is a SIMD class (i.e. in the SIMD assembly), then we will consider that we've + // found a SIMD type, even if this may not be a type we recognize (the assumption is that + // it is likely to use a SIMD type, and therefore we want to increase the inlining multiplier). + if (!foundSIMDType && varTypeIsStruct(sigType) && isSIMDorHWSIMDClass(argSigClass)) { - // If this is a SIMD class (i.e. in the SIMD assembly), then we will consider that we've - // found a SIMD type, even if this may not be a type we recognize (the assumption is that - // it is likely to use a SIMD type, and therefore we want to increase the inlining multiplier). foundSIMDType = true; - if (sigType == TYP_STRUCT) - { - var_types structType = impNormStructType(lclVarInfo[i].lclVerTypeInfo.GetClassHandle()); - sigType = structType; - } } #endif // FEATURE_SIMD - lclVarInfo[i].lclTypeInfo = sigType; lclVarInfo[i].lclHasLdlocaOp = false; + lclVarInfo[i].lclTypeInfo = sigType; + if (sigType == TYP_REF) + { + lclVarInfo[i].lclTypeHandle = eeGetArgClass(&methInfo->args, argLst); + } + else if (varTypeIsStruct(sigType)) + { + lclVarInfo[i].lclTypeHandle = argSigClass; + } // Does the tree type match the signature type? @@ -13231,21 +13068,16 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) // Is it a narrowing or widening cast? // Widening casts are ok since the value computed is already // normalized to an int (on the IL stack) - if (genTypeSize(inlArgNode->gtType) >= genTypeSize(sigType)) + if (genTypeSize(inlArgNode) >= genTypeSize(sigType)) { - if (sigType == TYP_BYREF) - { - lclVarInfo[i].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); - } - else if (inlArgNode->gtType == TYP_BYREF) + if ((sigType != TYP_BYREF) && (inlArgNode->TypeGet() == TYP_BYREF)) { assert(varTypeIsIntOrI(sigType)); /* If possible bash the BYREF to an int */ if (inlArgNode->OperIs(GT_LCL_ADDR)) { - inlArgNode->gtType = TYP_I_IMPL; - lclVarInfo[i].lclVerTypeInfo = typeInfo(varType2tiType(TYP_I_IMPL)); + inlArgNode->gtType = TYP_I_IMPL; } else { @@ -13304,11 +13136,22 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) for (i = 0; i < methInfo->locals.numArgs; i++) { - bool isPinned; - var_types type = (var_types)eeGetArgType(localsSig, &methInfo->locals, &isPinned); + CORINFO_CLASS_HANDLE sigClass; + ClassLayout* layout; + CorInfoTypeWithMod sigJitTypeWithMod = info.compCompHnd->getArgType(&methInfo->locals, localsSig, &sigClass); + var_types type = TypeHandleToVarType(strip(sigJitTypeWithMod), sigClass, &layout); + bool isPinned = (sigJitTypeWithMod & ~CORINFO_TYPE_MASK) != 0; lclVarInfo[i + ilArgCnt].lclHasLdlocaOp = false; lclVarInfo[i + ilArgCnt].lclTypeInfo = type; + if (type == TYP_REF) + { + lclVarInfo[i + ilArgCnt].lclTypeHandle = eeGetArgClass(&methInfo->locals, localsSig); + } + else if (varTypeIsStruct(type)) + { + lclVarInfo[i + ilArgCnt].lclTypeHandle = sigClass; + } if (varTypeIsGC(type)) { @@ -13332,49 +13175,36 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) JITDUMP("Ignoring pin on inlinee local #%02u -- not a GC type\n", i); } - lclVarInfo[i + ilArgCnt].lclVerTypeInfo = verParseArgSigToTypeInfo(&methInfo->locals, localsSig); - // If this local is a struct type with GC fields, inform the inliner. It may choose to bail // out on the inline. - if (type == TYP_STRUCT) + if ((type == TYP_STRUCT) && layout->HasGCPtr()) { - CORINFO_CLASS_HANDLE lclHandle = lclVarInfo[i + ilArgCnt].lclVerTypeInfo.GetClassHandle(); - DWORD typeFlags = info.compCompHnd->getClassAttribs(lclHandle); - if ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) != 0) + inlineResult->Note(InlineObservation::CALLEE_HAS_GC_STRUCT); + if (inlineResult->IsFailure()) + { + return; + } + + // Do further notification in the case where the call site is rare; some policies do + // not track the relative hotness of call sites for "always" inline cases. + if (pInlineInfo->iciBlock->isRunRarely()) { - inlineResult->Note(InlineObservation::CALLEE_HAS_GC_STRUCT); + inlineResult->Note(InlineObservation::CALLSITE_RARE_GC_STRUCT); if (inlineResult->IsFailure()) { return; } - - // Do further notification in the case where the call site is rare; some policies do - // not track the relative hotness of call sites for "always" inline cases. - if (pInlineInfo->iciBlock->isRunRarely()) - { - inlineResult->Note(InlineObservation::CALLSITE_RARE_GC_STRUCT); - if (inlineResult->IsFailure()) - { - - return; - } - } } } - localsSig = info.compCompHnd->getArgNext(localsSig); - #ifdef FEATURE_SIMD - if ((!foundSIMDType || (type == TYP_STRUCT)) && isSIMDorHWSIMDClass(&(lclVarInfo[i + ilArgCnt].lclVerTypeInfo))) + if (!foundSIMDType && varTypeIsStruct(type) && isSIMDorHWSIMDClass(sigClass)) { foundSIMDType = true; - if (type == TYP_STRUCT) - { - var_types structType = impNormStructType(lclVarInfo[i + ilArgCnt].lclVerTypeInfo.GetClassHandle()); - lclVarInfo[i + ilArgCnt].lclTypeInfo = structType; - } } #endif // FEATURE_SIMD + + localsSig = info.compCompHnd->getArgNext(localsSig); } #ifdef FEATURE_SIMD @@ -13382,6 +13212,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) { foundSIMDType = true; } + pInlineInfo->hasSIMDTypeArgLocalOrReturn = foundSIMDType; #endif // FEATURE_SIMD } @@ -13438,15 +13269,12 @@ unsigned Compiler::impInlineFetchLocal(unsigned lclNum DEBUGARG(const char* reas // signature and pass in a more precise type. if (lclTyp == TYP_REF) { - lvaSetClass(tmpNum, inlineeLocal.lclVerTypeInfo.GetClassHandleForObjRef()); + lvaSetClass(tmpNum, inlineeLocal.lclTypeHandle); } - if (inlineeLocal.lclVerTypeInfo.IsStruct()) + if (varTypeIsStruct(lclTyp)) { - if (varTypeIsStruct(lclTyp)) - { - lvaSetStruct(tmpNum, inlineeLocal.lclVerTypeInfo.GetClassHandle(), true /* unsafe value cls check */); - } + lvaSetStruct(tmpNum, inlineeLocal.lclTypeHandle, true /* unsafe value cls check */); } #ifdef DEBUG @@ -13631,16 +13459,15 @@ GenTree* Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, In JITDUMP("Marked V%02u as a single def temp\n", tmpNum); if (lclTyp == TYP_REF) { - lvaSetClass(tmpNum, argNode, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef()); + lvaSetClass(tmpNum, argNode, lclInfo.lclTypeHandle); } } else { if (lclTyp == TYP_REF) { - // Arg might be modified, use the declared type of - // the argument. - lvaSetClass(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandleForObjRef()); + // Arg might be modified, use the declared type of the argument. + lvaSetClass(tmpNum, lclInfo.lclTypeHandle); } } @@ -13650,15 +13477,12 @@ GenTree* Compiler::impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, In lvaTable[tmpNum].lvHasLdAddrOp = 1; } - if (lclInfo.lclVerTypeInfo.IsStruct()) + if (varTypeIsStruct(lclTyp)) { - if (varTypeIsStruct(lclTyp)) + lvaSetStruct(tmpNum, lclInfo.lclTypeHandle, true /* unsafe value cls check */); + if (info.compIsVarArgs) { - lvaSetStruct(tmpNum, lclInfo.lclVerTypeInfo.GetClassHandle(), true /* unsafe value cls check */); - if (info.compIsVarArgs) - { - lvaSetStructUsedAsVarArg(tmpNum); - } + lvaSetStructUsedAsVarArg(tmpNum); } } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 3a625b7cc0b8c..2c853e8f46d3c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -978,7 +978,7 @@ var_types Compiler::impImportCall(OPCODE opcode, call->gtType = TYP_REF; impSpillSpecialSideEff(); - impPushOnStack(call, typeInfo(TI_REF, clsHnd)); + impPushOnStack(call, typeInfo(clsHnd)); } else { @@ -1018,8 +1018,7 @@ var_types Compiler::impImportCall(OPCODE opcode, assert(newobjThis->IsLclVarAddr()); unsigned lclNum = newobjThis->AsLclVarCommon()->GetLclNum(); - impPushOnStack(gtNewLclvNode(lclNum, lvaGetRealType(lclNum)), - verMakeTypeInfo(clsHnd).NormaliseForStack()); + impPushOnStack(gtNewLclvNode(lclNum, lvaGetRealType(lclNum)), verMakeTypeInfo(clsHnd)); } else { @@ -1030,8 +1029,7 @@ var_types Compiler::impImportCall(OPCODE opcode, } assert(newobjThis->gtOper == GT_LCL_VAR); - impPushOnStack(gtNewLclvNode(newobjThis->AsLclVarCommon()->GetLclNum(), TYP_REF), - typeInfo(TI_REF, clsHnd)); + impPushOnStack(gtNewLclvNode(newobjThis->AsLclVarCommon()->GetLclNum(), TYP_REF), typeInfo(clsHnd)); } } return callRetTyp; @@ -1314,7 +1312,6 @@ var_types Compiler::impImportCall(OPCODE opcode, } typeInfo tiRetVal = verMakeTypeInfo(sig->retType, sig->retTypeClass); - tiRetVal.NormaliseForStack(); if (call->IsCall()) { @@ -1463,23 +1460,20 @@ var_types Compiler::impImportJitTestLabelMark(int numArgs) TestLabelAndNum tlAndN; if (numArgs == 2) { - tlAndN.m_num = 0; - StackEntry se = impPopStack(); - assert(se.seTypeInfo.GetType() == TI_INT); - GenTree* val = se.val; + tlAndN.m_num = 0; + StackEntry se = impPopStack(); + GenTree* val = se.val; assert(val->IsCnsIntOrI()); tlAndN.m_tl = (TestLabel)val->AsIntConCommon()->IconValue(); } else if (numArgs == 3) { - StackEntry se = impPopStack(); - assert(se.seTypeInfo.GetType() == TI_INT); - GenTree* val = se.val; + StackEntry se = impPopStack(); + GenTree* val = se.val; assert(val->IsCnsIntOrI()); tlAndN.m_num = val->AsIntConCommon()->IconValue(); se = impPopStack(); - assert(se.seTypeInfo.GetType() == TI_INT); - val = se.val; + val = se.val; assert(val->IsCnsIntOrI()); tlAndN.m_tl = (TestLabel)val->AsIntConCommon()->IconValue(); } @@ -5158,9 +5152,6 @@ void Compiler::impPopCallArgs(CORINFO_SIG_INFO* sig, GenTreeCall* call) if (varTypeIsStruct(argNode)) { - // Morph trees that aren't already OBJs or MKREFANY to be OBJs - assert(ti.IsType(TI_STRUCT)); - JITDUMP("Calling impNormStructVal on:\n"); DISPTREE(argNode); @@ -5248,7 +5239,7 @@ GenTree* Compiler::impTransformThis(GenTree* thisPtr, // This pushes on the dereferenced byref // This is then used immediately to box. - impPushOnStack(obj, verMakeTypeInfo(pConstrainedResolvedToken->hClass).NormaliseForStack()); + impPushOnStack(obj, verMakeTypeInfo(pConstrainedResolvedToken->hClass)); // This pops off the byref-to-a-value-type remaining on the stack and // replaces it with a boxed object. @@ -8842,32 +8833,29 @@ GenTree* Compiler::impArrayAccessIntrinsic( if ((intrinsicName != NI_Array_Get) && !readonlyCall && varTypeIsGC(elemType)) { // Get the call site signature - CORINFO_SIG_INFO LocalSig; - eeGetCallSiteSig(memberRef, info.compScopeHnd, impTokenLookupContextHandle, &LocalSig); - assert(LocalSig.hasThis()); + CORINFO_SIG_INFO localSig; + eeGetCallSiteSig(memberRef, info.compScopeHnd, impTokenLookupContextHandle, &localSig); + assert(localSig.hasThis()); CORINFO_CLASS_HANDLE actualElemClsHnd; if (intrinsicName == NI_Array_Set) { // Fetch the last argument, the one that indicates the type we are setting. - CORINFO_ARG_LIST_HANDLE argType = LocalSig.args; + CORINFO_ARG_LIST_HANDLE argList = localSig.args; for (unsigned r = 0; r < rank; r++) { - argType = info.compCompHnd->getArgNext(argType); + argList = info.compCompHnd->getArgNext(argList); } - typeInfo argInfo = verParseArgSigToTypeInfo(&LocalSig, argType); - actualElemClsHnd = argInfo.GetClassHandle(); + actualElemClsHnd = eeGetArgClass(&localSig, argList); } else { assert(intrinsicName == NI_Array_Address); + assert((localSig.retType == CORINFO_TYPE_BYREF) && (localSig.retTypeClass != NO_CLASS_HANDLE)); - // Fetch the return type - typeInfo retInfo = verMakeTypeInfo(LocalSig.retType, LocalSig.retTypeClass); - assert(retInfo.IsByRef()); - actualElemClsHnd = retInfo.GetClassHandle(); + info.compCompHnd->getChildType(localSig.retTypeClass, &actualElemClsHnd); } // if it's not final, we can't do the optimization diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index aa4edada1200b..74c337e998f24 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -663,12 +663,12 @@ struct InlArgInfo struct InlLclVarInfo { - typeInfo lclVerTypeInfo; - var_types lclTypeInfo; - unsigned lclHasLdlocaOp : 1; // Is there LDLOCA(s) operation on this local? - unsigned lclHasStlocOp : 1; // Is there a STLOC on this local? - unsigned lclHasMultipleStlocOp : 1; // Is there more than one STLOC on this local - unsigned lclIsPinned : 1; + CORINFO_CLASS_HANDLE lclTypeHandle; // Type handle from the signature. Available for structs and REFs. + var_types lclTypeInfo; // Type from the signature. + unsigned char lclHasLdlocaOp : 1; // Is there LDLOCA(s) operation on this local? + unsigned char lclHasStlocOp : 1; // Is there a STLOC on this local? + unsigned char lclHasMultipleStlocOp : 1; // Is there more than one STLOC on this local + unsigned char lclIsPinned : 1; }; // InlineInfo provides detailed information about a particular inline candidate. diff --git a/src/coreclr/jit/inlinepolicy.cpp b/src/coreclr/jit/inlinepolicy.cpp index 3d1d895c434a5..c9f0d509d4b96 100644 --- a/src/coreclr/jit/inlinepolicy.cpp +++ b/src/coreclr/jit/inlinepolicy.cpp @@ -841,7 +841,7 @@ int DefaultPolicy::DetermineNativeSizeEstimate() // Estimates the native size (in bytes, scaled up by 10x) for the // call site. While the quality of the estimate here is questionable // (especially for x64) it is being left as is for legacy compatibility. - +// int DefaultPolicy::DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methInfo) { int callsiteSize = 55; // Direct call take 5 native bytes; indirect call takes 6 native bytes. @@ -856,12 +856,11 @@ int DefaultPolicy::DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* meth for (unsigned i = 0; i < methInfo->args.numArgs; i++, argLst = comp->getArgNext(argLst)) { - var_types sigType = (var_types)m_RootCompiler->eeGetArgType(argLst, &methInfo->args); + CORINFO_CLASS_HANDLE sigClass; + var_types sigType = JITtype2varType(strip(comp->getArgType(&methInfo->args, argLst, &sigClass))); if (sigType == TYP_STRUCT) { - typeInfo verType = m_RootCompiler->verParseArgSigToTypeInfo(&methInfo->args, argLst); - /* IN0028: 00009B lea EAX, bword ptr [EBP-14H] @@ -873,7 +872,7 @@ int DefaultPolicy::DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* meth callsiteSize += 10; // "lea EAX, bword ptr [EBP-14H]" - unsigned opsz = roundUp(comp->getClassSize(verType.GetClassHandle()), TARGET_POINTER_SIZE); + unsigned opsz = roundUp(comp->getClassSize(sigClass), TARGET_POINTER_SIZE); unsigned slots = opsz / TARGET_POINTER_SIZE; callsiteSize += slots * 20; // "push gword ptr [EAX+offs] " diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 277ad0dd86684..a6b3d0c1ebaa2 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -803,7 +803,7 @@ LinearScan::LinearScan(Compiler* theCompiler) // Initialize the availableRegs to use for each TYP_* CLANG_FORMAT_COMMENT_ANCHOR; -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) \ +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) \ availableRegs[static_cast(TYP_##tn)] = ®Fld; #include "typelist.h" #undef DEF_TP diff --git a/src/coreclr/jit/titypes.h b/src/coreclr/jit/titypes.h deleted file mode 100644 index 02e71cc38a91a..0000000000000 --- a/src/coreclr/jit/titypes.h +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -DEF_TI(TI_ERROR, "") -DEF_TI(TI_REF, "Ref") -DEF_TI(TI_STRUCT, "Struct") -DEF_TI(TI_METHOD, "Method") -DEF_TI(TI_BYTE, "Byte") -DEF_TI(TI_SHORT, "Short") -DEF_TI(TI_INT, "Int") -DEF_TI(TI_LONG, "Long") -DEF_TI(TI_FLOAT, "Float") -DEF_TI(TI_DOUBLE, "Double") -DEF_TI(TI_NULL, "Null") diff --git a/src/coreclr/jit/typelist.h b/src/coreclr/jit/typelist.h index 5181d323c81df..81b06c1917d8d 100644 --- a/src/coreclr/jit/typelist.h +++ b/src/coreclr/jit/typelist.h @@ -18,7 +18,6 @@ /* tn - TYP_name nm - name string jitType - The jit compresses types that are 'equivalent', this is the jit type genActualType() - verType - Used for type checking sz - size in bytes (genTypeSize(t)) sze - size in bytes for the emitter (GC types are encoded) (emitTypeSize(t)) asze - size in bytes for the emitter (GC types are encoded) (emitActualTypeSize(t)) @@ -28,45 +27,45 @@ regFld - LSRA: field to use to track available registers tf - flags -DEF_TP(tn ,nm , jitType, verType, sz,sze,asze, st,al, regTyp, regFld, tf ) +DEF_TP(tn ,nm , jitType, sz,sze,asze, st,al, regTyp, regFld, tf ) */ // clang-format off -DEF_TP(UNDEF ,"" , TYP_UNDEF, TI_ERROR, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) -DEF_TP(VOID ,"void" , TYP_VOID, TI_ERROR, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) +DEF_TP(UNDEF ,"" , TYP_UNDEF, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) +DEF_TP(VOID ,"void" , TYP_VOID, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) -DEF_TP(BOOL ,"bool" , TYP_INT, TI_BYTE, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) -DEF_TP(BYTE ,"byte" , TYP_INT, TI_BYTE, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT) -DEF_TP(UBYTE ,"ubyte" , TYP_INT, TI_BYTE, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) +DEF_TP(BOOL ,"bool" , TYP_INT, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) +DEF_TP(BYTE ,"byte" , TYP_INT, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT) +DEF_TP(UBYTE ,"ubyte" , TYP_INT, 1, 1, 4, 1, 1, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) -DEF_TP(SHORT ,"short" , TYP_INT, TI_SHORT, 2, 2, 4, 1, 2, VTR_INT, availableIntRegs, VTF_INT) -DEF_TP(USHORT ,"ushort" , TYP_INT, TI_SHORT, 2, 2, 4, 1, 2, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) +DEF_TP(SHORT ,"short" , TYP_INT, 2, 2, 4, 1, 2, VTR_INT, availableIntRegs, VTF_INT) +DEF_TP(USHORT ,"ushort" , TYP_INT, 2, 2, 4, 1, 2, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS) -DEF_TP(INT ,"int" , TYP_INT, TI_INT, 4, 4, 4, 1, 4, VTR_INT, availableIntRegs, VTF_INT|VTF_I32) -DEF_TP(UINT ,"uint" , TYP_INT, TI_INT, 4, 4, 4, 1, 4, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS|VTF_I32) // Only used in GT_CAST nodes +DEF_TP(INT ,"int" , TYP_INT, 4, 4, 4, 1, 4, VTR_INT, availableIntRegs, VTF_INT|VTF_I32) +DEF_TP(UINT ,"uint" , TYP_INT, 4, 4, 4, 1, 4, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS|VTF_I32) // Only used in GT_CAST nodes -DEF_TP(LONG ,"long" , TYP_LONG, TI_LONG, 8,EPS,EPS, 2, 8, VTR_INT, availableIntRegs, VTF_INT|VTF_I64) -DEF_TP(ULONG ,"ulong" , TYP_LONG, TI_LONG, 8,EPS,EPS, 2, 8, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS|VTF_I64) // Only used in GT_CAST nodes +DEF_TP(LONG ,"long" , TYP_LONG, 8,EPS,EPS, 2, 8, VTR_INT, availableIntRegs, VTF_INT|VTF_I64) +DEF_TP(ULONG ,"ulong" , TYP_LONG, 8,EPS,EPS, 2, 8, VTR_INT, availableIntRegs, VTF_INT|VTF_UNS|VTF_I64) // Only used in GT_CAST nodes -DEF_TP(FLOAT ,"float" , TYP_FLOAT, TI_FLOAT, 4, 4, 4, 1, 4, VTR_FLOAT, availableFloatRegs, VTF_FLT) -DEF_TP(DOUBLE ,"double" , TYP_DOUBLE, TI_DOUBLE,8, 8, 8, 2, 8, VTR_FLOAT, availableDoubleRegs, VTF_FLT) +DEF_TP(FLOAT ,"float" , TYP_FLOAT, 4, 4, 4, 1, 4, VTR_FLOAT, availableFloatRegs, VTF_FLT) +DEF_TP(DOUBLE ,"double" , TYP_DOUBLE, 8, 8, 8, 2, 8, VTR_FLOAT, availableDoubleRegs, VTF_FLT) -DEF_TP(REF ,"ref" , TYP_REF, TI_REF, PS,GCS,GCS, PST,PS, VTR_INT, availableIntRegs, VTF_ANY|VTF_GCR|VTF_I) -DEF_TP(BYREF ,"byref" , TYP_BYREF, TI_ERROR,PS,BRS,BRS, PST,PS, VTR_INT, availableIntRegs, VTF_ANY|VTF_BYR|VTF_I) -DEF_TP(STRUCT ,"struct" , TYP_STRUCT, TI_STRUCT,0, 0, 0, 1, 4, VTR_INT, availableIntRegs, VTF_S) +DEF_TP(REF ,"ref" , TYP_REF, PS,GCS,GCS, PST,PS, VTR_INT, availableIntRegs, VTF_ANY|VTF_GCR|VTF_I) +DEF_TP(BYREF ,"byref" , TYP_BYREF, PS,BRS,BRS, PST,PS, VTR_INT, availableIntRegs, VTF_ANY|VTF_BYR|VTF_I) +DEF_TP(STRUCT ,"struct" , TYP_STRUCT, 0, 0, 0, 1, 4, VTR_INT, availableIntRegs, VTF_S) #ifdef FEATURE_SIMD -DEF_TP(SIMD8 ,"simd8" , TYP_SIMD8, TI_STRUCT, 8, 8, 8, 2, 8, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) -DEF_TP(SIMD12 ,"simd12" , TYP_SIMD12, TI_STRUCT,12,16, 16, 4,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) -DEF_TP(SIMD16 ,"simd16" , TYP_SIMD16, TI_STRUCT,16,16, 16, 4,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) +DEF_TP(SIMD8 ,"simd8" , TYP_SIMD8, 8, 8, 8, 2, 8, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) +DEF_TP(SIMD12 ,"simd12" , TYP_SIMD12, 12,16, 16, 4,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) +DEF_TP(SIMD16 ,"simd16" , TYP_SIMD16, 16,16, 16, 4,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) #if defined(TARGET_XARCH) -DEF_TP(SIMD32 ,"simd32" , TYP_SIMD32, TI_STRUCT,32,32, 32, 8,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) -DEF_TP(SIMD64 ,"simd64" , TYP_SIMD64, TI_STRUCT,64,64, 64, 16,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) -DEF_TP(MASK ,"mask" , TYP_MASK, TI_STRUCT, 8, 8, 8, 2, 8, VTR_MASK, availableMaskRegs, VTF_ANY) +DEF_TP(SIMD32 ,"simd32" , TYP_SIMD32, 32,32, 32, 8,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) +DEF_TP(SIMD64 ,"simd64" , TYP_SIMD64, 64,64, 64, 16,16, VTR_FLOAT, availableDoubleRegs, VTF_S|VTF_VEC) +DEF_TP(MASK ,"mask" , TYP_MASK, 8, 8, 8, 2, 8, VTR_MASK, availableMaskRegs, VTF_ANY) #endif // TARGET_XARCH #endif // FEATURE_SIMD -DEF_TP(UNKNOWN ,"unknown" ,TYP_UNKNOWN, TI_ERROR, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) +DEF_TP(UNKNOWN ,"unknown" ,TYP_UNKNOWN, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, VTF_ANY) // clang-format on #undef GCS diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index fb8266240ad74..27e5f14ac3fc0 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -79,13 +79,13 @@ const signed char opcodeSizes[] = // clang-format on const BYTE varTypeClassification[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) tf, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) tf, #include "typelist.h" #undef DEF_TP }; const BYTE varTypeRegister[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) regTyp, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) regTyp, #include "typelist.h" #undef DEF_TP }; @@ -111,7 +111,7 @@ extern const BYTE opcodeArgKinds[] = { const char* varTypeName(var_types vt) { static const char* const varTypeNames[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) nm, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) nm, #include "typelist.h" #undef DEF_TP }; diff --git a/src/coreclr/jit/vartypesdef.h b/src/coreclr/jit/vartypesdef.h index 8f088da425190..dc27ac3adb14b 100644 --- a/src/coreclr/jit/vartypesdef.h +++ b/src/coreclr/jit/vartypesdef.h @@ -8,7 +8,7 @@ enum var_types : BYTE { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, regTyp, regFld, tf) TYP_##tn, +#define DEF_TP(tn, nm, jitType, sz, sze, asze, st, al, regTyp, regFld, tf) TYP_##tn, #include "typelist.h" #undef DEF_TP TYP_COUNT From ce9ceb62cf6ac1681b503cb04bd82dc7d29ab9b2 Mon Sep 17 00:00:00 2001 From: David Mason Date: Wed, 17 May 2023 13:23:27 -0700 Subject: [PATCH 12/12] Add IPC commands to enable and disable PerfMaps/JitDumps (#85801) --- src/coreclr/inc/CrstTypes.def | 6 +- src/coreclr/inc/crsttypes_generated.h | 87 +++--- .../nativeaot/Runtime/eventpipe/ds-rt-aot.h | 14 + src/coreclr/pal/inc/pal.h | 5 + src/coreclr/pal/src/misc/perfjitdump.cpp | 64 ++--- src/coreclr/vm/ceemain.cpp | 2 +- .../vm/eventing/eventpipe/ds-rt-coreclr.h | 37 +++ src/coreclr/vm/perfmap.cpp | 250 ++++++++++++------ src/coreclr/vm/perfmap.h | 38 ++- src/mono/mono/eventpipe/ds-rt-mono.h | 14 + src/native/eventpipe/ds-process-protocol.c | 133 ++++++++++ src/native/eventpipe/ds-process-protocol.h | 26 ++ src/native/eventpipe/ds-rt.h | 8 + src/native/eventpipe/ds-types.h | 5 +- 14 files changed, 518 insertions(+), 171 deletions(-) diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index ea5dd4636ec00..9c92df1205d9e 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -402,7 +402,7 @@ End // Used to synchronize all rejit information stored in a given AppDomain. Crst CodeVersioning - AcquiredBefore LoaderHeap SingleUseLock DeadlockDetection JumpStubCache DebuggerController FuncPtrStubs + AcquiredBefore LoaderHeap SingleUseLock DeadlockDetection JumpStubCache DebuggerController FuncPtrStubs PerfMap AcquiredAfter ReJITGlobalRequest ThreadStore GlobalStrLiteralMap SystemDomain DebuggerMutex MethodDescBackpatchInfoTracker ReadyToRunEntryPointToMethodDescMap ClassInit AppDomainCache TypeIDMap FusionAppCtx COMWrapperCache InteropData End @@ -581,3 +581,7 @@ End Crst StaticBoxInit AcquiredBefore LoaderHeap FrozenObjectHeap AssemblyLoader End + +Crst PerfMap + AcquiredAfter CodeVersioning AssemblyList +End diff --git a/src/coreclr/inc/crsttypes_generated.h b/src/coreclr/inc/crsttypes_generated.h index 3e9b989cd2d1e..630827961df30 100644 --- a/src/coreclr/inc/crsttypes_generated.h +++ b/src/coreclr/inc/crsttypes_generated.h @@ -94,47 +94,48 @@ enum CrstType CrstObjectList = 76, CrstPEImage = 77, CrstPendingTypeLoadEntry = 78, - CrstPgoData = 79, - CrstPinnedByrefValidation = 80, - CrstPinnedHeapHandleTable = 81, - CrstProfilerGCRefDataFreeList = 82, - CrstProfilingAPIStatus = 83, - CrstRCWCache = 84, - CrstRCWCleanupList = 85, - CrstReadyToRunEntryPointToMethodDescMap = 86, - CrstReflection = 87, - CrstReJITGlobalRequest = 88, - CrstRetThunkCache = 89, - CrstSavedExceptionInfo = 90, - CrstSaveModuleProfileData = 91, - CrstSecurityStackwalkCache = 92, - CrstSigConvert = 93, - CrstSingleUseLock = 94, - CrstSpecialStatics = 95, - CrstStackSampler = 96, - CrstStaticBoxInit = 97, - CrstStressLog = 98, - CrstStubCache = 99, - CrstStubDispatchCache = 100, - CrstStubUnwindInfoHeapSegments = 101, - CrstSyncBlockCache = 102, - CrstSyncHashLock = 103, - CrstSystemBaseDomain = 104, - CrstSystemDomain = 105, - CrstSystemDomainDelayedUnloadList = 106, - CrstThreadIdDispenser = 107, - CrstThreadStore = 108, - CrstTieredCompilation = 109, - CrstTypeEquivalenceMap = 110, - CrstTypeIDMap = 111, - CrstUMEntryThunkCache = 112, - CrstUMEntryThunkFreeListLock = 113, - CrstUniqueStack = 114, - CrstUnresolvedClassLock = 115, - CrstUnwindInfoTableLock = 116, - CrstVSDIndirectionCellLock = 117, - CrstWrapperTemplate = 118, - kNumberOfCrstTypes = 119 + CrstPerfMap = 79, + CrstPgoData = 80, + CrstPinnedByrefValidation = 81, + CrstPinnedHeapHandleTable = 82, + CrstProfilerGCRefDataFreeList = 83, + CrstProfilingAPIStatus = 84, + CrstRCWCache = 85, + CrstRCWCleanupList = 86, + CrstReadyToRunEntryPointToMethodDescMap = 87, + CrstReflection = 88, + CrstReJITGlobalRequest = 89, + CrstRetThunkCache = 90, + CrstSavedExceptionInfo = 91, + CrstSaveModuleProfileData = 92, + CrstSecurityStackwalkCache = 93, + CrstSigConvert = 94, + CrstSingleUseLock = 95, + CrstSpecialStatics = 96, + CrstStackSampler = 97, + CrstStaticBoxInit = 98, + CrstStressLog = 99, + CrstStubCache = 100, + CrstStubDispatchCache = 101, + CrstStubUnwindInfoHeapSegments = 102, + CrstSyncBlockCache = 103, + CrstSyncHashLock = 104, + CrstSystemBaseDomain = 105, + CrstSystemDomain = 106, + CrstSystemDomainDelayedUnloadList = 107, + CrstThreadIdDispenser = 108, + CrstThreadStore = 109, + CrstTieredCompilation = 110, + CrstTypeEquivalenceMap = 111, + CrstTypeIDMap = 112, + CrstUMEntryThunkCache = 113, + CrstUMEntryThunkFreeListLock = 114, + CrstUniqueStack = 115, + CrstUnresolvedClassLock = 116, + CrstUnwindInfoTableLock = 117, + CrstVSDIndirectionCellLock = 118, + CrstWrapperTemplate = 119, + kNumberOfCrstTypes = 120 }; #endif // __CRST_TYPES_INCLUDED @@ -147,7 +148,7 @@ int g_rgCrstLevelMap[] = { 10, // CrstAppDomainCache 3, // CrstArgBasedStubCache - 0, // CrstAssemblyList + 3, // CrstAssemblyList 12, // CrstAssemblyLoader 4, // CrstAvailableClass 5, // CrstAvailableParamTypes @@ -224,6 +225,7 @@ int g_rgCrstLevelMap[] = 2, // CrstObjectList 5, // CrstPEImage 19, // CrstPendingTypeLoadEntry + 0, // CrstPerfMap 4, // CrstPgoData 0, // CrstPinnedByrefValidation 14, // CrstPinnedHeapHandleTable @@ -348,6 +350,7 @@ LPCSTR g_rgCrstNameMap[] = "CrstObjectList", "CrstPEImage", "CrstPendingTypeLoadEntry", + "CrstPerfMap", "CrstPgoData", "CrstPinnedByrefValidation", "CrstPinnedHeapHandleTable", diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index def48ebba590c..df255f7186e19 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -258,6 +258,20 @@ ds_rt_set_environment_variable (const ep_char16_t *name, const ep_char16_t *valu return 0xffff; } +static +uint32_t +ds_rt_enable_perfmap (uint32_t type) +{ + return DS_IPC_E_NOTSUPPORTED; +} + +static +uint32_t +ds_rt_disable_perfmap (void) +{ + return DS_IPC_E_NOTSUPPORTED; +} + /* * DiagnosticServer. */ diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 46c8b4c662c11..8d1bcf9ee9b2a 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -544,6 +544,11 @@ PALAPI // Start the jitdump file PAL_PerfJitDump_Start(const char* path); +PALIMPORT +bool +PALAPI +PAL_PerfJitDump_IsStarted(); + PALIMPORT int PALAPI diff --git a/src/coreclr/pal/src/misc/perfjitdump.cpp b/src/coreclr/pal/src/misc/perfjitdump.cpp index 043939ad2da28..7c7601ea45c83 100644 --- a/src/coreclr/pal/src/misc/perfjitdump.cpp +++ b/src/coreclr/pal/src/misc/perfjitdump.cpp @@ -139,17 +139,15 @@ struct PerfJitDumpState enabled(false), fd(-1), mmapAddr(MAP_FAILED), - mutex(PTHREAD_MUTEX_INITIALIZER), codeIndex(0) {} volatile bool enabled; int fd; void *mmapAddr; - pthread_mutex_t mutex; volatile uint64_t codeIndex; - int FatalError(bool locked) + int FatalError() { enabled = false; @@ -165,11 +163,6 @@ struct PerfJitDumpState fd = -1; } - if (locked) - { - pthread_mutex_unlock(&mutex); - } - return -1; } @@ -180,10 +173,8 @@ struct PerfJitDumpState // Write file header FileHeader header; - result = pthread_mutex_lock(&mutex); - if (result != 0) - return FatalError(false); + return FatalError(); if (enabled) goto exit; @@ -193,39 +184,37 @@ struct PerfJitDumpState result = snprintf(jitdumpPath, sizeof(jitdumpPath), "%s/jit-%i.dump", path, getpid()); if (result >= PATH_MAX) - return FatalError(true); + return FatalError(); result = open(jitdumpPath, O_CREAT|O_TRUNC|O_RDWR|O_CLOEXEC, S_IRUSR|S_IWUSR ); if (result == -1) - return FatalError(true); + return FatalError(); fd = result; result = write(fd, &header, sizeof(FileHeader)); if (result == -1) - return FatalError(true); + return FatalError(); result = fsync(fd); if (result == -1) - return FatalError(true); + return FatalError(); // mmap jitdump file // this is a marker for perf inject to find the jitdumpfile mmapAddr = mmap(nullptr, sizeof(FileHeader), PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); if (mmapAddr == MAP_FAILED) - return FatalError(true); + return FatalError(); enabled = true; exit: - result = pthread_mutex_unlock(&mutex); - if (result != 0) - return FatalError(false); + return FatalError(); return 0; } @@ -258,10 +247,8 @@ struct PerfJitDumpState size_t itemsWritten = 0; - result = pthread_mutex_lock(&mutex); - if (result != 0) - return FatalError(false); + return FatalError(); if (!enabled) goto exit; @@ -281,7 +268,7 @@ struct PerfJitDumpState if (errno == EINTR) continue; - return FatalError(true); + return FatalError(); } // Detect unexpected failure cases. @@ -312,10 +299,8 @@ struct PerfJitDumpState } while (true); exit: - result = pthread_mutex_unlock(&mutex); - if (result != 0) - return FatalError(false); + return FatalError(); } return 0; } @@ -328,11 +313,8 @@ struct PerfJitDumpState { enabled = false; - // Lock the mutex - result = pthread_mutex_lock(&mutex); - if (result != 0) - return FatalError(false); + return FatalError(); if (!enabled) goto exit; @@ -340,24 +322,22 @@ struct PerfJitDumpState result = munmap(mmapAddr, sizeof(FileHeader)); if (result == -1) - return FatalError(true); + return FatalError(); mmapAddr = MAP_FAILED; result = fsync(fd); if (result == -1) - return FatalError(true); + return FatalError(); result = close(fd); if (result == -1) - return FatalError(true); + return FatalError(); fd = -1; exit: - result = pthread_mutex_unlock(&mutex); - if (result != 0) return -1; } @@ -380,6 +360,13 @@ PAL_PerfJitDump_Start(const char* path) return GetState().Start(path); } +bool +PALAPI +PAL_PerfJitDump_IsStarted() +{ + return GetState().enabled; +} + int PALAPI PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo) @@ -403,6 +390,13 @@ PAL_PerfJitDump_Start(const char* path) return 0; } +bool +PALAPI +PAL_PerfJitDump_IsStarted() +{ + return false; +} + int PALAPI PAL_PerfJitDump_LogMethod(void* pCode, size_t codeSize, const char* symbol, void* debugInfo, void* unwindInfo) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 668f0183ba6c5..162c0aa7bf9cc 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -1242,7 +1242,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) #ifdef FEATURE_PERFMAP // Flush and close the perf map file. - PerfMap::Destroy(); + PerfMap::Disable(); #endif ceeInf.JitProcessShutdownWork(); // Do anything JIT-related that needs to happen at shutdown. diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index cf779c1f9c989..18d2ce62bdfc6 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -13,6 +13,9 @@ #include #include #include +#ifdef FEATURE_PERFMAP +#include "perfmap.h" +#endif #undef DS_LOG_ALWAYS_0 #define DS_LOG_ALWAYS_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg "\n") @@ -292,6 +295,40 @@ ds_rt_set_environment_variable (const ep_char16_t *name, const ep_char16_t *valu return SetEnvironmentVariableW(reinterpret_cast(name), reinterpret_cast(value)) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); } +static +uint32_t +ds_rt_enable_perfmap (uint32_t type) +{ + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_PERFMAP + PerfMap::PerfMapType perfMapType = (PerfMap::PerfMapType)type; + if (perfMapType == PerfMap::PerfMapType::DISABLED || perfMapType > PerfMap::PerfMapType::JITDUMP) + { + return DS_IPC_E_INVALIDARG; + } + + PerfMap::Enable(perfMapType, true); + + return DS_IPC_S_OK; +#else // FEATURE_PERFMAP + return DS_IPC_E_NOTSUPPORTED; +#endif // FEATURE_PERFMAP +} + +static +uint32_t +ds_rt_disable_perfmap (void) +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_PERFMAP + PerfMap::Disable(); + return DS_IPC_S_OK; +#else // FEATURE_PERFMAP + return DS_IPC_E_NOTSUPPORTED; +#endif // FEATURE_PERFMAP +} + /* * DiagnosticServer. */ diff --git a/src/coreclr/vm/perfmap.cpp b/src/coreclr/vm/perfmap.cpp index 89e58fb5adcc3..d4828cd80dc48 100644 --- a/src/coreclr/vm/perfmap.cpp +++ b/src/coreclr/vm/perfmap.cpp @@ -23,92 +23,193 @@ Volatile PerfMap::s_enabled = false; PerfMap * PerfMap::s_Current = nullptr; bool PerfMap::s_ShowOptimizationTiers = false; unsigned PerfMap::s_StubsMapped = 0; - -enum -{ - DISABLED, - ALL, - JITDUMP, - PERFMAP -}; +CrstStatic PerfMap::s_csPerfMap; // Initialize the map for the process - called from EEStartupHelper. void PerfMap::Initialize() { LIMITED_METHOD_CONTRACT; - // Only enable the map if requested. - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled) == ALL || CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled) == PERFMAP) - { - // Get the current process id. - int currentPid = GetCurrentProcessId(); + s_csPerfMap.Init(CrstPerfMap); - // Create the map. - s_Current = new PerfMap(currentPid); + PerfMapType perfMapType = (PerfMapType)CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled); + PerfMap::Enable(perfMapType, false); +} + +void PerfMap::Enable(PerfMapType type, bool sendExisting) +{ + LIMITED_METHOD_CONTRACT; - int signalNum = (int) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapIgnoreSignal); + if (type == PerfMapType::DISABLED) + { + return; + } - if (signalNum > 0) - { - PAL_IgnoreProfileSignal(signalNum); - } + { + CrstHolder ch(&(s_csPerfMap)); - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapShowOptimizationTiers) != 0) + if (s_Current == nullptr && (type == PerfMapType::ALL || type == PerfMapType::PERFMAP)) { - s_ShowOptimizationTiers = true; + s_Current = new PerfMap(); + int signalNum = (int) CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapIgnoreSignal); + + if (signalNum > 0) + { + PAL_IgnoreProfileSignal(signalNum); + } + + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapShowOptimizationTiers) != 0) + { + s_ShowOptimizationTiers = true; + } + + int currentPid = GetCurrentProcessId(); + s_Current->OpenFileForPid(currentPid); + s_enabled = true; } - s_enabled = true; - } + if (!PAL_PerfJitDump_IsStarted() && (type == PerfMapType::ALL || type == PerfMapType::JITDUMP)) + { + const char* jitdumpPath; + char jitdumpPathBuffer[4096]; - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled) == ALL || CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapEnabled) == JITDUMP) - { - const char* jitdumpPath; - char jitdumpPathBuffer[4096]; + CLRConfigNoCache value = CLRConfigNoCache::Get("PerfMapJitDumpPath"); + if (value.IsSet()) + { + jitdumpPath = value.AsString(); + } + else + { + GetTempPathA(sizeof(jitdumpPathBuffer) - 1, jitdumpPathBuffer); + jitdumpPath = jitdumpPathBuffer; + } - CLRConfigNoCache value = CLRConfigNoCache::Get("PerfMapJitDumpPath"); - if (value.IsSet()) - { - jitdumpPath = value.AsString(); + PAL_PerfJitDump_Start(jitdumpPath); + + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapShowOptimizationTiers) != 0) + { + s_ShowOptimizationTiers = true; + } + + s_enabled = true; } - else + } + + if (sendExisting) + { + AppDomain::AssemblyIterator assemblyIterator = GetAppDomain()->IterateAssembliesEx( + (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution)); + CollectibleAssemblyHolder pDomainAssembly; + while (assemblyIterator.Next(pDomainAssembly.This())) { - GetTempPathA(sizeof(jitdumpPathBuffer) - 1, jitdumpPathBuffer); - jitdumpPath = jitdumpPathBuffer; - } + CollectibleAssemblyHolder pAssembly = pDomainAssembly->GetAssembly(); + PerfMap::LogImageLoad(pAssembly->GetPEAssembly()); - PAL_PerfJitDump_Start(jitdumpPath); + // PerfMap does not log R2R methods so only proceed if we are emitting jitdumps + if (type == PerfMapType::ALL || type == PerfMapType::JITDUMP) + { + Module *pModule = pAssembly->GetModule(); + if (pModule->IsReadyToRun()) + { + ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo()); + + while (mi.Next()) + { + // Call GetMethodDesc_NoRestore instead of GetMethodDesc to avoid restoring methods. + MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc_NoRestore(); + if (hotDesc != nullptr && hotDesc->GetNativeCode() != NULL) + { + PerfMap::LogPreCompiledMethod(hotDesc, hotDesc->GetNativeCode()); + } + } + } + } + } - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerfMapShowOptimizationTiers) != 0) { - s_ShowOptimizationTiers = true; + CodeVersionManager::LockHolder codeVersioningLockHolder; + + EEJitManager::CodeHeapIterator heapIterator(nullptr); + while (heapIterator.Next()) + { + MethodDesc * pMethod = heapIterator.GetMethod(); + if (pMethod == nullptr) + { + continue; + } + + PCODE codeStart = PINSTRToPCODE(heapIterator.GetMethodCode()); + NativeCodeVersion nativeCodeVersion; +#ifdef FEATURE_CODE_VERSIONING + nativeCodeVersion = pMethod->GetCodeVersionManager()->GetNativeCodeVersion(pMethod, codeStart);; + if (nativeCodeVersion.IsNull() && codeStart != pMethod->GetNativeCode()) + { + continue; + } +#else // FEATURE_CODE_VERSIONING + if (codeStart != pMethod->GetNativeCode()) + { + continue; + } +#endif // FEATURE_CODE_VERSIONING + + EECodeInfo codeInfo(codeStart); + IJitManager::MethodRegionInfo methodRegionInfo; + codeInfo.GetMethodRegionInfo(&methodRegionInfo); + _ASSERTE(methodRegionInfo.hotStartAddress == codeStart); + + PrepareCodeConfig config(!nativeCodeVersion.IsNull() ? nativeCodeVersion : NativeCodeVersion(pMethod), FALSE, FALSE); + PerfMap::LogJITCompiledMethod(pMethod, codeStart, methodRegionInfo.hotSize, &config); + } } - - s_enabled = true; } } -// Destroy the map for the process - called from EEShutdownHelper. -void PerfMap::Destroy() +// Disable the map for the process - called from EEShutdownHelper. +void PerfMap::Disable() { LIMITED_METHOD_CONTRACT; if (s_enabled) { + CrstHolder ch(&(s_csPerfMap)); + s_enabled = false; + if (s_Current != nullptr) + { + delete s_Current; + s_Current = nullptr; + } + // PAL_PerfJitDump_Finish is lock protected and can safely be called multiple times PAL_PerfJitDump_Finish(); } } // Construct a new map for the process. -PerfMap::PerfMap(int pid) +PerfMap::PerfMap() { LIMITED_METHOD_CONTRACT; // Initialize with no failures. m_ErrorEncountered = false; + m_PerfInfo = nullptr; +} + +// Clean-up resources. +PerfMap::~PerfMap() +{ + LIMITED_METHOD_CONTRACT; + + delete m_FileStream; + m_FileStream = nullptr; + + delete m_PerfInfo; + m_PerfInfo = nullptr; +} +void PerfMap::OpenFileForPid(int pid) +{ // Build the path to the map file on disk. WCHAR tempPath[MAX_LONGPATH+1]; if(!GetTempPathW(MAX_LONGPATH, tempPath)) @@ -126,32 +227,6 @@ PerfMap::PerfMap(int pid) m_PerfInfo = new PerfInfo(pid); } -// Construct a new map without a specified file name. -// Used for offline creation of NGEN map files. -PerfMap::PerfMap() - : m_FileStream(nullptr) - , m_PerfInfo(nullptr) -{ - LIMITED_METHOD_CONTRACT; - - // Initialize with no failures. - m_ErrorEncountered = false; - - s_StubsMapped = 0; -} - -// Clean-up resources. -PerfMap::~PerfMap() -{ - LIMITED_METHOD_CONTRACT; - - delete m_FileStream; - m_FileStream = nullptr; - - delete m_PerfInfo; - m_PerfInfo = nullptr; -} - // Open the specified destination map file. void PerfMap::OpenFile(SString& path) { @@ -174,6 +249,9 @@ void PerfMap::OpenFile(SString& path) void PerfMap::WriteLine(SString& line) { STANDARD_VM_CONTRACT; +#ifdef _DEBUG + _ASSERTE(s_csPerfMap.OwnedByCurrentThread()); +#endif if (m_FileStream == nullptr || m_ErrorEncountered) { @@ -202,6 +280,8 @@ void PerfMap::WriteLine(SString& line) void PerfMap::LogImageLoad(PEAssembly * pPEAssembly) { + CrstHolder ch(&(s_csPerfMap)); + if (s_enabled && s_Current != nullptr) { s_Current->LogImage(pPEAssembly); @@ -235,8 +315,6 @@ void PerfMap::LogImage(PEAssembly * pPEAssembly) EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); } - -// Log a method to the map. void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig) { LIMITED_METHOD_CONTRACT; @@ -276,12 +354,16 @@ void PerfMap::LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t cod SString line; line.Printf(FMT_CODE_ADDR " %x %s\n", pCode, codeSize, name.GetUTF8()); - // Write the line. - if(s_Current != nullptr) { - s_Current->WriteLine(line); + CrstHolder ch(&(s_csPerfMap)); + + if(s_Current != nullptr) + { + s_Current->WriteLine(line); + } + + PAL_PerfJitDump_LogMethod((void*)pCode, codeSize, name.GetUTF8(), nullptr, nullptr); } - PAL_PerfJitDump_LogMethod((void*)pCode, codeSize, name.GetUTF8(), nullptr, nullptr); } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); @@ -320,16 +402,20 @@ void PerfMap::LogPreCompiledMethod(MethodDesc * pMethod, PCODE pCode) // Emit an entry for each section if it is used. if (methodRegionInfo.hotSize > 0) { + CrstHolder ch(&(s_csPerfMap)); PAL_PerfJitDump_LogMethod((void*)methodRegionInfo.hotStartAddress, methodRegionInfo.hotSize, name.GetUTF8(), nullptr, nullptr); } if (methodRegionInfo.coldSize > 0) { + CrstHolder ch(&(s_csPerfMap)); + if (s_ShowOptimizationTiers) { pMethod->GetFullMethodInfo(name); name.Append(W("[PreJit-cold]")); } + PAL_PerfJitDump_LogMethod((void*)methodRegionInfo.coldStartAddress, methodRegionInfo.coldSize, name.GetUTF8(), nullptr, nullptr); } } @@ -364,12 +450,16 @@ void PerfMap::LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, SString line; line.Printf(FMT_CODE_ADDR " %x %s\n", pCode, codeSize, name.GetUTF8()); - // Write the line. - if(s_Current != nullptr) { - s_Current->WriteLine(line); + CrstHolder ch(&(s_csPerfMap)); + + if(s_Current != nullptr) + { + s_Current->WriteLine(line); + } + + PAL_PerfJitDump_LogMethod((void*)pCode, codeSize, name.GetUTF8(), nullptr, nullptr); } - PAL_PerfJitDump_LogMethod((void*)pCode, codeSize, name.GetUTF8(), nullptr, nullptr); } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions); } diff --git a/src/coreclr/vm/perfmap.h b/src/coreclr/vm/perfmap.h index 11cfdd00a5f12..393f302cefff0 100644 --- a/src/coreclr/vm/perfmap.h +++ b/src/coreclr/vm/perfmap.h @@ -27,29 +27,26 @@ class PerfMap // Set to true if an error is encountered when writing to the file. static unsigned s_StubsMapped; + static CrstStatic s_csPerfMap; + // The file stream to write the map to. CFileStream * m_FileStream; // The perfinfo file to log images to. - PerfInfo* m_PerfInfo; + PerfInfo * m_PerfInfo; // Set to true if an error is encountered when writing to the file. bool m_ErrorEncountered; // Construct a new map for the specified pid. - PerfMap(int pid); + PerfMap(); + + void OpenFileForPid(int pid); // Write a line to the map file. void WriteLine(SString & line); protected: - // Construct a new map without a specified file name. - // Used for offline creation of NGEN map files. - PerfMap(); - - // Clean-up resources. - ~PerfMap(); - // Open the perf map file for write. void OpenFile(SString& path); @@ -63,13 +60,32 @@ class PerfMap static void GetNativeImageSignature(PEAssembly * pPEAssembly, CHAR * pszSig, unsigned int nSigSize); public: + // Clean-up resources. + ~PerfMap(); + + enum class PerfMapType + { + DISABLED = 0, + ALL = 1, + JITDUMP = 2, + PERFMAP = 3 + }; + + static bool IsEnabled() + { + LIMITED_METHOD_CONTRACT; + + return s_enabled; + } + // Initialize the map for the current process. static void Initialize(); + static void Enable(PerfMapType type, bool sendExisting); + // Log a native image load to the map. static void LogImageLoad(PEAssembly * pPEAssembly); - // Log a JIT compiled method to the map. static void LogJITCompiledMethod(MethodDesc * pMethod, PCODE pCode, size_t codeSize, PrepareCodeConfig *pConfig); // Log a pre-compiled method to the map. @@ -79,6 +95,6 @@ class PerfMap static void LogStubs(const char* stubType, const char* stubOwner, PCODE pCode, size_t codeSize); // Close the map and flush any remaining data. - static void Destroy(); + static void Disable(); }; #endif // PERFPID_H diff --git a/src/mono/mono/eventpipe/ds-rt-mono.h b/src/mono/mono/eventpipe/ds-rt-mono.h index 3c2c8e0fed8b9..3eea7f1ebe04c 100644 --- a/src/mono/mono/eventpipe/ds-rt-mono.h +++ b/src/mono/mono/eventpipe/ds-rt-mono.h @@ -225,6 +225,20 @@ ds_rt_set_environment_variable (const ep_char16_t *name, const ep_char16_t *valu return success ? DS_IPC_S_OK : DS_IPC_E_FAIL; } +static +uint32_t +ds_rt_enable_perfmap (uint32_t type) +{ + return DS_IPC_E_NOTSUPPORTED; +} + +static +uint32_t +ds_rt_disable_perfmap (void) +{ + return DS_IPC_E_NOTSUPPORTED; +} + /* * DiagnosticServer. */ diff --git a/src/native/eventpipe/ds-process-protocol.c b/src/native/eventpipe/ds-process-protocol.c index c395743d4f8e8..a40403a03f789 100644 --- a/src/native/eventpipe/ds-process-protocol.c +++ b/src/native/eventpipe/ds-process-protocol.c @@ -77,6 +77,18 @@ process_protocol_helper_set_environment_variable ( DiagnosticsIpcMessage *message, DiagnosticsIpcStream *stream); +static +bool +process_protocol_helper_enable_perfmap ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + +static +bool +process_protocol_helper_disable_perfmap ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream); + static bool process_protocol_helper_unknown_command ( @@ -745,6 +757,121 @@ process_protocol_helper_set_environment_variable ( ep_exit_error_handler (); } +DiagnosticsEnablePerfmapPayload * +ds_enable_perfmap_payload_alloc (void) +{ + return ep_rt_object_alloc (DiagnosticsEnablePerfmapPayload); +} + +void +ds_enable_perfmap_payload_free (DiagnosticsEnablePerfmapPayload *payload) +{ + ep_return_void_if_nok (payload != NULL); + ep_rt_byte_array_free (payload->incoming_buffer); + ep_rt_object_free (payload); +} + +static +uint8_t * +enable_perfmap_command_try_parse_payload ( + uint8_t *buffer, + uint16_t buffer_len) +{ + EP_ASSERT (buffer != NULL); + + uint8_t * buffer_cursor = buffer; + uint32_t buffer_cursor_len = buffer_len; + + DiagnosticsEnablePerfmapPayload *instance = ds_enable_perfmap_payload_alloc (); + ep_raise_error_if_nok (instance != NULL); + + instance->incoming_buffer = buffer; + + if (!ds_ipc_message_try_parse_uint32_t (&buffer_cursor, &buffer_cursor_len, &instance->perfMapType)) + ep_raise_error (); + +ep_on_exit: + return (uint8_t *)instance; + +ep_on_error: + ds_enable_perfmap_payload_free (instance); + instance = NULL; + ep_exit_error_handler (); +} + +static +bool +process_protocol_helper_enable_perfmap ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + if (!stream) + return false; + + bool result = false; + DiagnosticsEnablePerfmapPayload *payload = (DiagnosticsEnablePerfmapPayload *)ds_ipc_message_try_parse_payload (message, enable_perfmap_command_try_parse_payload); + if (!payload) { + ds_ipc_message_send_error (stream, DS_IPC_E_BAD_ENCODING); + ep_raise_error (); + } + + ds_ipc_result_t ipc_result; + ipc_result = ds_rt_enable_perfmap (payload->perfMapType); + if (ipc_result != DS_IPC_S_OK) { + ds_ipc_message_send_error (stream, ipc_result); + ep_raise_error (); + } else { + ds_ipc_message_send_success (stream, ipc_result); + } + + result = true; + +ep_on_exit: + ds_enable_perfmap_payload_free (payload); + ds_ipc_stream_free (stream); + return result; + +ep_on_error: + EP_ASSERT (!result); + ep_exit_error_handler (); +} + +static +bool +process_protocol_helper_disable_perfmap ( + DiagnosticsIpcMessage *message, + DiagnosticsIpcStream *stream) +{ + EP_ASSERT (message != NULL); + EP_ASSERT (stream != NULL); + + if (!stream) + return false; + + bool result = false; + ds_ipc_result_t ipc_result; + ipc_result = ds_rt_disable_perfmap (); + if (ipc_result != DS_IPC_S_OK) { + ds_ipc_message_send_error (stream, ipc_result); + ep_raise_error (); + } else { + ds_ipc_message_send_success (stream, ipc_result); + } + + result = true; + +ep_on_exit: + ds_ipc_stream_free (stream); + return result; + +ep_on_error: + EP_ASSERT (!result); + ep_exit_error_handler (); +} + static bool process_protocol_helper_unknown_command ( @@ -783,6 +910,12 @@ ds_process_protocol_helper_handle_ipc_message ( case DS_PROCESS_COMMANDID_GET_PROCESS_INFO_2: result = process_protocol_helper_get_process_info_2 (message, stream); break; + case DS_PROCESS_COMMANDID_ENABLE_PERFMAP: + result = process_protocol_helper_enable_perfmap (message, stream); + break; + case DS_PROCESS_COMMANDID_DISABLE_PERFMAP: + result = process_protocol_helper_disable_perfmap (message, stream); + break; default: result = process_protocol_helper_unknown_command (message, stream); break; diff --git a/src/native/eventpipe/ds-process-protocol.h b/src/native/eventpipe/ds-process-protocol.h index db43e7fea97b7..04430bbb72b85 100644 --- a/src/native/eventpipe/ds-process-protocol.h +++ b/src/native/eventpipe/ds-process-protocol.h @@ -164,6 +164,32 @@ ds_set_environment_variable_payload_alloc (void); void ds_set_environment_variable_payload_free (DiagnosticsSetEnvironmentVariablePayload *payload); +/* +* DiagnosticsEnablePerfmapPayload +*/ + +#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsEnablePerfmapPayload { +#else +struct _DiagnosticsEnablePerfmapPayload_Internal { +#endif + uint8_t * incoming_buffer; + + uint32_t perfMapType; +}; + +#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_PROCESS_PROTOCOL_GETTER_SETTER) +struct _DiagnosticsEnablePerfmapPayload { + uint8_t _internal [sizeof (struct _DiagnosticsEnablePerfmapPayload_Internal)]; +}; +#endif + +DiagnosticsEnablePerfmapPayload * +ds_enable_perfmap_payload_alloc (void); + +void +ds_enable_perfmap_payload_free (DiagnosticsEnablePerfmapPayload *payload); + /* * DiagnosticsProcessProtocolHelper. */ diff --git a/src/native/eventpipe/ds-rt.h b/src/native/eventpipe/ds-rt.h index ad3034619067a..f4a54b3d52df5 100644 --- a/src/native/eventpipe/ds-rt.h +++ b/src/native/eventpipe/ds-rt.h @@ -110,6 +110,14 @@ ds_rt_get_environment_variable (const ep_char16_t *name, uint32_t *valueLengthOut, ep_char16_t *valueBuffer); +static +uint32_t +ds_rt_enable_perfmap (uint32_t type); + +static +uint32_t +ds_rt_disable_perfmap (void); + /* * DiagnosticServer. */ diff --git a/src/native/eventpipe/ds-types.h b/src/native/eventpipe/ds-types.h index 0787f2032053e..85363f499e404 100644 --- a/src/native/eventpipe/ds-types.h +++ b/src/native/eventpipe/ds-types.h @@ -23,6 +23,7 @@ typedef struct _DiagnosticsGenerateCoreDumpCommandPayload DiagnosticsGenerateCor typedef struct _DiagnosticsGenerateCoreDumpResponsePayload DiagnosticsGenerateCoreDumpResponsePayload; typedef struct _DiagnosticsSetEnvironmentVariablePayload DiagnosticsSetEnvironmentVariablePayload; typedef struct _DiagnosticsGetEnvironmentVariablePayload DiagnosticsGetEnvironmentVariablePayload; +typedef struct _DiagnosticsEnablePerfmapPayload DiagnosticsEnablePerfmapPayload; typedef struct _DiagnosticsIpcHeader DiagnosticsIpcHeader; typedef struct _DiagnosticsIpcMessage DiagnosticsIpcMessage; typedef struct _DiagnosticsListenPort DiagnosticsListenPort; @@ -71,7 +72,9 @@ typedef enum { DS_PROCESS_COMMANDID_RESUME_RUNTIME = 0x01, DS_PROCESS_COMMANDID_GET_PROCESS_ENV = 0x02, DS_PROCESS_COMMANDID_SET_ENV_VAR = 0x03, - DS_PROCESS_COMMANDID_GET_PROCESS_INFO_2 = 0x04 + DS_PROCESS_COMMANDID_GET_PROCESS_INFO_2 = 0x04, + DS_PROCESS_COMMANDID_ENABLE_PERFMAP = 0x05, + DS_PROCESS_COMMANDID_DISABLE_PERFMAP = 0x06 // future } DiagnosticsProcessCommandId;