From 6f9076c28cf83c31bbecc012d6edc246fa11c0bc Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Wed, 3 Jul 2024 21:21:05 -0700 Subject: [PATCH 01/72] Make CertLoader reject duplicate attributes The new Pkcs12 loader will now reject duplicate attributes, either as multiple attribute sets, or a set with multiple values. The LoaderLimits type gains an option to disable this filter, but as of now it is not being made into public API (though the tests show how to get at it anyways, by the cloning behavior on DangerousNoLimits). This change also fixes the over-filtering of the MachineKey attribute, and adds tests for how that behaves under DefaultKeySet. --- .../src/System/Security/Cryptography/Oids.cs | 1 + .../X509Certificates/Pkcs12LoaderLimits.cs | 21 +++ .../X509CertificateLoader.Pkcs12.cs | 54 +++++-- .../Cryptography/X509Certificates/TestData.cs | 80 ++++++++++ ...9CertificateLoaderPkcs12CollectionTests.cs | 44 ++++++ ...cateLoaderPkcs12Tests.WindowsAttributes.cs | 101 +++++++++--- .../X509CertificateLoaderPkcs12Tests.cs | 149 ++++++++++++++++++ .../Microsoft.Bcl.Cryptography.Tests.csproj | 1 + .../tests/X509Certificates/PfxTests.cs | 32 ++++ 9 files changed, 452 insertions(+), 31 deletions(-) diff --git a/src/libraries/Common/src/System/Security/Cryptography/Oids.cs b/src/libraries/Common/src/System/Security/Cryptography/Oids.cs index 033c9c9db0d9f8..740d464b96e060 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Oids.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/Oids.cs @@ -52,6 +52,7 @@ internal static partial class Oids internal const string CertificateAuthorityIssuers = "1.3.6.1.5.5.7.48.2"; internal const string Pkcs9ExtensionRequest = "1.2.840.113549.1.9.14"; internal const string MsPkcs12KeyProviderName = "1.3.6.1.4.1.311.17.1"; + internal const string MsPkcs12MachineKeySet = "1.3.6.1.4.1.311.17.2"; // Key wrap algorithms internal const string CmsRc2Wrap = "1.2.840.113549.1.9.16.3.7"; diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs index 3200771cee0adb..f19859c54b0352 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/Pkcs12LoaderLimits.cs @@ -22,6 +22,7 @@ public sealed class Pkcs12LoaderLimits private bool _preserveUnknownAttributes; private bool _ignorePrivateKeys; private bool _ignoreEncryptedAuthSafes; + private bool _allowDuplicateAttributes; /// /// Gets a shared reference to the default loader limits. @@ -72,6 +73,7 @@ public sealed class Pkcs12LoaderLimits PreserveKeyName = true, PreserveCertificateAlias = true, PreserveUnknownAttributes = true, + AllowDuplicateAttributes = true, }); /// @@ -117,6 +119,7 @@ public Pkcs12LoaderLimits(Pkcs12LoaderLimits copyFrom) _preserveUnknownAttributes = copyFrom._preserveUnknownAttributes; _ignorePrivateKeys = copyFrom._ignorePrivateKeys; _ignoreEncryptedAuthSafes = copyFrom._ignoreEncryptedAuthSafes; + _allowDuplicateAttributes = copyFrom._allowDuplicateAttributes; } /// @@ -366,6 +369,24 @@ public bool IgnoreEncryptedAuthSafes } } + /// + /// Gets or sets a value indicating whether duplicate attributes are permitted. + /// + /// + /// to permit duplicate attributes; + /// to fail loading when duplicate attributes are found. + /// The default is . + /// + internal bool AllowDuplicateAttributes + { + get => _allowDuplicateAttributes; + set + { + CheckReadOnly(); + _allowDuplicateAttributes = value; + } + } + private static void CheckNonNegative( int? value, [CallerArgumentExpression(nameof(value))] string? paramName = null) diff --git a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs index 196af51bc4d91f..0a216a3a191058 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.Pkcs12.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections.Generic; using System.Diagnostics; using System.Formats.Asn1; using System.Security.Cryptography.Asn1; @@ -268,12 +269,19 @@ private static void ProcessSafeContents( AsnValueReader reader = outer.ReadSequence(); outer.ThrowIfNotEmpty(); + HashSet duplicateAttributeCheck = new(); + while (reader.HasData) { SafeBagAsn.Decode(ref reader, contentData, out SafeBagAsn bag); if (bag.BagId == Oids.Pkcs12CertBag) { + if (bag.BagAttributes is not null && !loaderLimits.AllowDuplicateAttributes) + { + RejectDuplicateAttributes(bag.BagAttributes, duplicateAttributeCheck); + } + CertBagAsn certBag = CertBagAsn.Decode(bag.BagValue, AsnEncodingRules.BER); if (certBag.CertId == Oids.Pkcs12X509CertBagType) @@ -302,6 +310,11 @@ private static void ProcessSafeContents( } else if (bag.BagId is Oids.Pkcs12KeyBag or Oids.Pkcs12ShroudedKeyBag) { + if (bag.BagAttributes is not null && !loaderLimits.AllowDuplicateAttributes) + { + RejectDuplicateAttributes(bag.BagAttributes, duplicateAttributeCheck); + } + if (loaderLimits.IgnorePrivateKeys) { continue; @@ -344,6 +357,9 @@ private static void ProcessSafeContents( attrType switch { Oids.LocalKeyId => true, + // MsPkcs12MachineKeySet can be forced off with the UserKeySet flag, or on with MachineKeySet, + // so always preserve it. + Oids.MsPkcs12MachineKeySet => true, Oids.Pkcs9FriendlyName => limits.PreserveKeyName, Oids.MsPkcs12KeyProviderName => limits.PreserveStorageProvider, _ => limits.PreserveUnknownAttributes, @@ -355,6 +371,21 @@ private static void ProcessSafeContents( } } + private static void RejectDuplicateAttributes(AttributeAsn[] bagAttributes, HashSet duplicateAttributeCheck) + { + duplicateAttributeCheck.Clear(); + + foreach (AttributeAsn attrSet in bagAttributes) + { + // Use >1 instead of =1 to account for MsPkcs12MachineKeySet, which is a named set with no values. + // An empty attribute set can't be followed by the same empty set, or a non-empty set. + if (!duplicateAttributeCheck.Add(attrSet.AttrType) || attrSet.AttrValues.Length > 1) + { + throw new Pkcs12LoadLimitExceededException(nameof(Pkcs12LoaderLimits.AllowDuplicateAttributes)); + } + } + } + private static void FilterAttributes( Pkcs12LoaderLimits loaderLimits, ref SafeBagAsn bag, @@ -362,10 +393,13 @@ private static void FilterAttributes( { if (bag.BagAttributes is not null) { - // Should this dedup/fail-on-dup? int attrIdx = -1; - for (int i = bag.BagAttributes.Length - 1; i > attrIdx; i--) + // Filter the attributes, per the loader limits. + // Because duplicates might be permitted by the options, this filter + // needs to be order preserving. + + for (int i = 0; i < bag.BagAttributes.Length; i++) { string attrType = bag.BagAttributes[i].AttrType; @@ -373,30 +407,28 @@ private static void FilterAttributes( { attrIdx++; - if (i > attrIdx) + if (attrIdx != i) { AttributeAsn attr = bag.BagAttributes[i]; +#if DEBUG bag.BagAttributes[i] = bag.BagAttributes[attrIdx]; +#endif bag.BagAttributes[attrIdx] = attr; - - // After swapping, back up one position to check if the attribute - // swapped into this position should also be preserved. - i++; } } } - attrIdx++; + int attrLen = attrIdx + 1; - if (attrIdx < bag.BagAttributes.Length) + if (attrLen < bag.BagAttributes.Length) { - if (attrIdx == 0) + if (attrLen == 0) { bag.BagAttributes = null; } else { - Array.Resize(ref bag.BagAttributes, attrIdx); + Array.Resize(ref bag.BagAttributes, attrLen); } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs index 458f9b04f4cb46..4c1025a7c1c028 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs @@ -4550,5 +4550,85 @@ internal static DSAParameters GetDSA1024Params() "928343622B80009D8C5B0440E74D33A5F7DA48801A4EF4FD65D0F442F26C845F" + "A418D3E0D78FD0285A92A74B433661F516C6955EC40FE46DAB813B6AE940C2DE" + "FE8F8F5E32E6B491C999D598020127").HexToByteArray(); + + // This is a PFX that has a lot of duplicated attributes: + // Integrity: None + // SC[0] Unencrypted + // Key (3DES, "Placeholder") + // MachineKey (empty) + // MachineKey (empty) + // LocalKeyId { 1 } + // LocalKeyId { 1, 2 } + // FriendlyName { keyname000, keyname001 } + // ProviderName Microsoft Enhanced RSA and AES Cryptographic Provider + // ProviderName Microsoft Software Key Storage Provider + // SC[1] Unencrypted + // Cert, CN=Cerificate 1 + // LocalKeyId { 1, 4 } + // FriendlyName { (CAPI CSP), (CNG KSP) } + // + // Note that this test cannot be built by Pkcs12Builder, because that type + // always unifies attribute sets. + internal static readonly byte[] DuplicateAttributesPfx = ( + "308207760201033082076F06092A864886F70D010701A08207600482075C3082" + + "07583082043006092A864886F70D010701A08204210482041D30820419308204" + + "15060B2A864886F70D010C0A0102A08202A6308202A2301C060A2A864886F70D" + + "010C0103300E0408F7A34F4CC26F79890202100004820280BD3E4386BE9699C0" + + "F53C27D4971B17E984BE885F3E1D3B1B75B666EF3F53BED590A70AA071C05057" + + "7CF587B92AF7F84F0D6E79475CA0D46C5F86A6D548ADE9538A955B7033154F2D" + + "A356B1DD930576A0A1D6CC87FF9055BB00CECFF5E61040B709FC19C29046A4AA" + + "BD4B8D1CA8F09B119ED6A8FBDB985E3F22E531D5DB13E292278D73A6C4E05498" + + "79642858CAA162BB21E9A17B4B14341B388665E0F5B90EB1EFB0C4B211B9B49E" + + "F8C7F435F9B2D32A319D43B9133039844E7ED8C0BA663E8681C8EFDF707356DC" + + "A1B78551839300323F046A497DD4C468232993946343764F151AB3EFFFC7FD27" + + "9739CBE00337399AF2341E7842DA1CCDD98B12A7831A4DE9827F8F1CCB5A0F4E" + + "ECB412D208983CA5D801D720B3F1E118C20BB8A1853770435177253EF59A62A2" + + "43E53ABC531F310E245CC1A0626E5456ADC08924F15E1408B2FD30BFDD4A4F32" + + "01B1983DE0F7F42E7EEF2E8EA6D9B0EAB98174A4B4D0410FD04167670FDFC20A" + + "1EFC58AB2A41ABD3EC42D3071F31EA5D0A6B93EC070E1D543F0FC7BB8B88361A" + + "D904E81ADD0C3B0261F1406EACF956F19055FC1C2832F25209DFBD35C8EDD8B4" + + "091B626E8C07D58F8537C519C90E23E94E8E61DCF3862C1DFB63010D2D909037" + + "6CFB21042041B550FE62122E3473B88E479B42153FB17077C4BC1318715BAB99" + + "597226F0C24524FB844CEAA4EC8DD164321DDFB74509FBC4844C205FFC27B067" + + "C9E4A78B8B12F4643F3A4C754E84F244F84D7A075F290C10A3B544264E317BFA" + + "41647EC4F50D6B1B2A691B5F0575B9492484019E88355CADADBC0A30FAEED71E" + + "4392E37BE497900408A85C711BF68B27A84433B0F546DF2FC2CA3FD22C4367BA" + + "BC074313B982E5012B26863FA98148E5DBF43D26423369C13182015A300D0609" + + "2B06010401823711023100300D06092B06010401823711023100301006092A86" + + "4886F70D0109153103040101301306092A864886F70D01091531060401010401" + + "02303906092A864886F70D010914312C1E14006B00650079006E0061006D0065" + + "0030003000301E14006B00650079006E0061006D006500300030003130790609" + + "2B0601040182371101316C1E6A004D006900630072006F0073006F0066007400" + + "200045006E00680061006E006300650064002000520053004100200061006E00" + + "640020004100450053002000430072007900700074006F006700720061007000" + + "6800690063002000500072006F00760069006400650072305D06092B06010401" + + "8237110131501E4E004D006900630072006F0073006F0066007400200053006F" + + "0066007400770061007200650020004B00650079002000530074006F00720061" + + "00670065002000500072006F007600690064006500723082032006092A864886" + + "F70D010701A08203110482030D3082030930820305060B2A864886F70D010C0A" + + "0103A082020F3082020B060A2A864886F70D01091601A08201FB048201F73082" + + "01F33082015CA00302010202080828E93144879296300D06092A864886F70D01" + + "010B0500303C31223020060355040B13194475706C6963617465204174747269" + + "62757465732054657374311630140603550403130D4365727469666963617465" + + "2031301E170D3234303730323231303531395A170D3234303730323231313031" + + "395A303C31223020060355040B13194475706C69636174652041747472696275" + + "7465732054657374311630140603550403130D43657274696669636174652031" + + "30819F300D06092A864886F70D010101050003818D0030818902818100E6EB18" + + "5FCE3E6A5A6F73D260B408FA9DD06DC99D546AE9A1F1F8792FDF818E8A2759FF" + + "D0FCD8A88301117CA992ACEC9AA4E429655AB7A73EE20994155FE97572471D06" + + "2C06295FF4EE218090DF64AAF787BAD7F511DF329F2F685FFC3B819F95F17811" + + "E9C158D97D832208C27214C958D844432481981B03FE8C9E0E8C1A5605020301" + + "0001300D06092A864886F70D01010B0500038181009243F8BCFB5B21BC5BFB45" + + "83DF87F0D1FE7680C62D1F35698F1A25A0D0517F17B977F039985457A85058DC" + + "7375D1ED845EB30B20FF7FC3E8970871F03AEDE3658FF964B1EF1BFD1EEB7CF5" + + "E4A219F1B00DDED1F00BE42F132A2409D0FE78BA131634F5059B06E1B905AE18" + + "6897E1E10716282E0BE25D460AE93483E9BC0329E13181E2301306092A864886" + + "F70D01091531060401010401043081CA06092A864886F70D0109143181BC1E6A" + + "004D006900630072006F0073006F0066007400200045006E00680061006E0063" + + "00650064002000520053004100200061006E0064002000410045005300200043" + + "0072007900700074006F0067007200610070006800690063002000500072006F" + + "007600690064006500721E4E004D006900630072006F0073006F006600740020" + + "0053006F0066007400770061007200650020004B00650079002000530074006F" + + "0072006100670065002000500072006F00760069006400650072").HexToByteArray(); } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs index 84d57f6e7e74e7..13fa4bb81971fc 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12CollectionTests.cs @@ -742,6 +742,50 @@ public void LoadPfx_VerifyIgnoreEncryptedSafes_EmptyIfIgnored(bool ignoreEncrypt } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadWithDuplicateAttributes(bool allowDuplicates) + { + Pkcs12LoaderLimits limits = Pkcs12LoaderLimits.Defaults; + + if (allowDuplicates) + { + limits = Pkcs12LoaderLimits.DangerousNoLimits; + } + + // remove the edit lock + limits = new Pkcs12LoaderLimits(limits) + { + PreserveCertificateAlias = false, + PreserveKeyName = false, + PreserveStorageProvider = false, + PreserveUnknownAttributes = false, + }; + + Func func = + () => LoadPfxNoFile(TestData.DuplicateAttributesPfx, TestData.PlaceholderPw, loaderLimits: limits); + + if (allowDuplicates) + { + X509Certificate2Collection coll = func(); + + using (new CollectionDisposer(coll)) + { + Assert.Equal(1, coll.Count); + X509Certificate2 cert = coll[0]; + + Assert.Equal("Certificate 1", cert.GetNameInfo(X509NameType.SimpleName, false)); + Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey"); + } + } + else + { + Pkcs12LoadLimitExceededException ex = Assert.Throws(() => func()); + Assert.Contains("AllowDuplicateAttributes", ex.Message); + } + } + private sealed class CollectionDisposer : IDisposable { private readonly X509Certificate2Collection _coll; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs index 444094c15b0ca8..3e07c4e25f5632 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.WindowsAttributes.cs @@ -9,9 +9,11 @@ namespace System.Security.Cryptography.X509Certificates.Tests public abstract partial class X509CertificateLoaderPkcs12Tests { [Theory] - [InlineData(true)] - [InlineData(false)] - public void VerifyPreserveKeyName(bool preserveName) + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void VerifyPreserveKeyName(bool preserveName, bool machineKey) { Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits { @@ -19,7 +21,7 @@ public void VerifyPreserveKeyName(bool preserveName) }; string keyName = Guid.NewGuid().ToString("D"); - byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: "Non-preserved"); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: "Non-preserved", machineKey: machineKey); X509Certificate2 cert = LoadPfxNoFile( pfx, @@ -29,9 +31,8 @@ public void VerifyPreserveKeyName(bool preserveName) using (cert) { using (RSA key = cert.GetRSAPrivateKey()) + using (CngKey cngKey = Assert.IsType(key).Key) { - CngKey cngKey = Assert.IsType(key).Key; - if (preserveName) { Assert.Equal(keyName, cngKey.KeyName); @@ -40,6 +41,9 @@ public void VerifyPreserveKeyName(bool preserveName) { Assert.NotEqual(keyName, cngKey.KeyName); } + + // MachineKey is preserved irrespective of PreserveKeyName + Assert.Equal(machineKey, cngKey.IsMachineKey); } // Alias was not preserved @@ -48,9 +52,11 @@ public void VerifyPreserveKeyName(bool preserveName) } [Theory] - [InlineData(true)] - [InlineData(false)] - public void VerifyPreserveAlias(bool preserveAlias) + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public void VerifyPreserveAlias(bool preserveAlias, bool machineKey) { Pkcs12LoaderLimits loaderLimits = new Pkcs12LoaderLimits { @@ -59,7 +65,7 @@ public void VerifyPreserveAlias(bool preserveAlias) string keyName = Guid.NewGuid().ToString("D"); string alias = Guid.NewGuid().ToString("D"); - byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias, machineKey: machineKey); X509Certificate2 cert = LoadPfxNoFile( pfx, @@ -78,21 +84,27 @@ public void VerifyPreserveAlias(bool preserveAlias) } using (RSA key = cert.GetRSAPrivateKey()) + using (CngKey cngKey = Assert.IsType(key).Key) { - CngKey cngKey = Assert.IsType(key).Key; - // Key name was not preserved Assert.NotEqual(keyName, cngKey.KeyName); + + // MachineKey is preserved irrespective of PreserveCertificateAlias + Assert.Equal(machineKey, cngKey.IsMachineKey); } } } [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveName) + [InlineData(true, true, true)] + [InlineData(true, true, false)] + [InlineData(true, false, true)] + [InlineData(true, false, false)] + [InlineData(false, true, true)] + [InlineData(false, true, false)] + [InlineData(false, false, true)] + [InlineData(false, false, false)] + public void VerifyPreserveProvider(bool preserveProvider, bool preserveName, bool machineKey) { // This test forces a key creation with CAPI, and verifies that // PreserveStorageProvider keeps the key in CAPI. Additionally, @@ -105,7 +117,7 @@ public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveN string keyName = Guid.NewGuid().ToString("D"); string alias = Guid.NewGuid().ToString("D"); - byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias, useCapi: true); + byte[] pfx = MakeAttributeTest(keyName: keyName, friendlyName: alias, useCapi: true, machineKey: machineKey); X509Certificate2 cert = LoadPfxNoFile( pfx, @@ -115,9 +127,8 @@ public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveN using (cert) { using (RSA key = cert.GetRSAPrivateKey()) + using (CngKey cngKey = Assert.IsType(key).Key) { - CngKey cngKey = Assert.IsType(key).Key; - if (preserveName) { Assert.Equal(keyName, cngKey.KeyName); @@ -137,6 +148,9 @@ public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveN { Assert.NotEqual(CapiProvider, cngKey.Provider.Provider); } + + // MachineKey is preserved irrespective of PreserveKeyName or PreserveStorageProvider + Assert.Equal(machineKey, cngKey.IsMachineKey); } // Alias is not preserved @@ -144,10 +158,55 @@ public void VerifyPreservePreserveProvider(bool preserveProvider, bool preserveN } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void VerifyNamesWithDuplicateAttributes(bool noLimits) + { + // This test mainly shows that when duplicate attributes are present contents + // processed by our filter and processed directly by PFXImportCertStore come up + // with the same answer. + + Pkcs12LoaderLimits limits = Pkcs12LoaderLimits.DangerousNoLimits; + + // DangerousNoLimits is tested by reference, by cloning the object we + // use a functional equivalent using the work-limiting and attribute-filtering + // loader. + if (!noLimits) + { + limits = new Pkcs12LoaderLimits(limits); + } + + X509Certificate2 cert = LoadPfxNoFile( + TestData.DuplicateAttributesPfx, + TestData.PlaceholderPw, + X509KeyStorageFlags.DefaultKeySet, + loaderLimits: limits); + + using (cert) + { + Assert.Equal("Certificate 1", cert.GetNameInfo(X509NameType.SimpleName, false)); + Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey"); + Assert.Equal("Microsoft Enhanced RSA and AES Cryptographic Provider", cert.FriendlyName); + + using (RSA key = cert.GetRSAPrivateKey()) + using (CngKey cngKey = Assert.IsType(key).Key) + { + Assert.Equal("Microsoft Enhanced RSA and AES Cryptographic Provider", cngKey.Provider.Provider); + Assert.True(cngKey.IsMachineKey, "cngKey.IsMachineKey"); + + // If keyname000 gets taken, we'll get a random key name on import. What's important is that we + // don't pick the second entry: keyname001. + Assert.NotEqual("keyname001", cngKey.KeyName); + } + } + } + private static byte[] MakeAttributeTest( string? keyName = null, string? friendlyName = null, bool useCapi = false, + bool machineKey = false, [CallerMemberName] string testName = null) { CngKey cngKey = null; @@ -164,6 +223,7 @@ private static byte[] MakeAttributeTest( CspParameters cspParameters = new CspParameters(24) { KeyContainerName = keyName, + Flags = machineKey ? CspProviderFlags.UseMachineKeyStore : CspProviderFlags.NoFlags, }; rsaCsp = new RSACryptoServiceProvider(2048, cspParameters); @@ -174,6 +234,7 @@ private static byte[] MakeAttributeTest( CngKeyCreationParameters cngParams = new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowPlaintextExport, + KeyCreationOptions = machineKey ? CngKeyCreationOptions.MachineKey : CngKeyCreationOptions.None, }; cngKey = CngKey.Create(CngAlgorithm.Rsa, keyName, cngParams); diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs index 6568be9756eb2f..086721eef28b3f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/X509CertificateLoaderPkcs12Tests.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.IO; using System.IO.MemoryMappedFiles; +using System.Security.Cryptography.Pkcs; using Test.Cryptography; using Xunit; @@ -735,5 +736,153 @@ public void VerifyIndependentLifetime() } } } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadWithDuplicateAttributes(bool allowDuplicates) + { + Pkcs12LoaderLimits limits = Pkcs12LoaderLimits.Defaults; + + if (allowDuplicates) + { + limits = Pkcs12LoaderLimits.DangerousNoLimits; + } + + // remove the edit lock + limits = new Pkcs12LoaderLimits(limits) + { + PreserveCertificateAlias = false, + PreserveKeyName = false, + PreserveStorageProvider = false, + PreserveUnknownAttributes = false, + }; + + Func func = + () => LoadPfxNoFile(TestData.DuplicateAttributesPfx, TestData.PlaceholderPw, loaderLimits: limits); + + if (allowDuplicates) + { + using (X509Certificate2 cert = func()) + { + Assert.Equal("Certificate 1", cert.GetNameInfo(X509NameType.SimpleName, false)); + Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey"); + } + } + else + { + Pkcs12LoadLimitExceededException ex = Assert.Throws(() => func()); + Assert.Contains("AllowDuplicateAttributes", ex.Message); + } + } + +#if NET + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadWithDuplicateAttributes_KeyOnly(bool ignorePrivateKeys) + { + byte[] pfx; + + using (ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256)) + { + CertificateRequest req = new CertificateRequest( + "CN=No Duplicates Here", + key, + HashAlgorithmName.SHA256); + + DateTimeOffset now = DateTimeOffset.UtcNow; + + using (X509Certificate2 cert = req.CreateSelfSigned(now, now.AddMinutes(1))) + { + Pkcs12SafeContents contents = new Pkcs12SafeContents(); + Pkcs9LocalKeyId keyId = new Pkcs9LocalKeyId(new byte[] { 2, 2, 4 }); + + Pkcs12CertBag certBag = contents.AddCertificate(cert); + certBag.Attributes.Add(keyId); + + Pkcs12KeyBag keyBag = contents.AddKeyUnencrypted(key); + keyBag.Attributes.Add(keyId); + keyBag.Attributes.Add(keyId); + + Pkcs12Builder builder = new Pkcs12Builder(); + builder.AddSafeContentsUnencrypted(contents); + builder.SealWithoutIntegrity(); + pfx = builder.Encode(); + } + } + + Pkcs12LoaderLimits limits = new Pkcs12LoaderLimits + { + IgnorePrivateKeys = ignorePrivateKeys, + }; + + Exception ex = Assert.Throws( + () => LoadPfxNoFile(pfx, loaderLimits: limits)); + + Assert.Contains("AllowDuplicateAttributes", ex.Message); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void LoadWithDuplicateAttributes_EncryptedOnly(bool ignoreEncryptedAuthSafes) + { + byte[] pfx; + + using (ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256)) + { + CertificateRequest req = new CertificateRequest( + "CN=No Duplicates Here", + key, + HashAlgorithmName.SHA256); + + DateTimeOffset now = DateTimeOffset.UtcNow; + + using (X509Certificate2 cert = req.CreateSelfSigned(now, now.AddMinutes(1))) + { + Pkcs12SafeContents certSafe = new Pkcs12SafeContents(); + Pkcs12SafeContents keySafe = new Pkcs12SafeContents(); + Pkcs9LocalKeyId keyId = new Pkcs9LocalKeyId(new byte[] { 2, 2, 4 }); + + Pkcs12CertBag certBag = certSafe.AddCertificate(cert); + certBag.Attributes.Add(keyId); + + Pkcs12KeyBag keyBag = keySafe.AddKeyUnencrypted(key); + keyBag.Attributes.Add(keyId); + keyBag.Attributes.Add(keyId); + + Pkcs12Builder builder = new Pkcs12Builder(); + builder.AddSafeContentsUnencrypted(certSafe); + + builder.AddSafeContentsEncrypted( + keySafe, + "", + new PbeParameters(PbeEncryptionAlgorithm.TripleDes3KeyPkcs12, HashAlgorithmName.SHA1, 1)); + + builder.SealWithoutIntegrity(); + pfx = builder.Encode(); + } + } + + Pkcs12LoaderLimits limits = new Pkcs12LoaderLimits + { + IgnoreEncryptedAuthSafes = ignoreEncryptedAuthSafes, + }; + + Func func = () => LoadPfxNoFile(pfx, loaderLimits: limits); + + if (ignoreEncryptedAuthSafes) + { + // Assert.NoThrow + func().Dispose(); + } + else + { + Exception ex = Assert.Throws(() => func()); + Assert.Contains("AllowDuplicateAttributes", ex.Message); + } + } +#endif } } diff --git a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj index 309d9b2e1c16b0..a08fb7f599bd17 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/tests/Microsoft.Bcl.Cryptography.Tests.csproj @@ -34,6 +34,7 @@ + diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs index 7a6a8db3567914..f59c1e7767c91e 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/PfxTests.cs @@ -481,6 +481,38 @@ public static void CollectionPerphemeralImport_HasKeyName() } } + [Fact] + public static void VerifyNamesWithDuplicateAttributes() + { + // This is the same as the Windows Attributes test for X509CertificateLoaderPkcs12Tests, + // but using the legacy X509Certificate2 ctor, to test the settings for that set of + // loader limits with respect to duplicates. + + X509Certificate2 cert = new X509Certificate2(TestData.DuplicateAttributesPfx, TestData.PlaceholderPw); + + using (cert) + { + Assert.Equal("Certificate 1", cert.GetNameInfo(X509NameType.SimpleName, false)); + Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey"); + + if (OperatingSystem.IsWindows()) + { + Assert.Equal("Microsoft Enhanced RSA and AES Cryptographic Provider", cert.FriendlyName); + + using (RSA key = cert.GetRSAPrivateKey()) + using (CngKey cngKey = Assert.IsType(key).Key) + { + Assert.Equal("Microsoft Enhanced RSA and AES Cryptographic Provider", cngKey.Provider.Provider); + Assert.True(cngKey.IsMachineKey, "cngKey.IsMachineKey"); + + // If keyname000 gets taken, we'll get a random key name on import. What's important is that we + // don't pick the second entry: keyname001. + Assert.NotEqual("keyname001", cngKey.KeyName); + } + } + } + } + internal static bool IsPkcs12IterationCountAllowed(long iterationCount, long allowedIterations) { if (allowedIterations == UnlimitedIterations) From 69f70063b453c7da963b779c5beb436399089fa9 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Wed, 3 Jul 2024 23:26:36 -0700 Subject: [PATCH 02/72] Arm64/Sve: Do not mark *Prefetch* APIs as MEMORY_LOAD (#104396) * fix prefetch in proper way * jit format * review comment --- src/coreclr/jit/fgdiagnostic.cpp | 8 +++ src/coreclr/jit/gentree.cpp | 24 ++++--- src/coreclr/jit/hwintrinsic.cpp | 24 ------- src/coreclr/jit/hwintrinsicarm64.cpp | 76 +++++++++++++++++++++++ src/coreclr/jit/hwintrinsiclistarm64sve.h | 16 ++--- 5 files changed, 108 insertions(+), 40 deletions(-) diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index ffc7a933b5cb51..45cdcfea0f46b6 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3459,6 +3459,14 @@ void Compiler::fgDebugCheckFlags(GenTree* tree, BasicBlock* block) #if defined(TARGET_ARM64) case NI_ArmBase_Yield: + case NI_Sve_PrefetchBytes: + case NI_Sve_PrefetchInt16: + case NI_Sve_PrefetchInt32: + case NI_Sve_PrefetchInt64: + case NI_Sve_GatherPrefetch16Bit: + case NI_Sve_GatherPrefetch32Bit: + case NI_Sve_GatherPrefetch64Bit: + case NI_Sve_GatherPrefetch8Bit: { assert(tree->OperRequiresCallFlag(this)); expectedFlags |= GTF_GLOB_REF; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a0a709985c1ea3..786e7c92f5f894 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27459,17 +27459,9 @@ bool GenTreeHWIntrinsic::OperIsMemoryLoad(GenTree** pAddr) const case NI_Sve_Load2xVectorAndUnzip: case NI_Sve_Load3xVectorAndUnzip: case NI_Sve_Load4xVectorAndUnzip: - case NI_Sve_PrefetchBytes: - case NI_Sve_PrefetchInt16: - case NI_Sve_PrefetchInt32: - case NI_Sve_PrefetchInt64: addr = Op(2); break; - case NI_Sve_GatherPrefetch8Bit: - case NI_Sve_GatherPrefetch16Bit: - case NI_Sve_GatherPrefetch32Bit: - case NI_Sve_GatherPrefetch64Bit: case NI_Sve_GatherVector: case NI_Sve_GatherVectorByteZeroExtend: case NI_Sve_GatherVectorInt16SignExtend: @@ -27963,6 +27955,14 @@ bool GenTreeHWIntrinsic::OperRequiresCallFlag() const #if defined(TARGET_ARM64) case NI_ArmBase_Yield: + case NI_Sve_PrefetchBytes: + case NI_Sve_PrefetchInt16: + case NI_Sve_PrefetchInt32: + case NI_Sve_PrefetchInt64: + case NI_Sve_GatherPrefetch16Bit: + case NI_Sve_GatherPrefetch32Bit: + case NI_Sve_GatherPrefetch64Bit: + case NI_Sve_GatherPrefetch8Bit: { return true; } @@ -28145,6 +28145,14 @@ void GenTreeHWIntrinsic::Initialize(NamedIntrinsic intrinsicId) #if defined(TARGET_ARM64) case NI_ArmBase_Yield: + case NI_Sve_PrefetchBytes: + case NI_Sve_PrefetchInt16: + case NI_Sve_PrefetchInt32: + case NI_Sve_PrefetchInt64: + case NI_Sve_GatherPrefetch16Bit: + case NI_Sve_GatherPrefetch32Bit: + case NI_Sve_GatherPrefetch64Bit: + case NI_Sve_GatherPrefetch8Bit: { // Mark as a call and global reference, much as is done for GT_KEEPALIVE gtFlags |= (GTF_CALL | GTF_GLOB_REF); diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index eb48a9b041ae99..4f016940840b2c 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1856,14 +1856,6 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, break; #elif defined(TARGET_ARM64) - case NI_Sve_GatherPrefetch8Bit: - case NI_Sve_GatherPrefetch16Bit: - case NI_Sve_GatherPrefetch32Bit: - case NI_Sve_GatherPrefetch64Bit: - assert(varTypeIsSIMD(op2->TypeGet())); - retNode->AsHWIntrinsic()->SetAuxiliaryJitType(getBaseJitTypeOfSIMDType(sigReader.op2ClsHnd)); - break; - case NI_Sve_GatherVector: case NI_Sve_GatherVectorByteZeroExtend: case NI_Sve_GatherVectorInt16SignExtend: @@ -1893,22 +1885,6 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, assert(!isScalar); retNode = gtNewSimdHWIntrinsicNode(nodeRetType, op1, op2, op3, op4, intrinsic, simdBaseJitType, simdSize); - -#if defined(TARGET_ARM64) - switch (intrinsic) - { - case NI_Sve_GatherPrefetch8Bit: - case NI_Sve_GatherPrefetch16Bit: - case NI_Sve_GatherPrefetch32Bit: - case NI_Sve_GatherPrefetch64Bit: - assert(varTypeIsSIMD(op3->TypeGet())); - retNode->AsHWIntrinsic()->SetAuxiliaryJitType(getBaseJitTypeOfSIMDType(sigReader.op3ClsHnd)); - break; - - default: - break; - } -#endif break; } diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index 0d367269c66ff7..d69b0a9016575a 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -2725,6 +2725,82 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, retNode->AsHWIntrinsic()->SetAuxiliaryJitType(op1BaseJitType); break; } + case NI_Sve_GatherPrefetch8Bit: + case NI_Sve_GatherPrefetch16Bit: + case NI_Sve_GatherPrefetch32Bit: + case NI_Sve_GatherPrefetch64Bit: + case NI_Sve_PrefetchBytes: + case NI_Sve_PrefetchInt16: + case NI_Sve_PrefetchInt32: + case NI_Sve_PrefetchInt64: + { + assert((sig->numArgs == 3) || (sig->numArgs == 4)); + assert(!isScalar); + + var_types argType = TYP_UNKNOWN; + CORINFO_CLASS_HANDLE argClass = NO_CLASS_HANDLE; + int immLowerBound = 0; + int immUpperBound = 0; + + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; + CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); + CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); + + HWIntrinsicInfo::lookupImmBounds(intrinsic, simdSize, simdBaseType, 1, &immLowerBound, &immUpperBound); + + if (sig->numArgs == 3) + { + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); + op3 = getArgForHWIntrinsic(argType, argClass); + + assert(HWIntrinsicInfo::isImmOp(intrinsic, op3)); + op3 = addRangeCheckIfNeeded(intrinsic, op3, mustExpand, immLowerBound, immUpperBound); + + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); + op2 = getArgForHWIntrinsic(argType, argClass); + CorInfoType op2BaseJitType = getBaseJitTypeOfSIMDType(argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass))); + op1 = impPopStack().val; + +#ifdef DEBUG + + if ((intrinsic == NI_Sve_GatherPrefetch8Bit) || (intrinsic == NI_Sve_GatherPrefetch16Bit) || + (intrinsic == NI_Sve_GatherPrefetch32Bit) || (intrinsic == NI_Sve_GatherPrefetch64Bit)) + { + assert(varTypeIsSIMD(op2->TypeGet())); + } + else + { + assert(varTypeIsIntegral(op2->TypeGet())); + } +#endif + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, intrinsic, simdBaseJitType, simdSize); + retNode->AsHWIntrinsic()->SetAuxiliaryJitType(op2BaseJitType); + } + else + { + CORINFO_ARG_LIST_HANDLE arg4 = info.compCompHnd->getArgNext(arg3); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg4, &argClass))); + op4 = getArgForHWIntrinsic(argType, argClass); + + assert(HWIntrinsicInfo::isImmOp(intrinsic, op4)); + op3 = addRangeCheckIfNeeded(intrinsic, op4, mustExpand, immLowerBound, immUpperBound); + + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); + op3 = getArgForHWIntrinsic(argType, argClass); + CorInfoType op3BaseJitType = getBaseJitTypeOfSIMDType(argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); + op2 = getArgForHWIntrinsic(argType, argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass))); + op1 = impPopStack().val; + + assert(varTypeIsSIMD(op3->TypeGet())); + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2, op3, op4, intrinsic, simdBaseJitType, simdSize); + retNode->AsHWIntrinsic()->SetAuxiliaryJitType(op3BaseJitType); + } + + break; + } default: { diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index 5149ac6fc867cd..6933f8e2f9b816 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -84,10 +84,10 @@ HARDWARE_INTRINSIC(Sve, FusedMultiplyAddNegated, HARDWARE_INTRINSIC(Sve, FusedMultiplySubtract, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmls, INS_sve_fmls}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, FusedMultiplySubtractBySelectedScalar, -1, 4, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmls, INS_sve_fmls}, HW_Category_SIMDByIndexedElement, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasRMWSemantics|HW_Flag_FmaIntrinsic|HW_Flag_LowVectorOperation) HARDWARE_INTRINSIC(Sve, FusedMultiplySubtractNegated, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fnmls, INS_sve_fnmls}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(Sve, GatherPrefetch16Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_sve_prfh, INS_sve_prfh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, GatherPrefetch32Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfw, INS_sve_prfw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, GatherPrefetch64Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfd, INS_sve_prfd, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, GatherPrefetch8Bit, -1, -1, false, {INS_sve_prfb, INS_sve_prfb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, GatherPrefetch16Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_sve_prfh, INS_sve_prfh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, GatherPrefetch32Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfw, INS_sve_prfw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, GatherPrefetch64Bit, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfd, INS_sve_prfd, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, GatherPrefetch8Bit, -1, -1, false, {INS_sve_prfb, INS_sve_prfb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) HARDWARE_INTRINSIC(Sve, GatherVector, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1w, INS_sve_ld1w, INS_sve_ld1d, INS_sve_ld1d, INS_sve_ld1w, INS_sve_ld1d}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, GatherVectorByteZeroExtend, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1b, INS_sve_ld1b, INS_sve_ld1b, INS_sve_ld1b, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, GatherVectorInt16SignExtend, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ld1sh, INS_sve_ld1sh, INS_sve_ld1sh, INS_sve_ld1sh, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) @@ -177,10 +177,10 @@ HARDWARE_INTRINSIC(Sve, Not, HARDWARE_INTRINSIC(Sve, Or, -1, -1, false, {INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_sve_orr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, OrAcross, -1, -1, false, {INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_sve_orv, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, PopCount, -1, -1, false, {INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt, INS_sve_cnt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) -HARDWARE_INTRINSIC(Sve, PrefetchBytes, -1, 3, false, {INS_invalid, INS_sve_prfb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, PrefetchInt16, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_prfh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, PrefetchInt32, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) -HARDWARE_INTRINSIC(Sve, PrefetchInt64, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfd, INS_invalid, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, PrefetchBytes, -1, 3, false, {INS_invalid, INS_sve_prfb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, PrefetchInt16, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_sve_prfh, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, PrefetchInt32, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) +HARDWARE_INTRINSIC(Sve, PrefetchInt64, -1, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_prfd, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand) HARDWARE_INTRINSIC(Sve, ReciprocalEstimate, -1, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_frecpe, INS_sve_frecpe}, HW_Category_SIMD, HW_Flag_Scalable) HARDWARE_INTRINSIC(Sve, ReciprocalExponent, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_frecpx, INS_sve_frecpx}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ReciprocalSqrtEstimate, -1, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_frsqrte, INS_sve_frsqrte}, HW_Category_SIMD, HW_Flag_Scalable) From c57f9aebb290fe7b626c6799c1b55aa7cf646497 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan <96171496+mkhamoyan@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:16:11 +0200 Subject: [PATCH 03/72] disable failing test (#104382) --- .../tests/System.Reflection.Tests/AssemblyNameTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs index a2bf6004072980..873d3d474545a4 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/AssemblyNameTests.cs @@ -222,6 +222,7 @@ public void CultureName_Set(AssemblyName assemblyName, string originalCultureNam } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))] public void CultureName_Set_Invalid_ThrowsCultureNotFoundException() { var assemblyName = new AssemblyName("Test"); From a8616d941556bc03e2519aac062761489083681f Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 4 Jul 2024 10:46:42 +0200 Subject: [PATCH 04/72] JIT: Add a disabled-by-default implementation of strength reduction (#104243) This adds a disabled-by-default implementation of strength reduction. At this point the implementation should be correct, however it is currently both a size and perfscore regression when it is enabled. More work will be needed to get the heuristics right and to make it kick in for more cases. Strength reduction replaces "expensive" operations computed on every loop iteration with cheaper ones by creating more induction variables. In C# terms it effectively transforms something like ``` private struct S { public int A, B, C; } [MethodImpl(MethodImplOptions.NoInlining)] private static float Sum(S[] ss) { int sum = 0; foreach (S v in ss) { sum += v.A; sum += v.B; sum += v.C; } return sum; } ``` into an equivalent ``` int sum = 0; ref S curS = ref ss[0]; for (int i = 0; i < ss.Length; i++) { sum += curS.A; sum += curS.B; sum += curS.C; curS = ref Unsafe.Add(ref curS, 1); } ``` With strength reduction enabled this PR thus changes codegen of the standard `foreach` version above from ```asm G_M63518_IG03: ;; offset=0x0011 lea r10, [rdx+2*rdx] lea r10, bword ptr [rcx+4*r10+0x10] mov r9d, dword ptr [r10] mov r11d, dword ptr [r10+0x04] mov r10d, dword ptr [r10+0x08] add eax, r9d add eax, r11d add eax, r10d inc edx cmp r8d, edx jg SHORT G_M63518_IG03 ;; size=36 bbWeight=4 PerfScore 39.00 ``` to ```asm G_M63518_IG04: ;; offset=0x0011 mov r8, rcx mov r10d, dword ptr [r8] mov r9d, dword ptr [r8+0x04] mov r8d, dword ptr [r8+0x08] add eax, r10d add eax, r9d add eax, r8d add rcx, 12 dec edx jne SHORT G_M63518_IG04 ;; size=31 bbWeight=4 PerfScore 34.00 ``` on x64. Further improvements can be made to enable better address modes. The current heuristics try to ensure that we do not actually end up with more primary induction variables. The strength reduction only kicks in when it thinks that all uses of the primary IV can be replaced by the new primary IV. However, uses inside loop exit tests are allowed to stay unreplaced by the assumption that the downwards loop transformation will be able to get rid of them. Getting the cases around overflow right turned out to be hard and required reasoning about trip counts that was added in a previous PR. Generally, the issue is that we need to prove that transforming a zero extension of an add recurrence to a 64-bit add recurrence is legal. For example, for a simple case of ``` for (int i = 0; i < arr.Length; i++) sum += arr[i]; ``` the IV analysis is eventually going to end up wanting to show that `zext<64>(int32 ) => int64 ` is a correct transformation. This requires showing that the add recurrence does not step past 2^32-1, which requires the bound on the trip count that we can now compute. The reasoning done for both the trip count and around the overflow is still very limited but can be improved incrementally. The implementation works by considering every primary IV of the loop in turn, and by initializing 'cursors' pointing to each use of the primary IV. It then tries to repeatedly advance these cursors to the parent of the uses while it results in a new set of cursors that still compute the same (now derived) IV. If it manages to do this once, then replacing the cursors by a new primary IV should result in the old primary IV no longer being necessary, while having replaced some operations by cheaper ones. --- src/coreclr/jit/arraystack.h | 5 + src/coreclr/jit/compiler.h | 6 +- src/coreclr/jit/inductionvariableopts.cpp | 767 +++++++++++++++++++++- src/coreclr/jit/jitconfigvalues.h | 3 + src/coreclr/jit/jitmetadatalist.h | 1 + src/coreclr/jit/scev.cpp | 236 ++++++- src/coreclr/jit/scev.h | 16 +- 7 files changed, 1000 insertions(+), 34 deletions(-) diff --git a/src/coreclr/jit/arraystack.h b/src/coreclr/jit/arraystack.h index 5d8a697a3820d3..872e64f3c8c99f 100644 --- a/src/coreclr/jit/arraystack.h +++ b/src/coreclr/jit/arraystack.h @@ -121,6 +121,11 @@ class ArrayStack tosIndex = 0; } + T* Data() + { + return data; + } + private: CompAllocator m_alloc; int tosIndex; // first free location diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 633653101e01a5..488d6a01de9e1e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6430,11 +6430,9 @@ class Compiler Statement* fgNewStmtAtEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo()); Statement* fgNewStmtNearEnd(BasicBlock* block, GenTree* tree, const DebugInfo& di = DebugInfo()); -private: void fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt); void fgInsertStmtAtBeg(BasicBlock* block, Statement* stmt); -public: void fgInsertStmtAfter(BasicBlock* block, Statement* insertionPoint, Statement* stmt); void fgInsertStmtBefore(BasicBlock* block, Statement* insertionPoint, Statement* stmt); @@ -7563,6 +7561,8 @@ class Compiler PhaseStatus optInductionVariables(); + template + void optVisitBoundingExitingCondBlocks(FlowGraphNaturalLoop* loop, TFunctor func); bool optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, FlowGraphNaturalLoop* loop, LoopLocalOccurrences* loopLocals); @@ -10345,6 +10345,8 @@ class Compiler STRESS_MODE(OPT_REPEAT) /* stress JitOptRepeat */ \ STRESS_MODE(INITIAL_PARAM_REG) /* Stress initial register assigned to parameters */ \ STRESS_MODE(DOWNWARDS_COUNTED_LOOPS) /* Make more loops downwards counted */ \ + STRESS_MODE(STRENGTH_REDUCTION) /* Enable strength reduction */ \ + STRESS_MODE(STRENGTH_REDUCTION_PROFITABILITY) /* Do more strength reduction */ \ \ /* After COUNT_VARN, stress level 2 does all of these all the time */ \ \ diff --git a/src/coreclr/jit/inductionvariableopts.cpp b/src/coreclr/jit/inductionvariableopts.cpp index 4311bb0c3ce42d..bdbece4cc3614e 100644 --- a/src/coreclr/jit/inductionvariableopts.cpp +++ b/src/coreclr/jit/inductionvariableopts.cpp @@ -67,6 +67,8 @@ class LoopLocalOccurrences template bool VisitStatementsWithOccurrences(FlowGraphNaturalLoop* loop, unsigned lclNum, TFunc func); + + void Invalidate(FlowGraphNaturalLoop* loop); }; LoopLocalOccurrences::LoopLocalOccurrences(FlowGraphNaturalLoops* loops) @@ -184,7 +186,7 @@ bool LoopLocalOccurrences::VisitLoopNestMaps(FlowGraphNaturalLoop* loop, TFunc& // Visit all occurrences of the specified local inside the loop. // // Type parameters: -// TFunc - Functor of type bool(Block*, Statement*, GenTree*) +// TFunc - Functor of type bool(Block*, Statement*, GenTreeLclVarCommon*) // // Parameters: // loop - The loop @@ -240,7 +242,7 @@ bool LoopLocalOccurrences::VisitOccurrences(FlowGraphNaturalLoop* loop, unsigned // bool LoopLocalOccurrences::HasAnyOccurrences(FlowGraphNaturalLoop* loop, unsigned lclNum) { - if (!VisitOccurrences(loop, lclNum, [](BasicBlock* block, Statement* stmt, GenTree* tree) { + if (!VisitOccurrences(loop, lclNum, [](BasicBlock* block, Statement* stmt, GenTreeLclVarCommon* tree) { return false; })) { @@ -314,6 +316,32 @@ bool LoopLocalOccurrences::VisitStatementsWithOccurrences(FlowGraphNaturalLoop* return VisitLoopNestMaps(loop, visitor); } +//------------------------------------------------------------------------ +// Invalidate: Invalidate all information about locals in the specified loop +// and its child loops. +// +// Parameters: +// loop - The loop +// +void LoopLocalOccurrences::Invalidate(FlowGraphNaturalLoop* loop) +{ + for (FlowGraphNaturalLoop* child = loop->GetChild(); child != nullptr; child = child->GetSibling()) + { + Invalidate(child); + } + + if (m_maps[loop->GetIndex()] != nullptr) + { + m_maps[loop->GetIndex()] = nullptr; + + BitVecTraits poTraits = m_loops->GetDfsTree()->PostOrderTraits(); + loop->VisitLoopBlocks([=, &poTraits](BasicBlock* block) { + BitVecOps::RemoveElemD(&poTraits, m_visitedBlocks, block->bbPostorderNum); + return BasicBlockVisit::Continue; + }); + } +} + //------------------------------------------------------------------------ // optCanSinkWidenedIV: Check to see if we are able to sink a store to the old // local into the exits of a loop if we decide to widen. @@ -862,23 +890,17 @@ bool Compiler::optWidenPrimaryIV(FlowGraphNaturalLoop* loop, } //------------------------------------------------------------------------ -// optMakeLoopDownwardsCounted: Transform a loop to be downwards counted if -// profitable and legal. +// optVisitBoundingExitingBlocks: Visit all the exiting BBJ_COND blocks of the +// loop that dominate all the loop's backedges. These exiting blocks bound the +// trip count of the loop. // // Parameters: -// scevContext - Context for scalar evolution -// loop - Loop to transform -// loopLocals - Data structure that tracks occurrences of locals in the loop -// -// Returns: -// True if the loop was made downwards counted; otherwise false. +// loop - The loop +// func - The functor, of type void(BasicBlock*). // -bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, - FlowGraphNaturalLoop* loop, - LoopLocalOccurrences* loopLocals) +template +void Compiler::optVisitBoundingExitingCondBlocks(FlowGraphNaturalLoop* loop, TFunctor func) { - JITDUMP("Checking if we should make " FMT_LP " downwards counted\n", loop->GetIndex()); - BasicBlock* dominates = nullptr; for (FlowEdge* backEdge : loop->BackEdges()) @@ -899,13 +921,37 @@ bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, if (dominates->KindIs(BBJ_COND) && (!loop->ContainsBlock(dominates->GetTrueTarget()) || !loop->ContainsBlock(dominates->GetFalseTarget()))) { - JITDUMP(" Considering exiting block " FMT_BB "\n", dominates->bbNum); // 'dominates' is an exiting block that dominates all backedges. - changed |= optMakeExitTestDownwardsCounted(scevContext, loop, dominates, loopLocals); + func(dominates); } dominates = dominates->bbIDom; } +} + +//------------------------------------------------------------------------ +// optMakeLoopDownwardsCounted: Transform a loop to be downwards counted if +// profitable and legal. +// +// Parameters: +// scevContext - Context for scalar evolution +// loop - Loop to transform +// loopLocals - Data structure that tracks occurrences of locals in the loop +// +// Returns: +// True if the loop was made downwards counted; otherwise false. +// +bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext, + FlowGraphNaturalLoop* loop, + LoopLocalOccurrences* loopLocals) +{ + JITDUMP("Checking if we should make " FMT_LP " downwards counted\n", loop->GetIndex()); + + bool changed = false; + optVisitBoundingExitingCondBlocks(loop, [=, &scevContext, &changed](BasicBlock* exiting) { + JITDUMP(" Considering exiting block " FMT_BB "\n", exiting->bbNum); + changed |= optMakeExitTestDownwardsCounted(scevContext, loop, exiting, loopLocals); + }); return changed; } @@ -1147,6 +1193,684 @@ bool Compiler::optMakeExitTestDownwardsCounted(ScalarEvolutionContext& scevConte return true; } +struct CursorInfo +{ + BasicBlock* Block; + Statement* Stmt; + GenTree* Tree; + ScevAddRec* IV; + bool IsInsideExitTest = false; + + CursorInfo(BasicBlock* block, Statement* stmt, GenTree* tree, ScevAddRec* iv, bool isInsideExitTest) + : Block(block) + , Stmt(stmt) + , Tree(tree) + , IV(iv) + , IsInsideExitTest(isInsideExitTest) + { + } +}; + +class StrengthReductionContext +{ + Compiler* m_comp; + ScalarEvolutionContext& m_scevContext; + FlowGraphNaturalLoop* m_loop; + LoopLocalOccurrences& m_loopLocals; + + ArrayStack m_backEdgeBounds; + SimplificationAssumptions m_simplAssumptions; + ArrayStack m_cursors1; + ArrayStack m_cursors2; + + void InitializeSimplificationAssumptions(); + bool InitializeCursors(GenTreeLclVarCommon* primaryIVLcl, ScevAddRec* primaryIV); + void AdvanceCursors(ArrayStack* cursors, ArrayStack* nextCursors); + bool CheckAdvancedCursors(ArrayStack* cursors, int derivedLevel, ScevAddRec** nextIV); + bool TryReplaceUsesWithNewPrimaryIV(ArrayStack* cursors, ScevAddRec* iv); + BasicBlock* FindUpdateInsertionPoint(ArrayStack* cursors); + + bool StressProfitability() + { + return m_comp->compStressCompile(Compiler::STRESS_STRENGTH_REDUCTION_PROFITABILITY, 50); + } + +public: + StrengthReductionContext(Compiler* comp, + ScalarEvolutionContext& scevContext, + FlowGraphNaturalLoop* loop, + LoopLocalOccurrences& loopLocals) + : m_comp(comp) + , m_scevContext(scevContext) + , m_loop(loop) + , m_loopLocals(loopLocals) + , m_backEdgeBounds(comp->getAllocator(CMK_LoopIVOpts)) + , m_cursors1(comp->getAllocator(CMK_LoopIVOpts)) + , m_cursors2(comp->getAllocator(CMK_LoopIVOpts)) + { + } + + bool TryStrengthReduce(); +}; + +//------------------------------------------------------------------------ +// TryStrengthReduce: Check for legal and profitable derived IVs to introduce +// new primary IVs for. +// +// Returns: +// True if any new primary IV was introduced; otherwise false. +// +bool StrengthReductionContext::TryStrengthReduce() +{ + JITDUMP("Considering " FMT_LP " for strength reduction...\n", m_loop->GetIndex()); + + if ((JitConfig.JitEnableStrengthReduction() == 0) && + !m_comp->compStressCompile(Compiler::STRESS_STRENGTH_REDUCTION, 50)) + { + JITDUMP(" Disabled: no stress mode\n"); + return false; + } + + // Compute information about the loop used to simplify SCEVs. + InitializeSimplificationAssumptions(); + + JITDUMP(" Considering primary IVs\n"); + + // We strength reduce only candidates where we see that we'll be able to + // remove all uses of a primary IV by introducing a different primary IV. + // + // The algorithm here works in the following way: we process each primary + // IV in turn. For every primary IV, we create a 'cursor' pointing to every + // use of that primary IV. We then continuously advance each cursor to the + // parent node as long as all cursors represent the same derived IV. Once we + // find out that the cursors are no longer the same derived IV we stop. + // + // We keep two lists here so that we can keep track of the most advanced + // cursor where all cursors pointed to the same derived IV, in which case + // we can strength reduce. + + bool strengthReducedAny = false; + for (Statement* stmt : m_loop->GetHeader()->Statements()) + { + if (!stmt->IsPhiDefnStmt()) + { + break; + } + + DISPSTMT(stmt); + + GenTreeLclVarCommon* primaryIVLcl = stmt->GetRootNode()->AsLclVarCommon(); + Scev* candidate = m_scevContext.Analyze(m_loop->GetHeader(), primaryIVLcl); + if (candidate == nullptr) + { + JITDUMP(" Could not analyze header PHI\n"); + continue; + } + + candidate = m_scevContext.Simplify(candidate, m_simplAssumptions); + + JITDUMP(" => "); + DBEXEC(m_comp->verbose, candidate->Dump(m_comp)); + + JITDUMP("\n"); + if (!candidate->OperIs(ScevOper::AddRec)) + { + JITDUMP(" Not an addrec\n"); + continue; + } + + ScevAddRec* primaryIV = static_cast(candidate); + + InitializeCursors(primaryIVLcl, primaryIV); + + ArrayStack* cursors = &m_cursors1; + ArrayStack* nextCursors = &m_cursors2; + + int derivedLevel = 0; + ScevAddRec* currentIV = primaryIV; + + while (true) + { + JITDUMP(" Advancing cursors to be %d-derived\n", derivedLevel + 1); + + // Advance cursors and store the result in 'nextCursors' + AdvanceCursors(cursors, nextCursors); + + // Verify that all cursors still represent the same IV + ScevAddRec* nextIV = nullptr; + if (!CheckAdvancedCursors(nextCursors, derivedLevel + 1, &nextIV)) + { + break; + } + assert(nextIV != nullptr); + + // We need more sanity checks to allow materializing GC-typed add + // recs. Otherwise we may eagerly form a GC pointer that was only + // lazily formed under some conditions before, which can be + // illegal. For now we just bail. + if (varTypeIsGC(nextIV->Type)) + { + JITDUMP(" Next IV has type %s. Bailing.\n", varTypeName(nextIV->Type)); + break; + } + + derivedLevel++; + std::swap(cursors, nextCursors); + currentIV = nextIV; + } + + if (derivedLevel <= 0) + { + continue; + } + + JITDUMP(" All uses of primary IV V%02u are used to compute a %d-derived IV ", primaryIVLcl->GetLclNum(), + derivedLevel); + DBEXEC(VERBOSE, currentIV->Dump(m_comp)); + JITDUMP("\n"); + + if (Scev::Equals(currentIV->Step, primaryIV->Step) && !StressProfitability()) + { + JITDUMP(" Skipping: candidate has same step as primary IV\n"); + continue; + } + + if (TryReplaceUsesWithNewPrimaryIV(cursors, currentIV)) + { + strengthReducedAny = true; + m_loopLocals.Invalidate(m_loop); + } + } + + return strengthReducedAny; +} + +//------------------------------------------------------------------------ +// InitializeSimplificationAssumptions: Compute assumptions that can be used +// when simplifying SCEVs. +// +void StrengthReductionContext::InitializeSimplificationAssumptions() +{ + m_comp->optVisitBoundingExitingCondBlocks(m_loop, [=](BasicBlock* exiting) { + Scev* exitNotTakenCount = m_scevContext.ComputeExitNotTakenCount(exiting); + if (exitNotTakenCount != nullptr) + { + m_backEdgeBounds.Push(exitNotTakenCount); + } + }); + + m_simplAssumptions.BackEdgeTakenBound = m_backEdgeBounds.Data(); + m_simplAssumptions.NumBackEdgeTakenBound = static_cast(m_backEdgeBounds.Height()); + +#ifdef DEBUG + if (m_comp->verbose) + { + printf(" Bound on backedge taken count is "); + if (m_simplAssumptions.NumBackEdgeTakenBound == 0) + { + printf("\n"); + } + + const char* pref = m_simplAssumptions.NumBackEdgeTakenBound > 1 ? "min(" : ""; + for (unsigned i = 0; i < m_simplAssumptions.NumBackEdgeTakenBound; i++) + { + printf("%s", pref); + m_simplAssumptions.BackEdgeTakenBound[i]->Dump(m_comp); + } + + printf("%s\n", m_simplAssumptions.NumBackEdgeTakenBound > 1 ? ")" : ""); + } +#endif +} + +//------------------------------------------------------------------------ +// InitializeCursors: Reset and initialize both cursor lists with information about all +// uses of the specified primary IV. +// +// Parameters: +// primaryIVLcl - Local representing a candidate primary IV for strength reduction +// primaryIV - SCEV for the candidate +// +// Returns: +// True if all uses were analyzed and cursors could be introduced for them +// all; otherwise false. +// +// Remarks: +// A cursor is created for a use when it represents the same value as the +// primary IV passed. The function will allow mismatching uses if the use is +// expected to be removed in the downwards loop transformation. Otherwise the +// function will fail. +// +// It is not a correctness requirement that we remove all uses; if we end up +// not doing so (e.g. because a cursor was not created by this function), +// then we may just end up with extra primary IVs in the loop. +// +bool StrengthReductionContext::InitializeCursors(GenTreeLclVarCommon* primaryIVLcl, ScevAddRec* primaryIV) +{ + m_cursors1.Reset(); + m_cursors2.Reset(); + + auto visitor = [=](BasicBlock* block, Statement* stmt, GenTreeLclVarCommon* tree) { + if (stmt->GetRootNode()->OperIsLocalStore()) + { + GenTreeLclVarCommon* lcl = stmt->GetRootNode()->AsLclVarCommon(); + if ((lcl->GetLclNum() == primaryIVLcl->GetLclNum()) && ((lcl->Data()->gtFlags & GTF_SIDE_EFFECT) == 0)) + { + // Store to the primary IV without side effects; if we end + // up strength reducing, then this store is expected to be + // removed by making the loop downwards counted. + return true; + } + } + + if (!tree->OperIs(GT_LCL_VAR)) + { + return false; + } + + bool isInsideExitTest = + block->KindIs(BBJ_COND) && (stmt == block->lastStmt()) && + (!m_loop->ContainsBlock(block->GetTrueTarget()) || !m_loop->ContainsBlock(block->GetFalseTarget())); + + if (tree->GetSsaNum() != primaryIVLcl->GetSsaNum()) + { + // Most likely a post-incremented use of the primary IV; we + // could replace these as well, but currently we only handle + // the cases where we expect the use to be removed. + return isInsideExitTest; + } + + Scev* iv = m_scevContext.Analyze(block, tree); + if (iv == nullptr) + { + // May not be able to analyze the use if it's mistyped (e.g. + // LCL_VAR(TYP_I_IMPL LclVarDsc)), or an int use of a long + // local. + // Just bail on these cases. + return false; + } + + // If we _did_ manage to analyze it then we expect it to be the same IV + // as the primary IV. + assert(Scev::Equals(m_scevContext.Simplify(iv, m_simplAssumptions), primaryIV)); + + m_cursors1.Emplace(block, stmt, tree, primaryIV, isInsideExitTest); + m_cursors2.Emplace(block, stmt, tree, primaryIV, isInsideExitTest); + return true; + }; + + if (!m_loopLocals.VisitOccurrences(m_loop, primaryIVLcl->GetLclNum(), visitor) || (m_cursors1.Height() <= 0)) + { + JITDUMP(" Could not create cursors for all loop uses of primary IV"); + return false; + } + + JITDUMP(" Found %d cursors using primary IV V%02u\n", m_cursors1.Height(), primaryIVLcl->GetLclNum()); + +#ifdef DEBUG + if (m_comp->verbose) + { + for (int i = 0; i < m_cursors1.Height(); i++) + { + CursorInfo& cursor = m_cursors1.BottomRef(i); + printf(" [%d] [%06u]%s: ", i, Compiler::dspTreeID(cursor.Tree), + cursor.IsInsideExitTest ? " (in-test)" : ""); + cursor.IV->Dump(m_comp); + printf("\n"); + } + } +#endif + + return true; +} + +//------------------------------------------------------------------------ +// AdvanceCursors: Advance cursors stored in "cursors" and store the advanced +// result in "nextCursors". +// +// Parameters: +// cursors - [in] List of current cursors. Unmodified. +// nextCursors - [in, out] List of next cursors. The "Tree" and "IV" fields +// of these cursors will be updated to point to the next derived +// IV. +// +void StrengthReductionContext::AdvanceCursors(ArrayStack* cursors, ArrayStack* nextCursors) +{ + for (int i = 0; i < cursors->Height(); i++) + { + CursorInfo& cursor = cursors->BottomRef(i); + CursorInfo& nextCursor = nextCursors->BottomRef(i); + + assert((nextCursor.Block == cursor.Block) && (nextCursor.Stmt == cursor.Stmt) && + (nextCursor.IsInsideExitTest == cursor.IsInsideExitTest)); + + nextCursor.Tree = cursor.Tree; + do + { + GenTree* cur = nextCursor.Tree; + nextCursor.Tree = cur->gtGetParent(nullptr); + + if ((nextCursor.Tree == nullptr) || + (nextCursor.Tree->OperIs(GT_COMMA) && (nextCursor.Tree->gtGetOp1() == cur))) + { + nextCursor.IV = nullptr; + break; + } + + // TODO-CQ: If this is now the source to a store, we can + // look for uses of the LHS local and add those as cursors + // as well. + Scev* parentIV = m_scevContext.Analyze(nextCursor.Block, nextCursor.Tree); + if (parentIV == nullptr) + { + nextCursor.IV = nullptr; + break; + } + + parentIV = m_scevContext.Simplify(parentIV, m_simplAssumptions); + assert(parentIV != nullptr); + if (!parentIV->OperIs(ScevOper::AddRec)) + { + nextCursor.IV = nullptr; + break; + } + + nextCursor.IV = static_cast(parentIV); + } while (Scev::Equals(nextCursor.IV, cursor.IV)); + } + +#ifdef DEBUG + if (m_comp->verbose) + { + for (int i = 0; i < nextCursors->Height(); i++) + { + CursorInfo& nextCursor = nextCursors->BottomRef(i); + printf(" [%d] [%06u]%s: ", i, nextCursor.Tree == nullptr ? 0 : Compiler::dspTreeID(nextCursor.Tree), + nextCursor.IsInsideExitTest ? " (in-test)" : ""); + if (nextCursor.IV == nullptr) + { + printf(""); + } + else + { + nextCursor.IV->Dump(m_comp); + } + printf("\n"); + } + } +#endif +} + +//------------------------------------------------------------------------ +// CheckAdvancedCursors: Check whether the specified advanced cursors still +// represent a valid set of cursors to introduce a new primary IV for. +// +// Parameters: +// cursors - List of cursors that were advanced. +// derivedLevel - The derived level of the advanced IVs. That is, the number +// of times they are derived from the primary IV. +// nextIV - [out] The next derived IV from the subset of advanced +// cursors to now consider strength reducing. +// +// Returns: +// True if all cursors still represent a common derived IV and would be +// replacable by a new primary IV computing it. +// +// Remarks: +// This function may remove cursors from m_cursors1 and m_cursors2 if it +// decides to no longer consider some cursors for strength reduction. +// +bool StrengthReductionContext::CheckAdvancedCursors(ArrayStack* cursors, + int derivedLevel, + ScevAddRec** nextIV) +{ + *nextIV = nullptr; + + for (int i = 0; i < cursors->Height(); i++) + { + CursorInfo& cursor = cursors->BottomRef(i); + + // Uses inside the exit test only need to opportunistically + // match. We check these after. + if (cursor.IsInsideExitTest) + { + continue; + } + + if ((cursor.IV != nullptr) && ((*nextIV == nullptr) || Scev::Equals(cursor.IV, *nextIV))) + { + *nextIV = cursor.IV; + continue; + } + + JITDUMP(" [%d] does not match; will not advance\n", i); + return false; + } + + // Now check all exit test uses. + for (int i = 0; i < cursors->Height(); i++) + { + CursorInfo& cursor = cursors->BottomRef(i); + + if (!cursor.IsInsideExitTest) + { + continue; + } + + if ((cursor.IV != nullptr) && ((*nextIV == nullptr) || Scev::Equals(cursor.IV, *nextIV))) + { + *nextIV = cursor.IV; + continue; + } + + // Use inside exit test does not match. + if (derivedLevel <= 1) + { + // We weren't able to advance the match in the exit test at all; in + // this situation we expect the downwards optimization to be able + // to remove the use of the primary IV, so this is ok. Remove the + // cursor pointing to the use inside the test. + JITDUMP(" [%d] does not match, but is inside loop test; ignoring mismatch and removing cursor\n", i); + + std::swap(m_cursors1.BottomRef(i), m_cursors1.TopRef(0)); + std::swap(m_cursors2.BottomRef(i), m_cursors2.TopRef(0)); + + m_cursors1.Pop(); + m_cursors2.Pop(); + + i--; + } + else + { + // We already found a derived IV in the exit test that matches, so + // stop here and allow the replacement to replace the uses of the + // current derived IV, including the one in the exit test + // statement. + JITDUMP(" [%d] does not match; will not advance\n", i); + return false; + } + } + + return *nextIV != nullptr; +} + +//------------------------------------------------------------------------ +// TryReplaceUsesWithNewPrimaryIV: Perform final sanity checks before +// introducing a new primary IV and replacing the uses represented by the +// specified cursors with it. +// +// Parameters: +// cursors - List of cursors representing uses to replace +// iv - IV to introduce a primary IV for +// +// Returns: +// True if the IV was introduced and uses were rewritten. +// +bool StrengthReductionContext::TryReplaceUsesWithNewPrimaryIV(ArrayStack* cursors, ScevAddRec* iv) +{ + int64_t stepCns; + if (!iv->Step->GetConstantValue(m_comp, &stepCns)) + { + // For other cases it's non-trivial to know if we can materialize + // the value as IR in the step block. + JITDUMP(" Skipping: step value is not a constant\n"); + return false; + } + + BasicBlock* insertionPoint = FindUpdateInsertionPoint(cursors); + if (insertionPoint == nullptr) + { + JITDUMP(" Skipping: could not find a legal insertion point for the new IV update\n"); + return false; + } + + BasicBlock* preheader = m_loop->EntryEdge(0)->getSourceBlock(); + GenTree* initValue = m_scevContext.Materialize(iv->Start); + if (initValue == nullptr) + { + JITDUMP(" Skipping: init value could not be materialized\n"); + return false; + } + + JITDUMP(" Strength reducing\n"); + + GenTree* stepValue = m_scevContext.Materialize(iv->Step); + assert(stepValue != nullptr); + + unsigned newPrimaryIV = m_comp->lvaGrabTemp(false DEBUGARG("Strength reduced derived IV")); + GenTree* initStore = m_comp->gtNewTempStore(newPrimaryIV, initValue); + Statement* initStmt = m_comp->fgNewStmtFromTree(initStore); + m_comp->fgInsertStmtNearEnd(preheader, initStmt); + + JITDUMP(" Inserting init statement in preheader " FMT_BB "\n", preheader->bbNum); + DISPSTMT(initStmt); + + GenTree* nextValue = + m_comp->gtNewOperNode(GT_ADD, iv->Type, m_comp->gtNewLclVarNode(newPrimaryIV, iv->Type), stepValue); + GenTree* stepStore = m_comp->gtNewTempStore(newPrimaryIV, nextValue); + Statement* stepStmt = m_comp->fgNewStmtFromTree(stepStore); + m_comp->fgInsertStmtNearEnd(insertionPoint, stepStmt); + + JITDUMP(" Inserting step statement in " FMT_BB "\n", insertionPoint->bbNum); + DISPSTMT(stepStmt); + + // Replace uses. + for (int i = 0; i < cursors->Height(); i++) + { + CursorInfo& cursor = cursors->BottomRef(i); + GenTree* newUse = m_comp->gtNewLclVarNode(newPrimaryIV, iv->Type); + + JITDUMP(" Replacing use [%06u] with [%06u]. Before:\n", Compiler::dspTreeID(cursor.Tree), + Compiler::dspTreeID(newUse)); + DISPSTMT(cursor.Stmt); + + GenTree** use = nullptr; + if (cursor.Stmt->GetRootNode() == cursor.Tree) + { + use = cursor.Stmt->GetRootNodePointer(); + } + else + { + cursor.Tree->gtGetParent(&use); + assert(use != nullptr); + } + + GenTree* sideEffects = nullptr; + m_comp->gtExtractSideEffList(cursor.Tree, &sideEffects); + if (sideEffects != nullptr) + { + *use = m_comp->gtNewOperNode(GT_COMMA, newUse->TypeGet(), sideEffects, newUse); + } + else + { + *use = newUse; + } + JITDUMP("\n After:\n\n"); + DISPSTMT(cursor.Stmt); + + m_comp->gtSetStmtInfo(cursor.Stmt); + m_comp->fgSetStmtSeq(cursor.Stmt); + m_comp->gtUpdateStmtSideEffects(cursor.Stmt); + } + + return true; +} + +//------------------------------------------------------------------------ +// FindUpdateInsertionPoint: Find a block at which to insert the "self-update" +// of a new primary IV introduced by strength reduction. +// +// Parameters: +// cursors - The list of cursors pointing to uses that are being replaced by +// the new IV +// +// Returns: +// Basic block; the insertion point is the end (before a potential +// terminator) of this basic block. May return null if no insertion point +// could be found. +// +BasicBlock* StrengthReductionContext::FindUpdateInsertionPoint(ArrayStack* cursors) +{ + // Find insertion point. It needs to post-dominate all uses we are going to + // replace and it needs to dominate all backedges. + // TODO-CQ: Canonicalizing backedges would make this simpler and work in + // more cases. + + BasicBlock* insertionPoint = nullptr; + for (FlowEdge* backEdge : m_loop->BackEdges()) + { + if (insertionPoint == nullptr) + { + insertionPoint = backEdge->getSourceBlock(); + } + else + { + insertionPoint = m_comp->m_domTree->Intersect(insertionPoint, backEdge->getSourceBlock()); + } + } + + while ((insertionPoint != nullptr) && m_loop->ContainsBlock(insertionPoint) && + m_loop->MayExecuteBlockMultipleTimesPerIteration(insertionPoint)) + { + insertionPoint = insertionPoint->bbIDom; + } + + if ((insertionPoint == nullptr) || !m_loop->ContainsBlock(insertionPoint)) + { + return nullptr; + } + + for (int i = 0; i < cursors->Height(); i++) + { + CursorInfo& cursor = cursors->BottomRef(i); + + if (insertionPoint == cursor.Block) + { + if (insertionPoint->HasTerminator() && (cursor.Stmt == insertionPoint->lastStmt())) + { + return nullptr; + } + } + else + { + if (m_comp->optReachable(cursor.Block, m_loop->GetHeader(), insertionPoint)) + { + // Header is reachable without going through the insertion + // point, meaning that the insertion point does not + // post-dominate the use of an IV we want to replace. + // + // TODO-CQ: We only need to check whether the header is + // reachable from inside the loop, which is both cheaper and + // less conservative to check. + // + return nullptr; + } + } + } + + return insertionPoint; +} + //------------------------------------------------------------------------ // optInductionVariables: Try and optimize induction variables in the method. // @@ -1200,6 +1924,13 @@ PhaseStatus Compiler::optInductionVariables() continue; } + StrengthReductionContext strengthReductionContext(this, scevContext, loop, loopLocals); + if (strengthReductionContext.TryStrengthReduce()) + { + Metrics.LoopsStrengthReduced++; + changed = true; + } + if (optMakeLoopDownwardsCounted(scevContext, loop, &loopLocals)) { Metrics.LoopsMadeDownwardsCounted++; @@ -1212,6 +1943,8 @@ PhaseStatus Compiler::optInductionVariables() #if defined(TARGET_XARCH) && defined(TARGET_64BIT) int numWidened = 0; + JITDUMP("Considering primary IVs of " FMT_LP " for widening\n", loop->GetIndex()); + for (Statement* stmt : loop->GetHeader()->Statements()) { if (!stmt->IsPhiDefnStmt()) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index c83fdb48fb3260..50824303d2f783 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -772,6 +772,9 @@ RELEASE_CONFIG_INTEGER(JitEnableCrossBlockLocalAssertionProp, W("JitEnableCrossB // Do greedy RPO-based layout in Compiler::fgReorderBlocks. RELEASE_CONFIG_INTEGER(JitDoReversePostOrderLayout, W("JitDoReversePostOrderLayout"), 1); +// Enable strength reduction +RELEASE_CONFIG_INTEGER(JitEnableStrengthReduction, W("JitEnableStrengthReduction"), 0) + // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the // file, certain other JIT config variables will be active. If the currently compiled function is not in the file, // the specific JIT config variables will not be active. diff --git a/src/coreclr/jit/jitmetadatalist.h b/src/coreclr/jit/jitmetadatalist.h index 10da7249177f88..4aad9abd1b3ccb 100644 --- a/src/coreclr/jit/jitmetadatalist.h +++ b/src/coreclr/jit/jitmetadatalist.h @@ -36,6 +36,7 @@ JITMETADATAMETRIC(LoopsAligned, int, 0) JITMETADATAMETRIC(LoopsIVWidened, int, 0) JITMETADATAMETRIC(WidenedIVs, int, 0) JITMETADATAMETRIC(LoopsMadeDownwardsCounted, int, 0) +JITMETADATAMETRIC(LoopsStrengthReduced, int, 0) JITMETADATAMETRIC(VarsInSsa, int, 0) JITMETADATAMETRIC(HoistedExpressions, int, 0) JITMETADATAMETRIC(RedundantBranchesEliminated, int, JIT_METADATA_HIGHER_IS_BETTER) diff --git a/src/coreclr/jit/scev.cpp b/src/coreclr/jit/scev.cpp index e1c65aa11fbc25..9053d47bbf55d0 100644 --- a/src/coreclr/jit/scev.cpp +++ b/src/coreclr/jit/scev.cpp @@ -220,6 +220,61 @@ bool Scev::IsInvariant() return result != ScevVisit::Abort; } +//------------------------------------------------------------------------ +// Scev::Equals: Check if two SCEV trees are equal. +// +// Parameters: +// left - First scev +// right - Second scev +// +// Returns: +// True if they represent the same value; otherwise false. +// +bool Scev::Equals(Scev* left, Scev* right) +{ + if (left == right) + { + return true; + } + + if ((left->Oper != right->Oper) || (left->Type != right->Type)) + { + return false; + } + + switch (left->Oper) + { + case ScevOper::Constant: + return static_cast(left)->Value == static_cast(right)->Value; + case ScevOper::Local: + { + ScevLocal* leftLocal = static_cast(left); + ScevLocal* rightLocal = static_cast(right); + return (leftLocal->LclNum == rightLocal->LclNum) && (leftLocal->SsaNum == rightLocal->SsaNum); + } + case ScevOper::ZeroExtend: + case ScevOper::SignExtend: + return Scev::Equals(static_cast(left)->Op1, static_cast(right)->Op1); + case ScevOper::Add: + case ScevOper::Mul: + case ScevOper::Lsh: + { + ScevBinop* leftBinop = static_cast(left); + ScevBinop* rightBinop = static_cast(right); + return Scev::Equals(leftBinop->Op1, rightBinop->Op1) && Scev::Equals(leftBinop->Op2, rightBinop->Op2); + } + case ScevOper::AddRec: + { + ScevAddRec* leftAddRec = static_cast(left); + ScevAddRec* rightAddRec = static_cast(right); + return Scev::Equals(leftAddRec->Start, rightAddRec->Start) && + Scev::Equals(leftAddRec->Step, rightAddRec->Step); + } + default: + unreached(); + } +} + //------------------------------------------------------------------------ // ScalarEvolutionContext: Construct an instance of a context to do scalar evolution in. // @@ -312,7 +367,26 @@ ScevUnop* ScalarEvolutionContext::NewExtension(ScevOper oper, var_types targetTy ScevBinop* ScalarEvolutionContext::NewBinop(ScevOper oper, Scev* op1, Scev* op2) { assert((op1 != nullptr) && (op2 != nullptr)); - ScevBinop* binop = new (m_comp, CMK_LoopIVOpts) ScevBinop(oper, op1->Type, op1, op2); + var_types resultType = op1->Type; + if (oper == ScevOper::Add) + { + if (varTypeIsGC(op1->Type)) + { + assert(op2->Type == TYP_I_IMPL); + resultType = TYP_BYREF; + } + else if (varTypeIsGC(op2->Type)) + { + assert(op1->Type == TYP_I_IMPL); + resultType = TYP_BYREF; + } + else + { + assert(op1->Type == op2->Type); + } + } + + ScevBinop* binop = new (m_comp, CMK_LoopIVOpts) ScevBinop(oper, resultType, op1, op2); return binop; } @@ -418,6 +492,12 @@ Scev* ScalarEvolutionContext::AnalyzeNew(BasicBlock* block, GenTree* tree, int d LclVarDsc* dsc = m_comp->lvaGetDesc(tree->AsLclVarCommon()); LclSsaVarDsc* ssaDsc = dsc->GetPerSsaData(tree->AsLclVarCommon()->GetSsaNum()); + if ((tree->TypeGet() != dsc->TypeGet()) || varTypeIsSmall(tree)) + { + // TODO: Truncations (for TYP_INT uses of TYP_LONG locals) and NOL handling? + return nullptr; + } + if ((ssaDsc->GetBlock() == nullptr) || !m_loop->ContainsBlock(ssaDsc->GetBlock())) { return NewLocal(tree->AsLclVarCommon()->GetLclNum(), tree->AsLclVarCommon()->GetSsaNum()); @@ -602,6 +682,15 @@ Scev* ScalarEvolutionContext::AnalyzeNew(BasicBlock* block, GenTree* tree, int d oper = ScevOper::Add; break; case GT_SUB: + if (varTypeIsGC(op2->Type)) + { + // We represent x - y as x + (-1)*y, which does not + // work if y is a GC type. If we wanted to support this + // we would need to add an explicit ScevOper::Sub + // operator. + return nullptr; + } + oper = ScevOper::Add; op2 = NewBinop(ScevOper::Mul, op2, NewConstant(op2->Type, -1)); break; @@ -909,11 +998,14 @@ static T FoldBinop(ScevOper oper, T op1, T op2) } } +const SimplificationAssumptions ScalarEvolutionContext::NoAssumptions; + //------------------------------------------------------------------------ // Simplify: Try to simplify a SCEV node by folding and canonicalization. // // Parameters: -// scev - The node +// scev - The node +// assumptions - Assumptions that the simplification procedure can use. // // Returns: // Simplified node. @@ -925,7 +1017,7 @@ static T FoldBinop(ScevOper oper, T op1, T op2) // Simple unops/binops on constants are folded. Operands are distributed into // add recs whenever possible. // -Scev* ScalarEvolutionContext::Simplify(Scev* scev) +Scev* ScalarEvolutionContext::Simplify(Scev* scev, const SimplificationAssumptions& assumptions) { switch (scev->Oper) { @@ -950,7 +1042,7 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) ScevUnop* unop = (ScevUnop*)scev; assert(genTypeSize(unop->Type) >= genTypeSize(unop->Op1->Type)); - Scev* op1 = Simplify(unop->Op1); + Scev* op1 = Simplify(unop->Op1, assumptions); if (unop->Type == op1->Type) { @@ -966,6 +1058,27 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) : (int64_t)(int32_t)cns->Value); } + if (op1->OperIs(ScevOper::AddRec)) + { + ScevAddRec* addRec = (ScevAddRec*)op1; + + // We need to guarantee that + // ext() = to distribute the extension. + // + // Equivalently this is the case iff + // forall i < backedgeTakenCount, ext(start + step * i) == ext(start) + ext(step) * i. + // + // For zext: we must guarantee that 0 <= start + step * i < 2^32. + // For sext: we must guarantee that -2^31 <= start + step * i < 2^31. + // + if (!AddRecMayOverflow(addRec, unop->OperIs(ScevOper::SignExtend), assumptions)) + { + Scev* newStart = Simplify(NewExtension(unop->Oper, TYP_LONG, addRec->Start), assumptions); + Scev* newStep = Simplify(NewExtension(unop->Oper, TYP_LONG, addRec->Step), assumptions); + return NewAddRec(newStart, newStep); + } + } + return (op1 == unop->Op1) ? unop : NewExtension(unop->Oper, unop->Type, op1); } case ScevOper::Add: @@ -973,8 +1086,8 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) case ScevOper::Lsh: { ScevBinop* binop = (ScevBinop*)scev; - Scev* op1 = Simplify(binop->Op1); - Scev* op2 = Simplify(binop->Op2); + Scev* op1 = Simplify(binop->Op1, assumptions); + Scev* op2 = Simplify(binop->Op2, assumptions); if (binop->OperIs(ScevOper::Add, ScevOper::Mul)) { @@ -995,9 +1108,9 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) // + x => // * x => ScevAddRec* addRec = (ScevAddRec*)op1; - Scev* newStart = Simplify(NewBinop(binop->Oper, addRec->Start, op2)); + Scev* newStart = Simplify(NewBinop(binop->Oper, addRec->Start, op2), assumptions); Scev* newStep = scev->OperIs(ScevOper::Mul, ScevOper::Lsh) - ? Simplify(NewBinop(binop->Oper, addRec->Step, op2)) + ? Simplify(NewBinop(binop->Oper, addRec->Step, op2), assumptions) : addRec->Step; return NewAddRec(newStart, newStep); } @@ -1037,7 +1150,7 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) { ScevBinop* newOp2 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op2, cns2); ScevBinop* newAdd = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op1, newOp2); - return Simplify(newAdd); + return Simplify(newAdd, assumptions); } } @@ -1060,7 +1173,7 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) { ScevBinop* newOp2 = NewBinop(ScevOper::Mul, ((ScevBinop*)op1)->Op2, cns2); ScevBinop* newMul = NewBinop(ScevOper::Mul, ((ScevBinop*)op1)->Op1, newOp2); - return Simplify(newMul); + return Simplify(newMul, assumptions); } } } @@ -1082,7 +1195,7 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) ScevBinop* newOp1 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op1, ((ScevBinop*)op2)->Op1); ScevBinop* newOp2 = NewBinop(ScevOper::Add, ((ScevBinop*)op1)->Op2, ((ScevBinop*)op2)->Op2); ScevBinop* newAdd = NewBinop(ScevOper::Add, newOp1, newOp2); - return Simplify(newAdd); + return Simplify(newAdd, assumptions); } } @@ -1091,8 +1204,8 @@ Scev* ScalarEvolutionContext::Simplify(Scev* scev) case ScevOper::AddRec: { ScevAddRec* addRec = (ScevAddRec*)scev; - Scev* start = Simplify(addRec->Start); - Scev* step = Simplify(addRec->Step); + Scev* start = Simplify(addRec->Start, assumptions); + Scev* step = Simplify(addRec->Step, assumptions); return (start == addRec->Start) && (step == addRec->Step) ? addRec : NewAddRec(start, step); } default: @@ -1119,7 +1232,31 @@ bool ScalarEvolutionContext::Materialize(Scev* scev, bool createIR, GenTree** re case ScevOper::Constant: { ScevConstant* cns = (ScevConstant*)scev; - *resultVN = m_comp->vnStore->VNForGenericCon(scev->Type, reinterpret_cast(&cns->Value)); + if (cns->TypeIs(TYP_REF)) + { + if (cns->Value != 0) + { + // TODO-CQ: Proper handling for handles + return false; + } + + *resultVN = m_comp->vnStore->VNForNull(); + } + else if (cns->TypeIs(TYP_BYREF)) + { + if (cns->Value != 0) + { + // TODO-CQ: Proper handling for handles + return false; + } + + *resultVN = m_comp->vnStore->VNForNull(); + } + else + { + *resultVN = m_comp->vnStore->VNForGenericCon(scev->Type, reinterpret_cast(&cns->Value)); + } + if (createIR) { if (scev->TypeIs(TYP_LONG)) @@ -1485,6 +1622,77 @@ bool ScalarEvolutionContext::MayOverflowBeforeExit(ScevAddRec* lhs, Scev* rhs, V return result != RelopEvaluationResult::True; } +//------------------------------------------------------------------------ +// AddRecMayOverflow: +// Check if an add recurrence may overflow inside the containing loop. +// +// Parameters: +// addRec - The add recurrence +// signedBound - Whether to check using signed (true) or unsigned (false) bounds. +// assumptions - Assumptions about the containing loop. +// +// Returns: +// True if the add recurrence may overflow and wrap around. False if we were +// able to prove that it cannot. +// +// Remarks: +// May return true conservatively. +// +bool ScalarEvolutionContext::AddRecMayOverflow(ScevAddRec* addRec, + bool signedBound, + const SimplificationAssumptions& assumptions) +{ + if (assumptions.NumBackEdgeTakenBound == 0) + { + return true; + } + + if (!addRec->TypeIs(TYP_INT)) + { + return true; + } + + // In general we are interested in proving that the add recurrence does not + // cross the minimum or maximum bounds during the iteration of the loop: + // + // For signed bounds (sext): sext(a + b) != sext(a) + sext(b) if a + b crosses -2^31 or 2^31 - 1. + // For unsigned bounds (zext): zext(a + b) != zext(a) + zext(b) if a + b crosses 0 or 2^32 - 1. + // + // We need to verify this condition for all i < bound where a = start, b = + // step + i. + // + // For now, we only handle the super duper simple case of unsigned bounds + // with addRec = and a TYP_INT bound. + // + if (signedBound) + { + return true; + } + + int64_t startCns; + if (addRec->Start->GetConstantValue(m_comp, &startCns) && (startCns != 0)) + { + return true; + } + + int64_t stepCns; + if (!addRec->Step->GetConstantValue(m_comp, &stepCns) || (stepCns != 1)) + { + return true; + } + + for (unsigned i = 0; i < assumptions.NumBackEdgeTakenBound; i++) + { + Scev* bound = assumptions.BackEdgeTakenBound[i]; + if (bound->TypeIs(TYP_INT)) + { + return false; + } + } + + return true; +} + //------------------------------------------------------------------------ // MapRelopToVNFunc: // Given a potentially unsigned IR relop, map it to a VNFunc. diff --git a/src/coreclr/jit/scev.h b/src/coreclr/jit/scev.h index cc4bcb6cfb4ac7..e965137b90f1a9 100644 --- a/src/coreclr/jit/scev.h +++ b/src/coreclr/jit/scev.h @@ -74,6 +74,8 @@ struct Scev ScevVisit Visit(TVisitor visitor); bool IsInvariant(); + + static bool Equals(Scev* left, Scev* right); }; struct ScevConstant : Scev @@ -201,6 +203,14 @@ enum class RelopEvaluationResult typedef JitHashTable, Scev*> ScalarEvolutionMap; +struct SimplificationAssumptions +{ + // A bound on the number of times a backedge will be taken; the backedge + // taken count is <= min(BackEdgeTakenBound). + Scev** BackEdgeTakenBound = nullptr; + unsigned NumBackEdgeTakenBound = 0; +}; + // Scalar evolution is analyzed in the context of a single loop, and are // computed on-demand by the use of the "Analyze" method on this class, which // also maintains a cache. @@ -230,8 +240,10 @@ class ScalarEvolutionContext VNFunc MapRelopToVNFunc(genTreeOps oper, bool isUnsigned); RelopEvaluationResult EvaluateRelop(ValueNum relop); bool MayOverflowBeforeExit(ScevAddRec* lhs, Scev* rhs, VNFunc exitOp); + bool AddRecMayOverflow(ScevAddRec* addRec, bool signedBound, const SimplificationAssumptions& assumptions); bool Materialize(Scev* scev, bool createIR, GenTree** result, ValueNum* resultVN); + public: ScalarEvolutionContext(Compiler* comp); @@ -244,7 +256,9 @@ class ScalarEvolutionContext ScevAddRec* NewAddRec(Scev* start, Scev* step); Scev* Analyze(BasicBlock* block, GenTree* tree); - Scev* Simplify(Scev* scev); + + static const SimplificationAssumptions NoAssumptions; + Scev* Simplify(Scev* scev, const SimplificationAssumptions& assumptions = NoAssumptions); Scev* ComputeExitNotTakenCount(BasicBlock* exiting); From dd6840d5e579b4a8a77814f0b96e5ace56da4932 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 4 Jul 2024 10:55:06 +0200 Subject: [PATCH 05/72] [browser] default WasmDebugLevel to 0 for tests (#104361) --- eng/testing/tests.browser.targets | 10 ---------- eng/testing/tests.wasi.targets | 10 ---------- eng/testing/tests.wasm.targets | 5 +---- .../System.Collections.Immutable.Tests.csproj | 5 ----- .../tests/System.Memory.Tests.csproj | 5 ----- .../System.Net.Http.Functional.Tests.csproj | 2 +- .../System.Net.WebSockets.Client.Tests.csproj | 2 -- .../tests/System.Private.Xml.Tests.csproj | 2 -- .../System.Text.Json.Tests.csproj | 2 -- src/libraries/sendtohelix-browser.targets | 18 +----------------- src/mono/browser/test-main.js | 2 +- 11 files changed, 4 insertions(+), 59 deletions(-) diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index 3ff4c3b5077631..8ece2d7b4b024b 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -173,16 +173,6 @@ true true - - 0 diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index e8f75ee3821bc5..d7183c26cb5972 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -94,16 +94,6 @@ $(InvariantGlobalization) true - - 0 diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index bf29d930d02f59..ef52bce43743c3 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -15,10 +15,7 @@ But we do want to set it for Configuration=Debug . --> -1 - - reset-to-zero + 0 full true diff --git a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj index c34ddf6a4365f4..cd83bd88de75b9 100644 --- a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj +++ b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj @@ -5,11 +5,6 @@ true - - - 01:15:00 - - diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index a8afce4264197e..319edd9039f8f4 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -6,11 +6,6 @@ $(NetCoreAppCurrent) - - - 01:15:00 - - diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index af5d23586c3020..5447195840f31f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -24,7 +24,7 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - + 01:15:00 diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index ae9109b200f6ba..b321f77aa21f32 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -13,8 +13,6 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - - 01:15:00 1 diff --git a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj index 5ab928104f016b..66bae45f352939 100644 --- a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj @@ -14,8 +14,6 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - - 01:15:00 diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index aed69c98cd0c10..b46a3c542af35c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -23,8 +23,6 @@ $(Features.Replace('nullablePublicOnly', '') - - 01:15:00 1 diff --git a/src/libraries/sendtohelix-browser.targets b/src/libraries/sendtohelix-browser.targets index 231cb656482c5b..e1d0d2a5c93248 100644 --- a/src/libraries/sendtohelix-browser.targets +++ b/src/libraries/sendtohelix-browser.targets @@ -242,16 +242,7 @@ %(Identity) $(HelixCommand) $(_workItemTimeout) - - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 + 01:20:00 @@ -277,13 +268,6 @@ %(Identity) $(HelixCommand) $(_workItemTimeout) - - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 - 01:20:00 diff --git a/src/mono/browser/test-main.js b/src/mono/browser/test-main.js index 96727664e51bf5..4029323393e1ef 100644 --- a/src/mono/browser/test-main.js +++ b/src/mono/browser/test-main.js @@ -368,7 +368,7 @@ async function run() { const main_assembly_name = runArgs.applicationArguments[1]; const app_args = runArgs.applicationArguments.slice(2); const result = await App.runtime.runMain(main_assembly_name, app_args); - console.log(`test-main.js exiting ${app_args.length > 1 ? main_assembly_name + " " + app_args[0] : main_assembly_name} with result ${result}`); + console.log(`test-main.js exiting ${app_args.length > 1 ? main_assembly_name + " " + app_args[0] : main_assembly_name} with result ${result} and linear memory ${App.runtime.Module.HEAPU8.length} bytes`); mono_exit(result); } catch (error) { if (error.name != "ExitStatus") { From 2e585aad5fb0a3c55a7e5f80af9e24f87fa9cfb4 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:36:13 +0000 Subject: [PATCH 06/72] [wasm] Disallow not useful configuration combinations (#104149) * Fix - aot doesn't support managed debugging * Debug + aot on linux should not be tested in this test. --- src/mono/browser/runtime/cwraps.ts | 2 +- .../Wasi.Build.Tests/WasiTemplateTests.cs | 3 ++- .../Blazor/BuildPublishTests.cs | 27 +++++++++++++++---- .../wasm/Wasm.Build.Tests/Blazor/MiscTests.cs | 1 - .../Wasm.Build.Tests/Blazor/SimpleRunTests.cs | 1 - .../Wasm.Build.Tests/BuildPublishTests.cs | 23 +++++++++++++++- .../Templates/WasmTemplateTests.cs | 13 +++++---- src/mono/wasm/build/WasmApp.Common.targets | 4 ++- 8 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 61ae0be40c7686..794682b7e6c292 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -67,7 +67,7 @@ const fn_signatures: SigLine[] = [ [true, "mono_wasm_set_main_args", "void", ["number", "number"]], // These two need to be lazy because they may be missing [() => !runtimeHelpers.emscriptenBuildOptions.enableAotProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]], - [() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]], + [() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_browser", "void", ["string"]], [true, "mono_wasm_profiler_init_browser", "void", ["number"]], [false, "mono_wasm_exec_regression", "number", ["number", "string"]], [false, "mono_wasm_invoke_jsexport", "void", ["number", "number"]], diff --git a/src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs b/src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs index 82b8844c5cb6e0..9b46e29d05fd40 100644 --- a/src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs +++ b/src/mono/wasi/Wasi.Build.Tests/WasiTemplateTests.cs @@ -96,7 +96,8 @@ public void ConsoleBuildThenRunThenPublish(string config, bool singleFileBundle, CreateProject: false, Publish: true, TargetFramework: BuildTestBase.DefaultTargetFramework, - UseCache: false)); + UseCache: false, + ExpectSuccess: !(config == "Debug" && aot))); } [Theory] diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index fc22d03b303bcc..9fe78d3df50726 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -42,17 +42,20 @@ public async Task DefaultTemplate_WithoutWorkload(string config) public static TheoryData TestDataForDefaultTemplate_WithWorkload(bool isAot) { var data = new TheoryData(); - data.Add("Debug", false); - data.Add("Release", false); // Release relinks by default - // [ActiveIssue("https://github.com/dotnet/runtime/issues/83497", TestPlatforms.Windows)] - if (!isAot || !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!isAot) { - data.Add("Debug", true); // for aot:true on Windows, it fails + // AOT does not support managed debugging, is disabled by design + data.Add("Debug", false); } + data.Add("Release", false); // Release relinks by default // [ActiveIssue("https://github.com/dotnet/runtime/issues/83497", TestPlatforms.Windows)] if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + if (!isAot) + { + data.Add("Debug", true); + } data.Add("Release", true); } return data; @@ -197,4 +200,18 @@ public async Task Test_WasmStripILAfterAOT(string stripILAfterAOT, bool expectIL WasmTemplateTests.TestWasmStripILAfterAOTOutput(objBuildDir, frameworkDir, expectILStripping, _testOutput); } + + [Theory] + [InlineData("Debug")] + public void BlazorWasm_CannotAOT_InDebug(string config) + { + string id = $"blazorwasm_{config}_aot_{GetRandomId()}"; + CreateBlazorWasmTemplateProject(id); + AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"), + extraItems: null, + extraProperties: "true"); + + (CommandResult res, _) = BlazorPublish(new BlazorBuildOptions(id, config, ExpectSuccess: false)); + Assert.Contains("AOT is not supported in debug configuration", res.Output); + } } diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs index e01a9f4f3e6428..c93ec2c6af452f 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs @@ -53,7 +53,6 @@ public void NativeBuild_WithDeployOnBuild_UsedByVS(string config, bool nativeRel } [Theory] - [InlineData("Debug")] [InlineData("Release")] public void DefaultTemplate_AOT_InProjectFile(string config) { diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs index fdce8ea756f844..433d77699c1c76 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleRunTests.cs @@ -78,7 +78,6 @@ public async Task BlazorBuildAndRunForDifferentOutputPaths(string config, bool a [Theory] [InlineData("Debug", false)] - [InlineData("Debug", true)] [InlineData("Release", false)] [InlineData("Release", true)] public async Task BlazorPublishRunTest(string config, bool aot) diff --git a/src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs index f287fa4574164a..31807b6d54bb7b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/BuildPublishTests.cs @@ -21,6 +21,28 @@ public BuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur { } + [Theory] + [BuildAndRun(host: RunHost.Chrome, aot: true, config: "Debug")] + public void Wasm_CannotAOT_InDebug(BuildArgs buildArgs, RunHost _, string id) + { + string projectName = GetTestProjectPath(prefix: "no_aot_in_debug", config: buildArgs.Config); + buildArgs = buildArgs with { ProjectName = projectName }; + buildArgs = ExpandBuildArgs(buildArgs); + (string projectDir, string buildOutput) = BuildProject(buildArgs, + id: id, + new BuildProjectOptions( + InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + DotnetWasmFromRuntimePack: true, + CreateProject: true, + Publish: true, + ExpectSuccess: false + )); + + Console.WriteLine($"buildOutput={buildOutput}"); + + Assert.Contains("AOT is not supported in debug configuration", buildOutput); + } + [Theory] [BuildAndRun(host: RunHost.Chrome, aot: false, config: "Release")] [BuildAndRun(host: RunHost.Chrome, aot: false, config: "Debug")] @@ -71,7 +93,6 @@ void Run() => RunAndTestWasmApp( [Theory] [BuildAndRun(host: RunHost.Chrome, aot: true, config: "Release")] - [BuildAndRun(host: RunHost.Chrome, aot: true, config: "Debug")] public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id) { bool testUnicode = true; diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index ac20f6f784513e..a17dc522988d23 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -21,7 +21,7 @@ public WasmTemplateTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur { } - private string StringReplaceWithAssert(string oldContent, string oldValue, string newValue) + private string StringReplaceWithAssert(string oldContent, string oldValue, string newValue) { string newContent = oldContent.Replace(oldValue, newValue); if (oldValue != newValue && oldContent == newContent) @@ -57,11 +57,11 @@ private void UpdateConsoleProgramCs() private void UpdateBrowserMainJs(string targetFramework, string runtimeAssetsRelativePath = DefaultRuntimeAssetsRelativePath) { base.UpdateBrowserMainJs( - (mainJsContent) => + (mainJsContent) => { // .withExitOnUnhandledError() is available only only >net7.0 mainJsContent = StringReplaceWithAssert( - mainJsContent, + mainJsContent, ".create()", (targetFramework == "net8.0" || targetFramework == "net9.0") ? ".withConsoleForwarding().withElementOnExit().withExitCodeLogging().withExitOnUnhandledError().create()" @@ -75,8 +75,8 @@ private void UpdateBrowserMainJs(string targetFramework, string runtimeAssetsRel mainJsContent = StringReplaceWithAssert(mainJsContent, "from './_framework/dotnet.js'", $"from '{runtimeAssetsRelativePath}dotnet.js'"); return mainJsContent; - }, - targetFramework, + }, + targetFramework, runtimeAssetsRelativePath ); } @@ -121,7 +121,7 @@ public void BrowserBuildThenPublish(string config) var buildArgs = new BuildArgs(projectName, config, false, id, null); - AddItemsPropertiesToProject(projectFile, + AddItemsPropertiesToProject(projectFile, atTheEnd: """ @@ -389,7 +389,6 @@ public static TheoryData TestDataForConsolePublishAndRun() // [ActiveIssue("https://github.com/dotnet/runtime/issues/71887", TestPlatforms.Windows)] if (!OperatingSystem.IsWindows()) { - data.Add("Debug", true, false); data.Add("Release", true, false); } diff --git a/src/mono/wasm/build/WasmApp.Common.targets b/src/mono/wasm/build/WasmApp.Common.targets index 6aafca7d110a2f..beccccae461df1 100644 --- a/src/mono/wasm/build/WasmApp.Common.targets +++ b/src/mono/wasm/build/WasmApp.Common.targets @@ -247,7 +247,7 @@ - + <_WasmDebuggerSupport Condition="'$(WasmDebugLevel)' != '' and '$(WasmDebugLevel)' != '0'">true <_WasmDebuggerSupport Condition="'$(WasmDebugLevel)' != '' and '$(WasmDebugLevel)' == '0'">false @@ -621,6 +621,8 @@ + From 2a72651144ccc127284a0e1aa8c78dd7705adcc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 4 Jul 2024 14:34:18 +0200 Subject: [PATCH 07/72] [wasi] Implement TODO in mono-threads-wasi.S (#101337) --- src/mono/mono/utils/CMakeLists.txt | 6 +- src/mono/mono/utils/mono-threads-wasi.S | 15 ----- src/mono/mono/utils/mono-threads-wasm.S | 17 ++++++ src/mono/mono/utils/mono-threads-wasm.c | 73 ++++++++----------------- 4 files changed, 43 insertions(+), 68 deletions(-) delete mode 100644 src/mono/mono/utils/mono-threads-wasi.S create mode 100644 src/mono/mono/utils/mono-threads-wasm.S diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index e5bd089b8203c7..ef271dcb830a9d 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -17,7 +17,7 @@ if(HOST_WIN32 AND HOST_AMD64) set(CMAKE_ASM_MASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "") list(APPEND utils_win32_sources win64.asm) -elseif(HOST_WASI) +elseif(HOST_WASM) set (CMAKE_ASM_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}") set (CMAKE_ASM_COMPILER_TARGET "${CMAKE_C_COMPILER_TARGET}") enable_language(ASM) @@ -30,8 +30,8 @@ set(utils_unix_sources if(HOST_WIN32) set(utils_platform_sources ${utils_win32_sources}) -elseif(HOST_WASI) - set(utils_platform_sources ${utils_unix_sources} mono-threads-wasi.S) +elseif(HOST_WASM) + set(utils_platform_sources ${utils_unix_sources} mono-threads-wasm.S) else() set(utils_platform_sources ${utils_unix_sources}) endif() diff --git a/src/mono/mono/utils/mono-threads-wasi.S b/src/mono/mono/utils/mono-threads-wasi.S deleted file mode 100644 index 6de64294456183..00000000000000 --- a/src/mono/mono/utils/mono-threads-wasi.S +++ /dev/null @@ -1,15 +0,0 @@ -// TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b -// use __stack_low && __stack_high - -.globl get_wasm_data_end -.globl get_wasm_heap_base - -get_wasm_data_end: - .functype get_wasm_data_end () -> (i32) - global.get __data_end@GOT - end_function - -get_wasm_heap_base: - .functype get_wasm_heap_base () -> (i32) - global.get __heap_base@GOT - end_function diff --git a/src/mono/mono/utils/mono-threads-wasm.S b/src/mono/mono/utils/mono-threads-wasm.S new file mode 100644 index 00000000000000..8752b03f124b78 --- /dev/null +++ b/src/mono/mono/utils/mono-threads-wasm.S @@ -0,0 +1,17 @@ +.globl get_wasm_stack_low +.globl get_wasm_stack_high + +get_wasm_stack_low: + .functype get_wasm_stack_low () -> (i32) + global.get __stack_low@GOT + // align up to 16 bytes + i32.const 0xF + i32.add + i32.const -0x10 + i32.and + end_function + +get_wasm_stack_high: + .functype get_wasm_stack_high () -> (i32) + global.get __stack_high@GOT + end_function diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index 597592a7966c66..c1e6d9144e7b30 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -20,74 +20,46 @@ #include #include -#include #ifndef DISABLE_THREADS #include #include #endif +#endif -#define round_down(addr, val) ((void*)((addr) & ~((val) - 1))) - -EMSCRIPTEN_KEEPALIVE -static int -wasm_get_stack_base (void) -{ - // wasm-mt: add MONO_ENTER_GC_UNSAFE / MONO_EXIT_GC_UNSAFE if this function becomes more complex - return emscripten_stack_get_end (); -} - -EMSCRIPTEN_KEEPALIVE -static int -wasm_get_stack_size (void) -{ - // wasm-mt: add MONO_ENTER_GC_UNSAFE / MONO_EXIT_GC_UNSAFE if this function becomes more complex - return (guint8*)emscripten_stack_get_base () - (guint8*)emscripten_stack_get_end (); -} - -#else /* HOST_BROWSER -> WASI */ - -// TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b -// use __stack_low && __stack_high -// see mono-threads-wasi.S -uintptr_t get_wasm_heap_base(void); -uintptr_t get_wasm_data_end(void); +uintptr_t get_wasm_stack_high(void); +uintptr_t get_wasm_stack_low(void); static int wasm_get_stack_size (void) { +#if defined(HOST_WASI) && !defined(DISABLE_THREADS) + // TODO: this will need changes for WASI multithreading as the stack will be allocated per thread at different addresses + g_assert_not_reached (); +#else /* * | -- increasing address ---> | - * | data (data_end)| stack |(heap_base) heap | + * | data |(stack low) stack (stack high)| heap | */ - size_t heap_base = get_wasm_heap_base(); - size_t data_end = get_wasm_data_end(); - size_t max_stack_size = heap_base - data_end; - - g_assert (data_end > 0); - g_assert (heap_base > data_end); + size_t stack_high = get_wasm_stack_high(); + size_t stack_low = get_wasm_stack_low(); + size_t max_stack_size = stack_high - stack_low; - // this is the max available stack size size, - // return a 16-byte aligned smaller size - return max_stack_size & ~0xF; -} + g_assert (stack_low >= 0); + g_assert (stack_high > stack_low); + g_assert (max_stack_size >= 64 * 1024); -static int -wasm_get_stack_base (void) -{ - return get_wasm_data_end(); - // this will need further change for multithreading as the stack will allocated be per thread at different addresses + // this is the max available stack size size + return max_stack_size; +#endif } -#endif /* HOST_BROWSER */ - int mono_thread_info_get_system_max_stack_size (void) { return wasm_get_stack_size (); } - void mono_threads_suspend_init_signals (void) { @@ -226,12 +198,13 @@ mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize) if (G_UNLIKELY (res != 0)) g_error ("%s: pthread_attr_destroy failed with \"%s\" (%d)", __func__, g_strerror (res), res); - if (*staddr == NULL) { - *staddr = (guint8*)wasm_get_stack_base (); - *stsize = wasm_get_stack_size (); - } + g_assert (*staddr != NULL); + g_assert (*stsize != (size_t)-1); +#elif defined(HOST_WASI) && !defined(DISABLE_THREADS) + // TODO: this will need changes for WASI multithreading as the stack will be allocated per thread at different addresses + g_assert_not_reached (); #else - *staddr = (guint8*)wasm_get_stack_base (); + *staddr = (guint8*)get_wasm_stack_low (); *stsize = wasm_get_stack_size (); #endif From 5b86dca8e75627d1f5ad77f1dcffafa96e4f5049 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Thu, 4 Jul 2024 06:47:50 -0700 Subject: [PATCH 08/72] Revert "Migrate to zlib-ng, part 2: consume it in runtime (#102403)" (#104414) This reverts commit f5c9a5e8f5ab28c1e3e49c4ecc047b4368a63d27. --- Directory.Build.props | 1 - eng/native/configurecompiler.cmake | 9 +-- eng/native/configureplatform.cmake | 6 -- eng/native/functions.cmake | 4 +- .../Microsoft.NETCore.Native.Unix.targets | 4 +- .../Microsoft.NETCore.Native.Windows.targets | 1 - .../Directory.Build.props | 2 - src/mono/CMakeLists.txt | 23 +++--- src/mono/browser/browser.proj | 1 - src/mono/browser/runtime/CMakeLists.txt | 1 - src/mono/cmake/config.h.in | 3 + src/mono/mono.proj | 3 - src/mono/mono/eventpipe/test/CMakeLists.txt | 11 +-- src/mono/mono/metadata/CMakeLists.txt | 18 +---- src/mono/mono/metadata/debug-mono-ppdb.c | 6 +- src/mono/mono/mini/CMakeLists.txt | 77 ++++--------------- src/mono/mono/profiler/CMakeLists.txt | 10 +-- src/mono/mono/profiler/log.c | 6 +- src/mono/mono/profiler/mprof-report.c | 6 +- .../msbuild/apple/build/AppleBuild.targets | 3 +- src/mono/wasi/runtime/CMakeLists.txt | 1 - src/mono/wasi/wasi.proj | 1 - src/native/external/zlib-intel.cmake | 29 +++++++ src/native/external/zlib-ng.cmake | 33 -------- src/native/external/zlib.cmake | 41 ++++++++++ src/native/libs/Common/pal_utilities.h | 6 +- .../CMakeLists.txt | 60 +++++---------- .../extra_libs.cmake | 15 ++-- .../System.IO.Compression.Native/pal_zlib.c | 19 ++--- .../System.IO.Compression.Native/pal_zlib.h | 18 ++--- .../zlib_allocator.h | 8 -- .../zlib_allocator_unix.c | 11 +-- .../zlib_allocator_win.c | 13 ++-- 33 files changed, 183 insertions(+), 267 deletions(-) create mode 100644 src/native/external/zlib-intel.cmake delete mode 100644 src/native/external/zlib-ng.cmake create mode 100644 src/native/external/zlib.cmake delete mode 100644 src/native/libs/System.IO.Compression.Native/zlib_allocator.h diff --git a/Directory.Build.props b/Directory.Build.props index 61edda987ed6d5..3e494e19c3d9e5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -324,7 +324,6 @@ true ClrFullNativeBuild;ClrRuntimeSubset;ClrJitSubset;ClrPalTestsSubset;ClrAllJitsSubset;ClrILToolsSubset;ClrNativeAotSubset;ClrSpmiSubset;ClrCrossComponentsSubset;ClrDebugSubset;HostArchitecture;PgoInstrument;NativeOptimizationDataSupported;CMakeArgs;CxxStandardLibrary;CxxStandardLibraryStatic;CxxAbiLibrary - true diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index aedc4825273003..76c441de6f2f56 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -12,11 +12,6 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# We need to set this to Release as there's no way to intercept configuration-specific linker flags -# for try_compile-style tests (like check_c_source_compiles) and some of the default Debug flags -# (ie. /INCREMENTAL) conflict with our own flags. -set(CMAKE_TRY_COMPILE_CONFIGURATION Release) - include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckLinkerFlag) @@ -63,7 +58,6 @@ if (MSVC) define_property(TARGET PROPERTY CLR_CONTROL_FLOW_GUARD INHERITED BRIEF_DOCS "Controls the /guard:cf flag presence" FULL_DOCS "Set this property to ON or OFF to indicate if the /guard:cf compiler and linker flag should be present") define_property(TARGET PROPERTY CLR_EH_CONTINUATION INHERITED BRIEF_DOCS "Controls the /guard:ehcont flag presence" FULL_DOCS "Set this property to ON or OFF to indicate if the /guard:ehcont compiler flag should be present") define_property(TARGET PROPERTY CLR_EH_OPTION INHERITED BRIEF_DOCS "Defines the value of the /EH option" FULL_DOCS "Set this property to one of the valid /EHxx options (/EHa, /EHsc, /EHa-, ...)") - define_property(TARGET PROPERTY MSVC_WARNING_LEVEL INHERITED BRIEF_DOCS "Define the warning level for the /Wn option" FULL_DOCS "Set this property to one of the valid /Wn options (/W0, /W1, /W2, /W3, /W4)") set_property(GLOBAL PROPERTY CLR_CONTROL_FLOW_GUARD ON) @@ -785,8 +779,7 @@ if (MSVC) # [[! Microsoft.Security.SystemsADM.10086 !]] - SDL required warnings # set default warning level to 4 but allow targets to override it. - set_property(GLOBAL PROPERTY MSVC_WARNING_LEVEL 4) - add_compile_options($<$:/W$>) + add_compile_options($<$:/W$>,$,4>>>) add_compile_options($<$:/WX>) # treat warnings as errors add_compile_options($<$:/Oi>) # enable intrinsics add_compile_options($<$:/Oy->) # disable suppressing of the creation of frame pointers on the call stack for quicker function calls diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index 1dfc2458d11e8c..20851f8617423d 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -500,9 +500,3 @@ if(LOWERCASE_CMAKE_BUILD_TYPE STREQUAL debug) string(REPLACE "-D_FORTIFY_SOURCE=2 " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE "-D_FORTIFY_SOURCE=2 " "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") endif() - -if (CLR_CMAKE_TARGET_ANDROID OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS OR CLR_CMAKE_HOST_ARCH_ARMV6) - # Our zlib-ng copy should only be used in non-mobile platforms only. - # In mobile platforms we should use the zlib package provided by the system. - set(CLR_CMAKE_USE_SYSTEM_ZLIB 1) -endif() diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake index b1cb09d6620436..6629e926afacf6 100644 --- a/eng/native/functions.cmake +++ b/eng/native/functions.cmake @@ -221,7 +221,7 @@ function(preprocess_file inputFilename outputFilename) get_compile_definitions(PREPROCESS_DEFINITIONS) get_include_directories(PREPROCESS_INCLUDE_DIRECTORIES) get_source_file_property(SOURCE_FILE_DEFINITIONS ${inputFilename} COMPILE_DEFINITIONS) - + foreach(DEFINITION IN LISTS SOURCE_FILE_DEFINITIONS) list(APPEND PREPROCESS_DEFINITIONS -D${DEFINITION}) endforeach() @@ -508,7 +508,7 @@ function(install_static_library targetName destination component) if (WIN32) set_target_properties(${targetName} PROPERTIES COMPILE_PDB_NAME "${targetName}" - COMPILE_PDB_OUTPUT_DIRECTORY "$" + COMPILE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}" ) install (FILES "$/${targetName}.pdb" DESTINATION ${destination} COMPONENT ${component}) endif() diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 3ff044e94d9d2a..d3dc85e04f82e4 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -24,7 +24,6 @@ The .NET Foundation licenses this file to you under the MIT license. lld bfd 1572864 - true @@ -130,7 +129,6 @@ The .NET Foundation licenses this file to you under the MIT license. - @@ -190,7 +188,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets index 7be88b5fde9912..8035484bf04a53 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets @@ -46,7 +46,6 @@ The .NET Foundation licenses this file to you under the MIT license. - diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 70c526efac38cc..0de334e7ff2113 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -277,8 +277,6 @@ - - diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index fac1d9fb0e33ca..ba41fa4d9a489f 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -249,6 +249,7 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "emscripten") set(DISABLE_SHARED_LIBS 1) # sys/random.h exists, but its not found set(HAVE_SYS_RANDOM_H 1) + set(INTERNAL_ZLIB 1) elseif(CLR_CMAKE_HOST_OS STREQUAL "wasi") set(HOST_WASI 1) add_definitions(-D_WASI_EMULATED_PROCESS_CLOCKS -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -DHOST_WASI) @@ -260,6 +261,7 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "wasi") add_compile_options(-Wno-unused-but-set-variable) set(ENABLE_PERFTRACING 0) set(DISABLE_SHARED_LIBS 1) + set(INTERNAL_ZLIB 1) set(DISABLE_EXECUTABLES 1) set(STATIC_COMPONENTS 1) elseif(CLR_CMAKE_HOST_OS STREQUAL "windows") @@ -268,6 +270,7 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "windows") set(HOST_NO_SYMLINKS 1) set(MONO_KEYWORD_THREAD "__declspec (thread)") set(MONO_ZERO_LEN_ARRAY 1) + set(INTERNAL_ZLIB 1) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # statically link VC runtime library add_compile_options($<$:/W4>) # set warning level 4 add_compile_options($<$:/WX>) # treat warnings as errors @@ -508,15 +511,16 @@ if ((HOST_MACCAT AND HOST_ARM64) OR (TARGET_MACCAT AND TARGET_ARM64)) set(TARGET_APPLE_MOBILE 1) endif() -# Decide if we need zlib-ng. +# Decide if we need zlib, and if so whether we want the system zlib or the in-tree copy. if(NOT DISABLE_EMBEDDED_PDB OR NOT DISABLE_LOG_PROFILER_GZ) - if (CLR_CMAKE_USE_SYSTEM_ZLIB) - # if we're not on a platform where we use the in-tree zlib-ng, require system zlib + if(INTERNAL_ZLIB) + # defines ZLIB_SOURCES + include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) + else() + # if we're not on a platform where we use the in-tree zlib, require system zlib include(${CLR_SRC_NATIVE_DIR}/libs/System.IO.Compression.Native/extra_libs.cmake) set(Z_LIBS) append_extra_compression_libs(Z_LIBS) - else() - include(${CLR_SRC_NATIVE_DIR}/external/zlib-ng.cmake) endif() endif() @@ -660,17 +664,12 @@ if(LLVM_PREFIX) endif() set(llvm_includedir "${LLVM_PREFIX}/include") - set(llvm_system_libs "") if(HOST_LINUX) # llvm-config --system-libs - list(APPEND llvm_system_libs ${MONO_cxx_lib} "-lrt" "-ldl" "-lpthread" "-lm") + set(llvm_system_libs ${MONO_cxx_lib} "-lz" "-lrt" "-ldl" "-lpthread" "-lm") elseif(HOST_OSX) # llvm-config --system-libs - list(APPEND llvm_system_libs "-lm") - endif() - - if (CLR_CMAKE_USE_SYSTEM_ZLIB AND (HOST_LINUX OR HOST_OSX)) - list(APPEND llvm_system_libs "-lz") + set(llvm_system_libs "-lz" "-lm") endif() # llvm-config --libs analysis core bitwriter mcjit orcjit diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 47a351fb53b415..81768d43e6a536 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -63,7 +63,6 @@ - diff --git a/src/mono/browser/runtime/CMakeLists.txt b/src/mono/browser/runtime/CMakeLists.txt index f707431fc38d55..8b7939330f8c3a 100644 --- a/src/mono/browser/runtime/CMakeLists.txt +++ b/src/mono/browser/runtime/CMakeLists.txt @@ -28,7 +28,6 @@ target_link_libraries(dotnet.native ${MONO_ARTIFACTS_DIR}/libmono-wasm-${CONFIGURATION_INTERPSIMDTABLES_LIB}.a ${MONO_ARTIFACTS_DIR}/libmono-profiler-aot.a ${MONO_ARTIFACTS_DIR}/libmono-profiler-browser.a - ${MONO_ARTIFACTS_DIR}/libz.a ${NATIVE_BIN_DIR}/wasm-bundled-timezones.a ${NATIVE_BIN_DIR}/libSystem.Native.a ${NATIVE_BIN_DIR}/libSystem.Globalization.Native.a diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 8ac0c0f7c3c290..e972500d1cd81d 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -168,6 +168,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNWIND_H 1 +/* Use in-tree zlib */ +#cmakedefine INTERNAL_ZLIB 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_POLL_H 1 diff --git a/src/mono/mono.proj b/src/mono/mono.proj index ceb783599086e5..14056eb3a7e24e 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -1189,9 +1189,6 @@ JS_ENGINES = [NODE_JS] <_MonoRuntimeArtifacts Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-wasm-nosimd.a"> $(RuntimeBinDir)libmono-wasm-nosimd.a - <_MonoRuntimeArtifacts Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)_deps\fetchzlibng-build\libz.a"> - $(RuntimeBinDir)libz.a - <_MonoICorDebugArtifacts Condition="'$(MonoMsCorDbi)' == 'true'" Include="$(MonoObjDir)out\lib\$(LibPrefix)mscordbi$(LibSuffix)"> $(RuntimeBinDir)$(LibPrefix)mscordbi$(LibSuffix) diff --git a/src/mono/mono/eventpipe/test/CMakeLists.txt b/src/mono/mono/eventpipe/test/CMakeLists.txt index 92ddfb3ce17f96..d313e630adaaba 100644 --- a/src/mono/mono/eventpipe/test/CMakeLists.txt +++ b/src/mono/mono/eventpipe/test/CMakeLists.txt @@ -39,16 +39,7 @@ if(ENABLE_PERFTRACING) set(CMAKE_SKIP_RPATH 1) add_executable(ep-test ${EVENTPIPE_TEST_SOURCES} ${EVENTPIPE_TEST_HEADERS}) target_sources(ep-test PRIVATE "${mono-components-objects}") - - set(EPTEST_LINKABLE_LIBS "") - list(APPEND EPTEST_LINKABLE_LIBS eglib_api monosgen-static ${OS_LIBS} ${LLVM_LIBS} monoapi) - if (CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND EPTEST_LINKABLE_LIBS ${EPTEST_LINKABLE_LIBS} ${Z_LIBS}) - else() - list(APPEND EPTEST_LINKABLE_LIBS ${EPTEST_LINKABLE_LIBS} zlib) - endif() - target_link_libraries(ep-test PRIVATE ${EPTEST_LINKABLE_LIBS}) - + target_link_libraries(ep-test PRIVATE eglib_api monosgen-static ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS} monoapi) install_with_stripped_symbols(ep-test TARGETS bin) else(ENABLE_EVENTPIPE_TEST AND STATIC_COMPONENTS AND (NOT DISABLE_COMPONENTS) AND (NOT DISABLE_LIBS) AND (NOT DISABLE_EXECUTABLES)) message(VERBOSE "Skip building native EventPipe library test runner.") diff --git a/src/mono/mono/metadata/CMakeLists.txt b/src/mono/mono/metadata/CMakeLists.txt index e3463b514d0c0f..773381d6c1c9ad 100644 --- a/src/mono/mono/metadata/CMakeLists.txt +++ b/src/mono/mono/metadata/CMakeLists.txt @@ -200,14 +200,7 @@ set(metadata_sources "${metadata_platform_sources};${metadata_common_sources};${ if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) add_library(metadata_objects_shared OBJECT ${metadata_sources}) target_compile_definitions(metadata_objects_shared PRIVATE ${metadata_compile_definitions}) - - set(METADATAOBJECTSSHARED_LINKABLE_LIBS "") - list(APPEND METADATAOBJECTSSHARED_LINKABLE_LIBS monoapi eglib_api utils_objects_shared) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND METADATAOBJECTSSHARED_LINKABLE_LIBS zlib) - endif() - target_link_libraries(metadata_objects_shared PRIVATE ${METADATAOBJECTSSHARED_LINKABLE_LIBS}) - + target_link_libraries(metadata_objects_shared PRIVATE monoapi eglib_api utils_objects_shared) # note: metadata_objects is an object library, so this doesn't force linking with sgen, # it just adds the relevant include directories - which we need even with Boehm target_link_libraries(metadata_objects_shared PRIVATE sgen_objects_shared) @@ -219,14 +212,7 @@ endif() add_library(metadata_objects OBJECT ${metadata_sources}) target_compile_definitions(metadata_objects PRIVATE ${metadata_compile_definitions}) - -set(METADATAOBJECTS_LINKABLE_LIBS "") -list(APPEND METADATAOBJECTS_LINKABLE_LIBS monoapi eglib_api utils_objects) -if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND METADATAOBJECTS_LINKABLE_LIBS zlib) -endif() -target_link_libraries(metadata_objects PRIVATE ${METADATAOBJECTS_LINKABLE_LIBS}) - +target_link_libraries(metadata_objects PRIVATE monoapi eglib_api utils_objects) # note: metadata_objects is an object library, so this doesn't force linking with sgen, # it just adds the relevant include directories - which we need even with Boehm target_link_libraries(metadata_objects PRIVATE sgen_objects) diff --git a/src/mono/mono/metadata/debug-mono-ppdb.c b/src/mono/mono/metadata/debug-mono-ppdb.c index 3ed40e9243dad3..bcbbb4c7f2be03 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.c +++ b/src/mono/mono/metadata/debug-mono-ppdb.c @@ -30,8 +30,12 @@ #include #ifndef DISABLE_EMBEDDED_PDB +#ifdef INTERNAL_ZLIB +#include +#else #include -#endif // DISABLE_EMBEDDED_PDB +#endif +#endif #include "debug-mono-ppdb.h" diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index af9987869f5ac2..93ff6c431ef03b 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -314,7 +314,10 @@ elseif(NOT HOST_BROWSER AND NOT HOST_WASI) set(mini_sources "${mini_sources};${VERSION_FILE_PATH}") # this is generated by GenerateNativeVersionFile in Arcade endif() -set(monosgen-sources "${mini_sources}") +set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") +set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") + +set(monosgen-sources "${mini_sources};${ZLIB_SOURCES}") if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) add_library(monosgen-objects_shared OBJECT "${monosgen-sources}") @@ -323,14 +326,7 @@ if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) endif() add_library(monosgen-objects OBJECT "${monosgen-sources}") - -set(MONOSGEN_OBJECTS_LINKABLE_LIBS "") -list(APPEND MONOSGEN_OBJECTS_LINKABLE_LIBS monoapi eglib_api utils_objects sgen_objects metadata_objects) -if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND MONOSGEN_OBJECTS_LINKABLE_LIBS zlib) -endif() -target_link_libraries (monosgen-objects PRIVATE ${MONOSGEN_OBJECTS_LINKABLE_LIBS}) - +target_link_libraries (monosgen-objects PRIVATE monoapi eglib_api utils_objects sgen_objects metadata_objects) if(NOT HOST_WIN32) target_compile_definitions(monosgen-objects PRIVATE -DMONO_DLL_EXPORT) endif() @@ -357,23 +353,15 @@ if(NOT DISABLE_SHARED_LIBS) # musl-libc implements ucontext in a different library on s390x if(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) target_link_libraries(monosgen-shared PRIVATE ucontext) - endif() + endif(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) set_target_properties(monosgen-shared PROPERTIES OUTPUT_NAME ${MONO_SHARED_LIB_NAME}) if(MONO_SET_RPATH_ORIGIN) set_target_properties(monosgen-shared PROPERTIES INSTALL_RPATH "$ORIGIN") endif() - - set(MONOSGENSHARED_LINKABLE_LIBS "") - list(APPEND MONOSGENSHARED_LINKABLE_LIBS monoapi eglib_objects dn-containers) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND MONOSGENSHARED_LINKABLE_LIBS zlib) - endif() if(HOST_WIN32) - list(APPEND MONOSGENSHARED_LINKABLE_LIBS utils_objects_shared sgen_objects_shared metadata_objects_shared) - target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_LIBS}) + target_link_libraries(monosgen-shared PRIVATE monoapi eglib_objects utils_objects_shared sgen_objects_shared metadata_objects_shared dn-containers) else() - list(APPEND MONOSGENSHARED_LINKABLE_LIBS utils_objects sgen_objects metadata_objects) - target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_LIBS}) + target_link_libraries(monosgen-shared PRIVATE monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) endif() target_include_directories (monosgen-shared PRIVATE monoapi) if(TARGET_WIN32) @@ -381,16 +369,11 @@ if(NOT DISABLE_SHARED_LIBS) # to avoid a conflict we rename the import library with the .import.lib suffix set_target_properties(monosgen-shared PROPERTIES IMPORT_SUFFIX ".import.lib") endif() - - set(MONOSGENSHARED_LINKABLE_EXTRALIBS "") - list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) - if (CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS ${Z_LIBS}) + if(CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + target_link_libraries(monosgen-shared PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) else() - list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS zlib) + target_link_libraries(monosgen-shared PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) endif() - target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_EXTRALIBS}) - if(TARGET_DARWIN) set_property(TARGET monosgen-shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0") endif() @@ -434,22 +417,8 @@ if(NOT DISABLE_SHARED_LIBS) endif() add_library(${frameworkconfig} SHARED $) target_compile_definitions(${frameworkconfig} PRIVATE -DMONO_DLL_EXPORT) - - set(FRAMEWORKCONFIG_LINKABLE_LIBS "") - list(APPEND FRAMEWORKCONFIG_LINKABLE_LIBS monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND FRAMEWORKCONFIG_LINKABLE_LIBS zlib) - endif() - target_link_libraries(${frameworkconfig} PRIVATE ${FRAMEWORKCONFIG_LINKABLE_LIBS}) - - set(FRAMEWORKCONFIG_LINKABLE_EXTRALIBS "") - list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS zlib) - else() - list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS ${Z_LIBS}) - endif() - target_link_libraries(${frameworkconfig} PRIVATE ${FRAMEWORKCONFIG_LINKABLE_EXTRALIBS}) + target_link_libraries(${frameworkconfig} PRIVATE monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) + target_link_libraries(${frameworkconfig} PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) set_property(TARGET ${frameworkconfig} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0") string(REPLACE "*" ";" componentsobjects-whitespace "${componentsobjects}") @@ -565,27 +534,11 @@ if(NOT DISABLE_EXECUTABLES) set_target_properties(mono-sgen PROPERTIES INSTALL_RPATH "$ORIGIN") endif() endif() - - set(MONOSGEN_LINKABLE_LIBS "") - list(APPEND MONOSGEN_LINKABLE_LIBS monoapi eglib_api monosgen-static dn-containers) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND MONOSGEN_LINKABLE_LIBS zlib) - endif() - target_link_libraries(mono-sgen PRIVATE ${MONOSGEN_LINKABLE_LIBS}) - + target_link_libraries(mono-sgen PRIVATE monoapi eglib_api monosgen-static dn-containers) if (HOST_WASM) target_link_libraries(mono-sgen PRIVATE mono-wasm-nosimd) endif() - - set(MONOSGEN_LINKABLE_EXTRALIBS "") - list(APPEND MONOSGEN_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) - if (CLR_CMAKE_USE_SYSTEM_ZLIB) - list(APPEND MONOSGEN_LINKABLE_EXTRALIBS ${Z_LIBS}) - else() - list(APPEND MONOSGEN_LINKABLE_EXTRALIBS zlib) - endif() - target_link_libraries(mono-sgen PRIVATE ${MONOSGEN_LINKABLE_EXTRALIBS}) - + target_link_libraries(mono-sgen PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) # musl-libc implements ucontext in a different library on s390x if(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) target_link_libraries(mono-sgen PRIVATE ucontext) diff --git a/src/mono/mono/profiler/CMakeLists.txt b/src/mono/mono/profiler/CMakeLists.txt index e172774a6ebc5a..511c19dbe5a322 100644 --- a/src/mono/mono/profiler/CMakeLists.txt +++ b/src/mono/mono/profiler/CMakeLists.txt @@ -12,7 +12,9 @@ include_directories( if(NOT DISABLE_LIBS) if(HOST_ANDROID OR HOST_IOS OR HOST_TVOS OR HOST_S390X) # Build the logging profiler only for certain platforms - add_library(mono-profiler-log SHARED helper.c log.c log-args.c) + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") + add_library(mono-profiler-log SHARED helper.c log.c log-args.c ${ZLIB_SOURCES}) target_compile_definitions(mono-profiler-log PRIVATE -DMONO_DLL_EXPORT) target_link_libraries(mono-profiler-log PRIVATE monosgen-shared monoapi eglib_objects) if(HOST_ANDROID) @@ -26,11 +28,7 @@ if(NOT DISABLE_LIBS) install(TARGETS mono-profiler-log-static LIBRARY) if(NOT DISABLE_LOG_PROFILER_GZ) - if (CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(mono-profiler-log PRIVATE ${Z_LIBS}) - else() - target_link_libraries(mono-profiler-log PRIVATE zlib) - endif() + target_link_libraries(mono-profiler-log PRIVATE ${Z_LIBS}) endif() endif() diff --git a/src/mono/mono/profiler/log.c b/src/mono/mono/profiler/log.c index abe5f3f479f578..9d4304faae3ecb 100644 --- a/src/mono/mono/profiler/log.c +++ b/src/mono/mono/profiler/log.c @@ -72,8 +72,12 @@ #include #endif #ifndef DISABLE_LOG_PROFILER_GZ +#ifdef INTERNAL_ZLIB +#include +#else #include -#endif // DISABLE_LOG_PROFILER_GZ +#endif +#endif #ifdef HOST_WIN32 #include diff --git a/src/mono/mono/profiler/mprof-report.c b/src/mono/mono/profiler/mprof-report.c index 5e2cc5ad7a20e1..35c88fe0a2fd9d 100644 --- a/src/mono/mono/profiler/mprof-report.c +++ b/src/mono/mono/profiler/mprof-report.c @@ -23,8 +23,12 @@ #endif #include #ifndef DISABLE_LOG_PROFILER_GZ +#ifdef INTERNAL_ZLIB +#include +#else #include -#endif // DISABLE_LOG_PROFILER_GZ +#endif +#endif #include #include #include diff --git a/src/mono/msbuild/apple/build/AppleBuild.targets b/src/mono/msbuild/apple/build/AppleBuild.targets index 483ae04924a728..35d0f16c35b4b8 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.targets +++ b/src/mono/msbuild/apple/build/AppleBuild.targets @@ -6,7 +6,6 @@ false <_ProcessRuntimeComponentsForLibraryMode Condition="'$(_IsLibraryMode)' == 'true' and '$(UseNativeAOTRuntime)' != 'true'">_ProcessRuntimeComponentsForLibraryMode false - true - <_CommonLinkerArgs Include="-lz" Condition="'$(UseSystemZlib)' == 'true'" /> + <_CommonLinkerArgs Include="-lz" /> <_CommonLinkerArgs Include="-lc++" /> <_CommonLinkerArgs Include="-liconv" /> <_CommonLinkerArgs Include="-licucore" /> diff --git a/src/mono/wasi/runtime/CMakeLists.txt b/src/mono/wasi/runtime/CMakeLists.txt index ec45f3aa2e488e..1d2a220494779a 100644 --- a/src/mono/wasi/runtime/CMakeLists.txt +++ b/src/mono/wasi/runtime/CMakeLists.txt @@ -27,7 +27,6 @@ target_link_libraries(dotnet ${MONO_ARTIFACTS_DIR}/libmonosgen-2.0.a ${MONO_ARTIFACTS_DIR}/libmono-icall-table.a ${MONO_ARTIFACTS_DIR}/libmono-wasm-${CONFIGURATION_INTERPSIMDTABLES_LIB}.a - ${MONO_ARTIFACTS_DIR}/libz.a ${NATIVE_BIN_DIR}/wasm-bundled-timezones.a ${NATIVE_BIN_DIR}/libSystem.Native.a ${NATIVE_BIN_DIR}/libSystem.Globalization.Native.a diff --git a/src/mono/wasi/wasi.proj b/src/mono/wasi/wasi.proj index 99bf8a6ee54f80..9c386481a26e19 100644 --- a/src/mono/wasi/wasi.proj +++ b/src/mono/wasi/wasi.proj @@ -49,7 +49,6 @@ - diff --git a/src/native/external/zlib-intel.cmake b/src/native/external/zlib-intel.cmake new file mode 100644 index 00000000000000..a664f154dbef09 --- /dev/null +++ b/src/native/external/zlib-intel.cmake @@ -0,0 +1,29 @@ +# IMPORTANT: do not use add_compile_options(), add_definitions() or similar functions here since it will leak to the including projects + +set(ZLIB_SOURCES_BASE + adler32.c + compress.c + crc_folding.c + crc32.c + deflate_medium.c + deflate_quick.c + deflate.c + inffast.c + inflate.c + inftrees.c + match.c + slide_sse.c + trees.c + x86.c + zutil.c + ../../libs/System.IO.Compression.Native/zlib_allocator_win.c +) + +addprefix(ZLIB_SOURCES "${CMAKE_CURRENT_LIST_DIR}/zlib-intel" "${ZLIB_SOURCES_BASE}") + +# enable custom zlib allocator +set(ZLIB_COMPILE_DEFINITIONS "MY_ZCALLOC") + +if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) + set(ZLIB_COMPILE_OPTIONS "/wd4127;/wd4131") +endif() diff --git a/src/native/external/zlib-ng.cmake b/src/native/external/zlib-ng.cmake deleted file mode 100644 index 3f005eaa54f3d3..00000000000000 --- a/src/native/external/zlib-ng.cmake +++ /dev/null @@ -1,33 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - fetchzlibng - SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/zlib-ng") - -set(ZLIB_COMPAT ON) -set(ZLIB_ENABLE_TESTS OFF) -set(ZLIBNG_ENABLE_TESTS OFF) -set(Z_PREFIX ON) - -add_compile_options($<$:-Wno-unused-command-line-argument>) # clang : error : argument unused during compilation: '-fno-semantic-interposition' -add_compile_options($<$:-Wno-logical-op-parentheses>) # place parentheses around the '&&' expression to silence this warning -add_compile_options($<$:/wd4127>) # warning C4127: conditional expression is constant -add_compile_options($<$:/wd4242>) # 'function': conversion from 'unsigned int' to 'Pos', possible loss of data, in various deflate_*.c files -add_compile_options($<$:/wd4244>) # 'function': conversion from 'unsigned int' to 'Pos', possible loss of data, in various deflate_*.c files - -# 'aligned_alloc' is not available in browser/wasi, yet it is set by zlib-ng/CMakeLists.txt. -if (CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) - set(HAVE_ALIGNED_ALLOC FALSE CACHE BOOL "have aligned_alloc" FORCE) -endif() - -set(BUILD_SHARED_LIBS OFF) # Shared libraries aren't supported in wasm -set(SKIP_INSTALL_ALL ON) -FetchContent_MakeAvailable(fetchzlibng) -set(SKIP_INSTALL_ALL OFF) - -set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/zlib-ng PROPERTY MSVC_WARNING_LEVEL 3) # Set the MSVC warning level for all zlib-ng targets to 3. -target_compile_options(zlib PRIVATE $<$:-Wno-unused-command-line-argument>) # Make sure MacOS respects ignoring unused CLI arguments -target_compile_options(zlib PRIVATE $<$:-Wno-logical-op-parentheses>) # place parentheses around the '&&' expression to silence this warning -target_compile_options(zlib PRIVATE $<$:/guard:cf>) # Enable CFG always for zlib-ng so we don't need to build two flavors. - -set_target_properties(zlib PROPERTIES DEBUG_POSTFIX "") # Workaround: zlib's debug lib name is zlibd.lib diff --git a/src/native/external/zlib.cmake b/src/native/external/zlib.cmake new file mode 100644 index 00000000000000..498ec977960e01 --- /dev/null +++ b/src/native/external/zlib.cmake @@ -0,0 +1,41 @@ +# IMPORTANT: do not use add_compile_options(), add_definitions() or similar functions here since it will leak to the including projects + +set(ZLIB_SOURCES_BASE + adler32.c + compress.c + crc32.c + uncompr.c + deflate.c + gzguts.h + trees.c + zutil.c + inflate.c + infback.c + inftrees.c + inffast.c + crc32.h + deflate.h + inffast.h + inffixed.h + inflate.h + inftrees.h + trees.h + zconf.h + zlib.h + zutil.h +) + +if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) + set(ZLIB_SOURCES_BASE ${ZLIB_SOURCES_BASE} ../../libs/System.IO.Compression.Native/zlib_allocator_win.c) +else() + set(ZLIB_SOURCES_BASE ${ZLIB_SOURCES_BASE} ../../libs/System.IO.Compression.Native/zlib_allocator_unix.c) +endif() + +addprefix(ZLIB_SOURCES "${CMAKE_CURRENT_LIST_DIR}/zlib" "${ZLIB_SOURCES_BASE}") + +# enable custom zlib allocator +set(ZLIB_COMPILE_DEFINITIONS "MY_ZCALLOC") + +if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) + set(ZLIB_COMPILE_OPTIONS "/wd4127;/wd4131") +endif() diff --git a/src/native/libs/Common/pal_utilities.h b/src/native/libs/Common/pal_utilities.h index a1a57c8ed0e8ce..7b5fa63b6cac03 100644 --- a/src/native/libs/Common/pal_utilities.h +++ b/src/native/libs/Common/pal_utilities.h @@ -12,9 +12,7 @@ #include #include #include -#ifndef _WIN32 - #include -#endif +#include #include #include @@ -71,7 +69,6 @@ inline static int ToFileDescriptorUnchecked(intptr_t fd) return (int)fd; } -#ifndef _WIN32 /** * Converts an intptr_t to a file descriptor. * intptr_t is the type used to marshal file descriptors so we can use SafeHandles effectively. @@ -89,7 +86,6 @@ static inline bool CheckInterrupted(ssize_t result) { return result < 0 && errno == EINTR; } -#endif inline static uint32_t Int32ToUint32(int32_t value) { diff --git a/src/native/libs/System.IO.Compression.Native/CMakeLists.txt b/src/native/libs/System.IO.Compression.Native/CMakeLists.txt index 69d333ae53f9c5..089363cf5554ec 100644 --- a/src/native/libs/System.IO.Compression.Native/CMakeLists.txt +++ b/src/native/libs/System.IO.Compression.Native/CMakeLists.txt @@ -2,24 +2,10 @@ project(System.IO.Compression.Native C) include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake) -if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - include(${CLR_SRC_NATIVE_DIR}/external/zlib-ng.cmake) -endif() - -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/../Common/pal_config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/pal_config.h) - set(NATIVECOMPRESSION_SOURCES pal_zlib.c ) -if (HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) - list(APPEND NATIVECOMPRESSION_SOURCES "zlib_allocator_win.c") -else() - list(APPEND NATIVECOMPRESSION_SOURCES "zlib_allocator_unix.c") -endif() - if (NOT CLR_CMAKE_TARGET_BROWSER AND NOT CLR_CMAKE_TARGET_WASI) if (CLR_CMAKE_USE_SYSTEM_BROTLI) @@ -43,6 +29,14 @@ if (CLR_CMAKE_TARGET_UNIX OR CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) set(NATIVE_LIBS_EXTRA) append_extra_compression_libs(NATIVE_LIBS_EXTRA) + if (CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) + include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) + add_definitions(-DINTERNAL_ZLIB) + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") + set(NATIVECOMPRESSION_SOURCES ${ZLIB_SOURCES} ${NATIVECOMPRESSION_SOURCES}) + endif() + # Disable implicit fallthrough warning for Zlib and Brotli set(FLAGS -Wno-implicit-fallthrough) @@ -102,16 +96,24 @@ if (CLR_CMAKE_TARGET_UNIX OR CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) ${NATIVECOMPRESSION_SOURCES} ) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(System.IO.Compression.Native-Static PRIVATE zlibstatic) - endif() - set_target_properties(System.IO.Compression.Native-Static PROPERTIES OUTPUT_NAME System.IO.Compression.Native CLEAN_DIRECT_OUTPUT 1) else () if (GEN_SHARED_LIB) include (GenerateExportHeader) endif () + if (CLR_CMAKE_HOST_ARCH_I386 OR CLR_CMAKE_HOST_ARCH_AMD64) + include(${CLR_SRC_NATIVE_DIR}/external/zlib-intel.cmake) + add_definitions(-DINTERNAL_ZLIB_INTEL) + else () + include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) + endif () + + add_definitions(-DINTERNAL_ZLIB) + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") + set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") + set(NATIVECOMPRESSION_SOURCES ${ZLIB_SOURCES} ${NATIVECOMPRESSION_SOURCES}) + if (GEN_SHARED_LIB) add_definitions(-DVER_FILEDESCRIPTION_STR="System.IO.Compression.Native") add_library(System.IO.Compression.Native @@ -120,10 +122,6 @@ else () System.IO.Compression.Native.def ${VERSION_FILE_RC_PATH} ) - - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(System.IO.Compression.Native PRIVATE zlib) - endif() endif () if (NOT GEN_SHARED_LIB AND NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_ANDROID AND NOT CLR_CMAKE_TARGET_BROWSER AND NOT CLR_CMAKE_TARGET_WASI) @@ -135,20 +133,11 @@ else () ${NATIVECOMPRESSION_SOURCES} ) - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(System.IO.Compression.Native-Static PRIVATE zlibstatic) - endif() - if(STATIC_LIBS_ONLY) add_library(System.IO.Compression.Native.Aot STATIC ${NATIVECOMPRESSION_SOURCES} ) - - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(System.IO.Compression.Native.Aot PRIVATE zlibstatic) - endif() - set_target_properties(System.IO.Compression.Native.Aot PROPERTIES CLR_CONTROL_FLOW_GUARD OFF) set_target_properties(System.IO.Compression.Native.Aot PROPERTIES INTERPROCEDURAL_OPTIMIZATION OFF) @@ -156,11 +145,6 @@ else () STATIC ${NATIVECOMPRESSION_SOURCES} ) - - if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) - target_link_libraries(System.IO.Compression.Native.Aot.GuardCF PRIVATE zlibstatic) - endif() - set_target_properties(System.IO.Compression.Native.Aot.GuardCF PROPERTIES INTERPROCEDURAL_OPTIMIZATION OFF) endif() @@ -183,8 +167,4 @@ else () endif () -if((NOT CLR_CMAKE_USE_SYSTEM_ZLIB) AND STATIC_LIBS_ONLY) - install_static_library(zlib aotsdk nativeaot) -endif() - install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/native/libs/System.IO.Compression.Native/extra_libs.cmake b/src/native/libs/System.IO.Compression.Native/extra_libs.cmake index 6ad60233fce3f2..78530ae98e8ff7 100644 --- a/src/native/libs/System.IO.Compression.Native/extra_libs.cmake +++ b/src/native/libs/System.IO.Compression.Native/extra_libs.cmake @@ -1,19 +1,16 @@ + macro(append_extra_compression_libs NativeLibsExtra) - set(ZLIB_LIBRARIES "") # TODO: remove the mono-style HOST_ variable checks once Mono is using eng/native/configureplatform.cmake to define the CLR_CMAKE_TARGET_ defines if (CLR_CMAKE_TARGET_BROWSER OR HOST_BROWSER OR CLR_CMAKE_TARGET_WASI OR HOST_WASI) # nothing special to link elseif (CLR_CMAKE_TARGET_ANDROID OR HOST_ANDROID) # need special case here since we want to link against libz.so but find_package() would resolve libz.a - list(APPEND ZLIB_LIBRARIES z) - elseif (CLR_CMAKE_HOST_ARCH_ARMV6) - find_package(ZLIB REQUIRED) - list(APPEND ZLIB_LIBRARIES z) - elseif (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + set(ZLIB_LIBRARIES z) + elseif (CLR_CMAKE_TARGET_SUNOS OR HOST_SOLARIS) + set(ZLIB_LIBRARIES z m) + else () find_package(ZLIB REQUIRED) - list(APPEND ZLIB_LIBRARIES m) - else() - list(APPEND ZLIB_LIBRARIES zlib) + set(ZLIB_LIBRARIES ${ZLIB_LIBRARIES} m) endif () list(APPEND ${NativeLibsExtra} ${ZLIB_LIBRARIES}) diff --git a/src/native/libs/System.IO.Compression.Native/pal_zlib.c b/src/native/libs/System.IO.Compression.Native/pal_zlib.c index 87b7043ea20ffd..bed37d1955f481 100644 --- a/src/native/libs/System.IO.Compression.Native/pal_zlib.c +++ b/src/native/libs/System.IO.Compression.Native/pal_zlib.c @@ -5,14 +5,19 @@ #include #include "pal_zlib.h" -#ifdef _WIN32 - #define c_static_assert(e) static_assert((e),"") - #include "../Common/pal_utilities.h" +#ifdef INTERNAL_ZLIB + #ifdef _WIN32 + #define c_static_assert(e) static_assert((e),"") + #endif + #ifdef INTERNAL_ZLIB_INTEL + #include + #else + #include + #endif #else #include "pal_utilities.h" + #include #endif -#include -#include c_static_assert(PAL_Z_NOFLUSH == Z_NO_FLUSH); c_static_assert(PAL_Z_FINISH == Z_FINISH); @@ -39,10 +44,6 @@ Initializes the PAL_ZStream by creating and setting its underlying z_stream. static int32_t Init(PAL_ZStream* stream) { z_stream* zStream = (z_stream*)calloc(1, sizeof(z_stream)); - - zStream->zalloc = z_custom_calloc; - zStream->zfree = z_custom_cfree; - stream->internalState = zStream; if (zStream != NULL) diff --git a/src/native/libs/System.IO.Compression.Native/pal_zlib.h b/src/native/libs/System.IO.Compression.Native/pal_zlib.h index efb090b0631ae7..b317091b843f62 100644 --- a/src/native/libs/System.IO.Compression.Native/pal_zlib.h +++ b/src/native/libs/System.IO.Compression.Native/pal_zlib.h @@ -5,12 +5,12 @@ #include #include #define FUNCTIONEXPORT - #define FUNCTIONCALLINGCONVENTION WINAPI + #define FUNCTIONCALLINGCONVENCTION WINAPI #else #include "pal_types.h" #include "pal_compiler.h" #define FUNCTIONEXPORT PALEXPORT - #define FUNCTIONCALLINGCONVENTION + #define FUNCTIONCALLINGCONVENCTION #endif /* @@ -84,7 +84,7 @@ Initializes the PAL_ZStream so the Deflate function can be invoked on it. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_DeflateInit2_( +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_DeflateInit2_( PAL_ZStream* stream, int32_t level, int32_t method, int32_t windowBits, int32_t memLevel, int32_t strategy); /* @@ -93,21 +93,21 @@ compressed bytes in nextOut. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_Deflate(PAL_ZStream* stream, int32_t flush); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Deflate(PAL_ZStream* stream, int32_t flush); /* All dynamically allocated data structures for this stream are freed. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_DeflateEnd(PAL_ZStream* stream); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_DeflateEnd(PAL_ZStream* stream); /* Initializes the PAL_ZStream so the Inflate function can be invoked on it. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_InflateInit2_(PAL_ZStream* stream, int32_t windowBits); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_InflateInit2_(PAL_ZStream* stream, int32_t windowBits); /* Inflates (uncompresses) the bytes in the PAL_ZStream's nextIn buffer and puts the @@ -115,14 +115,14 @@ uncompressed bytes in nextOut. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_Inflate(PAL_ZStream* stream, int32_t flush); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Inflate(PAL_ZStream* stream, int32_t flush); /* All dynamically allocated data structures for this stream are freed. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_InflateEnd(PAL_ZStream* stream); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_InflateEnd(PAL_ZStream* stream); /* Update a running CRC-32 with the bytes buffer[0..len-1] and return the @@ -130,4 +130,4 @@ updated CRC-32. Returns the updated CRC-32. */ -FUNCTIONEXPORT uint32_t FUNCTIONCALLINGCONVENTION CompressionNative_Crc32(uint32_t crc, uint8_t* buffer, int32_t len); +FUNCTIONEXPORT uint32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Crc32(uint32_t crc, uint8_t* buffer, int32_t len); diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator.h b/src/native/libs/System.IO.Compression.Native/zlib_allocator.h deleted file mode 100644 index cadd00bb5879c5..00000000000000 --- a/src/native/libs/System.IO.Compression.Native/zlib_allocator.h +++ /dev/null @@ -1,8 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include // voidpf - -voidpf z_custom_calloc(voidpf opaque, unsigned items, unsigned size); - -void z_custom_cfree(voidpf opaque, voidpf ptr); diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c b/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c index b2c898f70a4e36..9eb4bbf2671057 100644 --- a/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c +++ b/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c @@ -3,10 +3,7 @@ #include #include -#include -#include -#include -#include +#include /* A custom allocator for zlib that provides some defense-in-depth over standard malloc / free. * (non-Windows version) @@ -73,7 +70,7 @@ static void WriteAllocCookieUnaligned(void* pDest, DOTNET_ALLOC_COOKIE vCookie) const size_t DOTNET_ALLOC_HEADER_COOKIE_SIZE_WITH_PADDING = (sizeof(DOTNET_ALLOC_COOKIE) + MEMORY_ALLOCATION_ALIGNMENT - 1) & ~((size_t)MEMORY_ALLOCATION_ALIGNMENT - 1); const size_t DOTNET_ALLOC_TRAILER_COOKIE_SIZE = sizeof(DOTNET_ALLOC_COOKIE); -voidpf z_custom_calloc(opaque, items, size) +voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) voidpf opaque; unsigned items; unsigned size; @@ -82,7 +79,7 @@ voidpf z_custom_calloc(opaque, items, size) // If initializing a fixed-size structure, zero the memory. bool fZeroMemory = (items == 1); - + size_t cbRequested; if (sizeof(items) + sizeof(size) <= sizeof(cbRequested)) { @@ -122,7 +119,7 @@ static void zcfree_trash_cookie(void* pCookie) memset(pCookie, 0, sizeof(DOTNET_ALLOC_COOKIE)); } -void z_custom_cfree(opaque, ptr) +void ZLIB_INTERNAL zcfree(opaque, ptr) voidpf opaque; voidpf ptr; { diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c b/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c index fefba550c16ed5..9bdf694495e680 100644 --- a/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c +++ b/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c @@ -7,10 +7,11 @@ #include #include /* _ASSERTE */ -#include -#include -#include -#include +#ifdef INTERNAL_ZLIB_INTEL +#include +#else +#include +#endif /* A custom allocator for zlib that provides some defense-in-depth over standard malloc / free. * (Windows-specific version) @@ -91,7 +92,7 @@ typedef struct _DOTNET_ALLOC_COOKIE const SIZE_T DOTNET_ALLOC_HEADER_COOKIE_SIZE_WITH_PADDING = (sizeof(DOTNET_ALLOC_COOKIE) + MEMORY_ALLOCATION_ALIGNMENT - 1) & ~((SIZE_T)MEMORY_ALLOCATION_ALIGNMENT - 1); const SIZE_T DOTNET_ALLOC_TRAILER_COOKIE_SIZE = sizeof(DOTNET_ALLOC_COOKIE); -voidpf z_custom_calloc(opaque, items, size) +voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) voidpf opaque; unsigned items; unsigned size; @@ -149,7 +150,7 @@ void zcfree_cookie_check_failed() __fastfail(FAST_FAIL_HEAP_METADATA_CORRUPTION); } -void z_custom_cfree(opaque, ptr) +void ZLIB_INTERNAL zcfree(opaque, ptr) voidpf opaque; voidpf ptr; { From 13d2bab5a566781cf604849652748868007b0c2a Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Thu, 4 Jul 2024 13:54:30 +0000 Subject: [PATCH 09/72] Enable `ConsolePublishAndRun` AOT without relink on Windows (#104357) --- .../wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index a17dc522988d23..3142bed49a35fe 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -385,12 +385,7 @@ public static TheoryData TestDataForConsolePublishAndRun() data.Add("Debug", false, false); data.Add("Debug", false, true); data.Add("Release", false, false); // Release relinks by default - - // [ActiveIssue("https://github.com/dotnet/runtime/issues/71887", TestPlatforms.Windows)] - if (!OperatingSystem.IsWindows()) - { - data.Add("Release", true, false); - } + data.Add("Release", true, false); return data; } From d198728ed698764e0d4ac97e8ff835fd5f18a853 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 4 Jul 2024 15:54:51 +0200 Subject: [PATCH 10/72] Remove uses of CLRJIT_AZ_KEY/clrjit_key1 from SPMI (#104164) --- .../templates/runtimes/run-test-job.yml | 14 +++++++---- .../templates/run-superpmi-collect-job.yml | 14 +++++++---- .../coreclr/templates/upload-jits-steps.yml | 16 +++++++----- .../libraries/superpmi-postprocess-step.yml | 14 +++++++---- src/coreclr/scripts/jitrollingbuild.py | 15 +++++------ src/coreclr/scripts/jitutil.py | 10 ++++---- src/coreclr/scripts/superpmi.py | 25 ++++++------------- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml index e859e9ae257497..c604e0c48306f8 100644 --- a/eng/pipelines/common/templates/runtimes/run-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml @@ -615,15 +615,19 @@ jobs: condition: always() # Ensure the Python azure-storage-blob package is installed before doing the upload. - - script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall - displayName: Upgrade Pip to latest and install azure-storage-blob Python package + - script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall && $(PipScript) install azure.identity==1.16.1 --force-reinstall + displayName: Upgrade Pip to latest and install azure-storage-blob and azure-identity Python packages condition: always() - - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch $(archType) -build_type $(buildConfig) -mch_files $(MergedMchFileLocation)$(CollectionName).$(CollectionType).$(MchFileTag).mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).x64.$(buildConfigUpper) + - task: AzureCLI@2 displayName: 'Upload SuperPMI $(CollectionName)-$(CollectionType) collection to Azure Storage' + inputs: + azureSubscription: 'superpmi-collect-rw' + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch $(archType) -build_type $(buildConfig) -mch_files $(MergedMchFileLocation)$(CollectionName).$(CollectionType).$(MchFileTag).mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).x64.$(buildConfigUpper) condition: always() - env: - CLRJIT_AZ_KEY: $(clrjit_key1) # secret key stored as variable in pipeline - task: CopyFiles@2 displayName: Copying superpmi.log of all partitions diff --git a/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml b/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml index 9903c61d4d414d..669347d2c38887 100644 --- a/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml +++ b/eng/pipelines/coreclr/templates/run-superpmi-collect-job.yml @@ -177,13 +177,17 @@ jobs: onlyAddExtraIndex: false # Ensure the Python azure-storage-blob package is installed before doing the upload. - - script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall - displayName: Upgrade Pip to latest and install azure-storage-blob Python package + - script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall && $(PipScript) install azure.identity==1.16.1 --force-reinstall + displayName: Upgrade Pip to latest and install azure-storage-blob and azure-identity Python packages - - script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch $(archType) -build_type $(buildConfig) -mch_files $(MergedMchFileLocation)$(CollectionName).$(CollectionType).$(MchFileTag).mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).x64.$(buildConfigUpper) + - task: AzureCLI@2 displayName: ${{ format('Upload SuperPMI {0}-{1} collection to Azure Storage', parameters.collectionName, parameters.collectionType) }} - env: - CLRJIT_AZ_KEY: $(clrjit_key1) # secret key stored as variable in pipeline + inputs: + azureSubscription: 'superpmi-collect-rw' + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch $(archType) -build_type $(buildConfig) -mch_files $(MergedMchFileLocation)$(CollectionName).$(CollectionType).$(MchFileTag).mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).x64.$(buildConfigUpper) # Always upload the available logs for diagnostics - task: CopyFiles@2 diff --git a/eng/pipelines/coreclr/templates/upload-jits-steps.yml b/eng/pipelines/coreclr/templates/upload-jits-steps.yml index 912732b90b8476..536a18dc927a40 100644 --- a/eng/pipelines/coreclr/templates/upload-jits-steps.yml +++ b/eng/pipelines/coreclr/templates/upload-jits-steps.yml @@ -10,10 +10,14 @@ steps: displayName: Enable python venv # Ensure the Python azure-storage-blob package is installed before doing the upload. -- script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall - displayName: Upgrade Pip to latest and install azure-storage-blob Python package +- script: $(PipScript) install --upgrade pip && $(PipScript) install azure.storage.blob==12.5.0 --force-reinstall && $(PipScript) install azure.identity==1.16.1 --force-reinstall + displayName: Upgrade Pip to latest and install azure-storage-blob and azure-identity Python packages -- script: $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/jitrollingbuild.py upload -build_type $(_BuildConfig) -arch $(archType) -host_os $(osGroup) -git_hash $(Build.SourceVersion) --use_latest_jit_change - displayName: Upload JIT to Azure Storage - env: - CLRJIT_AZ_KEY: $(clrjit_key1) # secret key stored as variable in pipeline +- task: AzureCLI@2 + displayName: 'Upload JIT to Azure Storage' + inputs: + azureSubscription: 'superpmi-collect-rw' + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + $(PythonScript) $(Build.SourcesDirectory)/src/coreclr/scripts/jitrollingbuild.py upload -build_type $(_BuildConfig) -arch $(archType) -host_os $(osGroup) -git_hash $(Build.SourceVersion) --use_latest_jit_change diff --git a/eng/pipelines/libraries/superpmi-postprocess-step.yml b/eng/pipelines/libraries/superpmi-postprocess-step.yml index e9ae5d13f32f29..343cb1ee58c118 100644 --- a/eng/pipelines/libraries/superpmi-postprocess-step.yml +++ b/eng/pipelines/libraries/superpmi-postprocess-step.yml @@ -66,15 +66,19 @@ steps: condition: always() # Ensure the Python azure-storage-blob package is installed before doing the upload. - - script: ${{ parameters.PipScript }} install --upgrade pip && ${{ parameters.PipScript }} install azure.storage.blob==12.5.0 --force-reinstall - displayName: Upgrade Pip to latest and install azure-storage-blob Python package + - script: ${{ parameters.PipScript }} install --upgrade pip && ${{ parameters.PipScript }} install azure.storage.blob==12.5.0 --force-reinstall && ${{ parameters.PipScript }} install azure.identity==1.16.1 --force-reinstall + displayName: Upgrade Pip to latest and install azure-storage-blob and azure-identity Python packages condition: always() - - script: ${{ parameters.PythonScript }} $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch ${{ parameters.archType }} -build_type ${{ parameters.buildConfig }} -mch_files ${{ parameters.MergedMchFileLocation }}${{ parameters.SuperPmiCollectionName }}.${{ parameters.SuperPmiCollectionType }}.${{ parameters.osGroup }}.${{ parameters.archType }}.${{ parameters.buildConfig }}.mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/${{ parameters.osGroup }}.x64.${{ parameters.buildConfigUpper }} + - task: AzureCLI@2 displayName: 'Upload SuperPMI ${{ parameters.SuperPmiCollectionName }}-${{ parameters.SuperPmiCollectionType }} collection to Azure Storage' + inputs: + azureSubscription: 'superpmi-collect-rw' + scriptType: 'pscore' + scriptLocation: 'inlineScript' + inlineScript: | + ${{ parameters.PythonScript }} $(Build.SourcesDirectory)/src/coreclr/scripts/superpmi.py upload -log_level DEBUG -arch ${{ parameters.archType }} -build_type ${{ parameters.buildConfig }} -mch_files ${{ parameters.MergedMchFileLocation }}${{ parameters.SuperPmiCollectionName }}.${{ parameters.SuperPmiCollectionType }}.${{ parameters.osGroup }}.${{ parameters.archType }}.${{ parameters.buildConfig }}.mch -core_root $(Build.SourcesDirectory)/artifacts/bin/coreclr/${{ parameters.osGroup }}.x64.${{ parameters.buildConfigUpper }} condition: always() - env: - CLRJIT_AZ_KEY: $(clrjit_key1) # secret key stored as variable in pipeline - task: CopyFiles@2 displayName: Copying superpmi.log of all partitions diff --git a/src/coreclr/scripts/jitrollingbuild.py b/src/coreclr/scripts/jitrollingbuild.py index 93f1f02c28c585..795030178e5042 100644 --- a/src/coreclr/scripts/jitrollingbuild.py +++ b/src/coreclr/scripts/jitrollingbuild.py @@ -108,7 +108,6 @@ upload_parser.add_argument("-git_hash", required=True, help=git_hash_help) upload_parser.add_argument("--use_latest_jit_change", action="store_true", help=use_latest_jit_change_help) -upload_parser.add_argument("-az_storage_key", help="Key for the clrjit Azure Storage location. Default: use the value of the CLRJIT_AZ_KEY environment variable.") upload_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) # subparser for download @@ -458,14 +457,18 @@ def upload_blob(file, blob_name): try: from azure.storage.blob import BlobServiceClient + from azure.identity import DefaultAzureCredential except: logging.warning("Please install:") logging.warning(" pip install azure-storage-blob") + logging.warning(" pip install azure-identiy") logging.warning("See also https://learn.microsoft.com/azure/storage/blobs/storage-quickstart-blobs-python") - raise RuntimeError("Missing azure storage package.") + raise RuntimeError("Missing azure storage or identity packages.") - blob_service_client = BlobServiceClient(account_url=az_blob_storage_account_uri, credential=coreclr_args.az_storage_key) + default_credential = DefaultAzureCredential() + + blob_service_client = BlobServiceClient(account_url=az_blob_storage_account_uri, credential=default_credential) blob_folder_name = "{}/{}/{}/{}/{}".format(az_builds_root_folder, jit_git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type) total_bytes_uploaded = 0 @@ -623,12 +626,6 @@ def setup_spmi_location_arg(spmi_location): lambda unused: True, "Unable to set use_latest_jit_change") - coreclr_args.verify(args, - "az_storage_key", - lambda item: item is not None, - "Specify az_storage_key or set environment variable CLRJIT_AZ_KEY to the key to use.", - modify_arg=lambda arg: os.environ["CLRJIT_AZ_KEY"] if arg is None and "CLRJIT_AZ_KEY" in os.environ else arg) - coreclr_args.verify(args, "skip_cleanup", lambda unused: True, diff --git a/src/coreclr/scripts/jitutil.py b/src/coreclr/scripts/jitutil.py index 78cb26e01c7faa..b397cecdc53b86 100644 --- a/src/coreclr/scripts/jitutil.py +++ b/src/coreclr/scripts/jitutil.py @@ -537,13 +537,13 @@ def require_azure_storage_libraries(need_azure_storage_blob=True, need_azure_ide Once we've done it once, we don't do it again. For this to work for cross-module usage, after you call this function, you need to add a line like: - from jitutil import BlobClient, AzureCliCredential + from jitutil import BlobClient, DefaultAzureCredential naming all the types you want to use. The full set of types this function loads: - BlobServiceClient, BlobClient, ContainerClient, AzureCliCredential + BlobServiceClient, BlobClient, ContainerClient, DefaultAzureCredential """ - global azure_storage_libraries_check, BlobServiceClient, BlobClient, ContainerClient, AzureCliCredential + global azure_storage_libraries_check, BlobServiceClient, BlobClient, ContainerClient, DefaultAzureCredential if azure_storage_libraries_check: return @@ -560,7 +560,7 @@ def require_azure_storage_libraries(need_azure_storage_blob=True, need_azure_ide azure_identity_import_ok = True if need_azure_identity: try: - from azure.identity import AzureCliCredential + from azure.identity import DefaultAzureCredential except: azure_identity_import_ok = False @@ -608,7 +608,7 @@ def download_with_azure(uri, target_location, fail_if_not_found=True): logging.info("Download: %s -> %s", uri, target_location) ok = True - az_credential = AzureCliCredential() + az_credential = DefaultAzureCredential() blob = BlobClient.from_blob_url(uri, credential=az_credential) with open(target_location, "wb") as my_blob: try: diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 99ea6e65e52bec..493965ecb0a437 100644 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -364,7 +364,6 @@ def add_core_root_arguments(parser, build_type_default, build_type_help): upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[core_root_parser, target_parser]) upload_parser.add_argument("-mch_files", metavar="MCH_FILE", required=True, nargs='+', help=upload_mch_files_help) -upload_parser.add_argument("-az_storage_key", help="Key for the clrjit Azure Storage location. Default: use the value of the CLRJIT_AZ_KEY environment variable.") upload_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) upload_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) @@ -3563,14 +3562,14 @@ def list_superpmi_collections_container_via_azure_api(path_filter=lambda unused: """ require_azure_storage_libraries() - from jitutil import ContainerClient, AzureCliCredential + from jitutil import ContainerClient, DefaultAzureCredential superpmi_container_url = az_blob_storage_superpmi_container_uri paths = [] ok = True try: - az_credential = AzureCliCredential() + az_credential = DefaultAzureCredential() container = ContainerClient.from_container_url(superpmi_container_url, credential=az_credential) blob_name_prefix = az_collections_root_folder + "/" blob_list = container.list_blobs(name_starts_with=blob_name_prefix, retry_total=0) @@ -3789,8 +3788,8 @@ def upload_mch(coreclr_args): coreclr_args (CoreclrArguments): parsed args """ - require_azure_storage_libraries(need_azure_identity=False) - from jitutil import BlobServiceClient + require_azure_storage_libraries(need_azure_identity=True) + from jitutil import BlobServiceClient, DefaultAzureCredential def upload_blob(file, blob_name): blob_client = blob_service_client.get_blob_client(container=az_superpmi_container_name, blob=blob_name) @@ -3826,7 +3825,9 @@ def upload_blob(file, blob_name): for item in files_to_upload: logging.info(" %s", item) - blob_service_client = BlobServiceClient(account_url=az_blob_storage_account_uri, credential=coreclr_args.az_storage_key) + default_credential = DefaultAzureCredential() + + blob_service_client = BlobServiceClient(account_url=az_blob_storage_account_uri, credential=default_credential) blob_folder_name = "{}/{}/{}/{}".format(az_collections_root_folder, coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch) total_bytes_uploaded = 0 @@ -5005,12 +5006,6 @@ def verify_base_diff_args(): verify_target_args() verify_jit_ee_version_arg() - coreclr_args.verify(args, - "az_storage_key", - lambda item: item is not None, - "Specify az_storage_key or set environment variable CLRJIT_AZ_KEY to the key to use.", - modify_arg=lambda arg: os.environ["CLRJIT_AZ_KEY"] if arg is None and "CLRJIT_AZ_KEY" in os.environ else arg) - coreclr_args.verify(args, "mch_files", lambda unused: True, @@ -5035,12 +5030,6 @@ def verify_base_diff_args(): print("Error: private store directory '" + coreclr_args.private_store + "' not found.") sys.exit(1) - # Safety measure: don't allow CLRJIT_AZ_KEY to be set if we are uploading to a private store. - # Note that this should be safe anyway, since we're publishing something private, not public. - if "CLRJIT_AZ_KEY" in os.environ: - print("Error: environment variable CLRJIT_AZ_KEY is set, but command is `upload-private`, not `upload`. That is not allowed.") - sys.exit(1) - elif coreclr_args.mode == "download": verify_target_args() From aa9e4d3caf14233694677858f38c545469cc373f Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 4 Jul 2024 15:55:18 +0200 Subject: [PATCH 11/72] Small clean up in gtMarkAddrMode (#104387) --- src/coreclr/jit/gentree.cpp | 102 +----------------------------------- 1 file changed, 2 insertions(+), 100 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 786e7c92f5f894..da54ff739d7558 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -4193,13 +4193,6 @@ void Compiler::gtWalkOp(GenTree** op1WB, GenTree** op2WB, GenTree* base, bool co } op1 = addOp1; - // If op1 is a GT_NOP then swap op1 and op2. - // (Why? Also, presumably op2 is not a GT_NOP in this case?) - if (op1->OperIs(GT_NOP)) - { - std::swap(op1, op2); - } - if (!constOnly && ((op2 == base) || !op2->IsCnsIntOrI() || !op2->AsIntCon()->ImmedValCanBeFolded(this, GT_ADD))) { break; @@ -4813,8 +4806,6 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ assert((base != nullptr) || (idx != nullptr && mul >= 2)); - INDEBUG(GenTree* op1Save = addr); - // Walk 'addr' identifying non-overflow ADDs that will be part of the address mode. // Note that we will be modifying 'op1' and 'op2' so that eventually they should // map to the base and index. @@ -4823,9 +4814,6 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ gtWalkOp(&op1, &op2, base, false); // op1 and op2 are now descendents of the root GT_ADD of the addressing mode. - assert(op1 != op1Save); - assert(op2 != nullptr); - #if defined(TARGET_XARCH) // Walk the operands again (the third operand is unused in this case). // This time we will only consider adds with constant op2's, since @@ -4845,95 +4833,9 @@ bool Compiler::gtMarkAddrMode(GenTree* addr, int* pCostEx, int* pCostSz, var_typ gtWalkOp(&op2, &op1, nullptr, true); #endif // defined(TARGET_XARCH) - // OK we are done walking the tree - // Now assert that op1 and op2 correspond with base and idx - // in one of the several acceptable ways. - - // Note that sometimes op1/op2 is equal to idx/base - // and other times op1/op2 is a GT_COMMA node with - // an effective value that is idx/base - - if (mul > 1) + if ((mul > 1) && (op2 != nullptr) && op2->OperIs(GT_LSH, GT_MUL)) { - if ((op1 != base) && (op1->gtOper == GT_LSH)) - { - op1->gtFlags |= GTF_ADDRMODE_NO_CSE; - if (op1->AsOp()->gtOp1->gtOper == GT_MUL) - { - op1->AsOp()->gtOp1->gtFlags |= GTF_ADDRMODE_NO_CSE; - } - assert((base == nullptr) || (op2 == base) || (op2->gtEffectiveVal() == base->gtEffectiveVal()) || - (gtWalkOpEffectiveVal(op2) == gtWalkOpEffectiveVal(base))); - } - else - { - assert(op2 != nullptr); - assert(op2->OperIs(GT_LSH, GT_MUL)); - op2->gtFlags |= GTF_ADDRMODE_NO_CSE; - // We may have eliminated multiple shifts and multiplies in the addressing mode, - // so navigate down through them to get to "idx". - GenTree* op2op1 = op2->AsOp()->gtOp1; - while ((op2op1->gtOper == GT_LSH || op2op1->gtOper == GT_MUL) && op2op1 != idx) - { - op2op1->gtFlags |= GTF_ADDRMODE_NO_CSE; - op2op1 = op2op1->AsOp()->gtOp1; - } - - // if genCreateAddrMode reported base as nullptr it means that op1 is effectively null address - assert((op1->gtEffectiveVal() == base) || - (base == nullptr && op1->gtEffectiveVal()->IsIntegralConst(0))); - assert(op2op1 == idx); - } - } - else - { - assert(mul == 0); - - if ((op1 == idx) || (op1->gtEffectiveVal() == idx)) - { - if (idx != nullptr) - { - if ((op1->gtOper == GT_MUL) || (op1->gtOper == GT_LSH)) - { - GenTree* op1op1 = op1->AsOp()->gtOp1; - if ((op1op1->gtOper == GT_NOP) || - (op1op1->gtOper == GT_MUL && op1op1->AsOp()->gtOp1->gtOper == GT_NOP)) - { - op1->gtFlags |= GTF_ADDRMODE_NO_CSE; - if (op1op1->gtOper == GT_MUL) - { - op1op1->gtFlags |= GTF_ADDRMODE_NO_CSE; - } - } - } - } - assert((op2 == base) || (op2->gtEffectiveVal() == base)); - } - else if ((op1 == base) || (op1->gtEffectiveVal() == base)) - { - if (idx != nullptr) - { - assert(op2 != nullptr); - if (op2->OperIs(GT_MUL, GT_LSH)) - { - GenTree* op2op1 = op2->AsOp()->gtOp1; - if ((op2op1->gtOper == GT_NOP) || - (op2op1->gtOper == GT_MUL && op2op1->AsOp()->gtOp1->gtOper == GT_NOP)) - { - op2->gtFlags |= GTF_ADDRMODE_NO_CSE; - if (op2op1->gtOper == GT_MUL) - { - op2op1->gtFlags |= GTF_ADDRMODE_NO_CSE; - } - } - } - assert((op2 == idx) || (op2->gtEffectiveVal() == idx)); - } - } - else - { - // op1 isn't base or idx. Is this possible? Or should there be an assert? - } + op2->gtFlags |= GTF_ADDRMODE_NO_CSE; } // Finally, adjust the costs on the parenting COMMAs. From 3bad19f042a6ad4db9636b53e4078b7fec786372 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Thu, 4 Jul 2024 17:12:20 +0100 Subject: [PATCH 12/72] Add Utf8JsonReader.AllowMultipleValues and related APIs. (#104328) * Add Utf8JsonReader.AllowMultipleValues and related APIs. * Update src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderOptions.cs Co-authored-by: Stephen Toub * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs Co-authored-by: Stephen Toub * Address feedback * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs * Update DOM tests to demonstrate chained root-value parsing. * Add another assertion --------- Co-authored-by: Stephen Toub --- .../System.Text.Json/ref/System.Text.Json.cs | 5 + .../src/System.Text.Json.csproj | 1 + .../src/System/Text/Json/JsonHelpers.cs | 87 ++++++---- .../Text/Json/Reader/JsonReaderOptions.cs | 9 + .../Reader/Utf8JsonReader.MultiSegment.cs | 21 ++- .../System/Text/Json/Reader/Utf8JsonReader.cs | 38 +++-- .../Collection/RootLevelListConverter.cs | 73 ++++++++ .../Text/Json/Serialization/JsonConverter.cs | 9 +- .../JsonConverterOfT.ReadCore.cs | 94 +++++----- .../Json/Serialization/JsonConverterOfT.cs | 4 + .../Serialization/JsonSerializer.Read.Span.cs | 8 +- .../JsonSerializer.Read.Stream.cs | 159 +++++++++++++---- .../Metadata/JsonTypeInfoOfT.ReadHelper.cs | 39 +++-- .../Json/Serialization/ReadBufferState.cs | 1 - .../Text/Json/ThrowHelper.Serialization.cs | 6 +- .../JsonSourceGenerationOptionsTests.cs | 4 + .../JsonDocumentTests.cs | 31 ++++ .../JsonElementParseTests.cs | 32 ++++ .../JsonNode/JsonNodeTests.cs | 31 ++++ .../System.Text.Json.Tests/JsonTestHelper.cs | 6 + .../Serialization/ReadValueTests.cs | 23 +++ .../Stream.DeserializeAsyncEnumerable.cs | 105 ++++++++++++ .../Utf8JsonReaderTests.MultiSegment.cs | 153 +++++++++++++++++ .../Utf8JsonReaderTests.cs | 160 ++++++++++++++++++ 24 files changed, 956 insertions(+), 143 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/RootLevelListConverter.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 313a13c07a92f9..99d7be45b10773 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -187,6 +187,7 @@ public partial struct JsonReaderOptions { private int _dummyPrimitive; public bool AllowTrailingCommas { readonly get { throw null; } set { } } + public bool AllowMultipleValues { readonly get { throw null; } set { } } public System.Text.Json.JsonCommentHandling CommentHandling { readonly get { throw null; } set { } } public int MaxDepth { readonly get { throw null; } set { } } } @@ -247,7 +248,11 @@ public static partial class JsonSerializer public static System.Threading.Tasks.ValueTask DeserializeAsync(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable(System.IO.Stream utf8Json, bool topLevelValues, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable(System.IO.Stream utf8Json, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable(System.IO.Stream utf8Json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, bool topLevelValues, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Collections.Generic.IAsyncEnumerable DeserializeAsyncEnumerable(System.IO.Stream utf8Json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 8cc2f59351d853..0fb5fcd5acf44f 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -124,6 +124,7 @@ The System.Text.Json library is built-in as part of the shared framework in .NET + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index b69976e1541fd1..2b189272f5d1e5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -37,43 +37,72 @@ public static bool TryAdvanceWithOptionalReadAhead(this scoped ref Utf8JsonReade // No read-ahead necessary if we're at the final block of JSON data. bool readAhead = requiresReadAhead && !reader.IsFinalBlock; return readAhead ? TryAdvanceWithReadAhead(ref reader) : reader.Read(); + } - // The read-ahead method is not inlined - static bool TryAdvanceWithReadAhead(scoped ref Utf8JsonReader reader) + /// + /// Attempts to read ahead to the next root-level JSON value, if it exists. + /// + public static bool TryAdvanceToNextRootLevelValueWithOptionalReadAhead(this scoped ref Utf8JsonReader reader, bool requiresReadAhead, out bool isAtEndOfStream) + { + Debug.Assert(reader.AllowMultipleValues, "only supported by readers that support multiple values."); + Debug.Assert(reader.CurrentDepth == 0, "should only invoked for top-level values."); + + Utf8JsonReader checkpoint = reader; + if (!reader.Read()) { - // When we're reading ahead we always have to save the state - // as we don't know if the next token is a start object or array. - Utf8JsonReader restore = reader; + // If the reader didn't return any tokens and it's the final block, + // then there are no other JSON values to be read. + isAtEndOfStream = reader.IsFinalBlock; + reader = checkpoint; + return false; + } - if (!reader.Read()) - { - return false; - } + // We found another JSON value, read ahead accordingly. + isAtEndOfStream = false; + if (requiresReadAhead && !reader.IsFinalBlock) + { + // Perform full read-ahead to ensure the full JSON value has been buffered. + reader = checkpoint; + return TryAdvanceWithReadAhead(ref reader); + } - // Perform the actual read-ahead. - JsonTokenType tokenType = reader.TokenType; - if (tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray) + return true; + } + + private static bool TryAdvanceWithReadAhead(scoped ref Utf8JsonReader reader) + { + // When we're reading ahead we always have to save the state + // as we don't know if the next token is a start object or array. + Utf8JsonReader restore = reader; + + if (!reader.Read()) + { + return false; + } + + // Perform the actual read-ahead. + JsonTokenType tokenType = reader.TokenType; + if (tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray) + { + // Attempt to skip to make sure we have all the data we need. + bool complete = reader.TrySkipPartial(); + + // We need to restore the state in all cases as we need to be positioned back before + // the current token to either attempt to skip again or to actually read the value. + reader = restore; + + if (!complete) { - // Attempt to skip to make sure we have all the data we need. - bool complete = reader.TrySkipPartial(); - - // We need to restore the state in all cases as we need to be positioned back before - // the current token to either attempt to skip again or to actually read the value. - reader = restore; - - if (!complete) - { - // Couldn't read to the end of the object, exit out to get more data in the buffer. - return false; - } - - // Success, requeue the reader to the start token. - reader.ReadWithVerify(); - Debug.Assert(tokenType == reader.TokenType); + // Couldn't read to the end of the object, exit out to get more data in the buffer. + return false; } - return true; + // Success, requeue the reader to the start token. + reader.ReadWithVerify(); + Debug.Assert(tokenType == reader.TokenType); } + + return true; } #if !NET diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderOptions.cs index 5cc83c995b18cc..3d2b40b4f301bf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/JsonReaderOptions.cs @@ -70,5 +70,14 @@ public int MaxDepth /// By default, it's set to false, and is thrown if a trailing comma is encountered. /// public bool AllowTrailingCommas { get; set; } + + /// + /// Defines whether the should tolerate + /// zero or more top-level JSON values that are whitespace separated. + /// + /// + /// By default, it's set to false, and is thrown if trailing content is encountered after the first top-level JSON value. + /// + public bool AllowMultipleValues { get; set; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs index 22d3cc1c497d81..0aaac420d6d47b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs @@ -363,17 +363,13 @@ private bool ReadFirstTokenMultiSegment(byte first) } _tokenType = JsonTokenType.Number; _consumed += numberOfBytes; - return true; } else if (!ConsumeValueMultiSegment(first)) { return false; } - if (_tokenType == JsonTokenType.StartObject || _tokenType == JsonTokenType.StartArray) - { - _isNotPrimitive = true; - } + _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray; // Intentionally fall out of the if-block to return true } return true; @@ -1580,6 +1576,11 @@ private ConsumeTokenResult ConsumeNextTokenMultiSegment(byte marker) if (_bitStack.CurrentDepth == 0) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker); } @@ -1711,6 +1712,11 @@ private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentTokenMultiSegment() if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstTokenMultiSegment(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first); } @@ -2064,6 +2070,11 @@ private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkippedMultiS } else if (_bitStack.CurrentDepth == 0) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstTokenMultiSegment(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker); } else if (marker == JsonConstants.ListSeparator) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs index 5505094127a082..c2f9dbcfa88a16 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs @@ -4,7 +4,6 @@ using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Text.Json { @@ -55,6 +54,8 @@ public ref partial struct Utf8JsonReader internal readonly int ValueLength => HasValueSequence ? checked((int)ValueSequence.Length) : ValueSpan.Length; + internal readonly bool AllowMultipleValues => _readerOptions.AllowMultipleValues; + /// /// Gets the value of the last processed token as a ReadOnlySpan<byte> slice /// of the input payload. If the JSON is provided within a ReadOnlySequence<byte> @@ -280,7 +281,7 @@ public bool Read() if (!retVal) { - if (_isFinalBlock && TokenType == JsonTokenType.None) + if (_isFinalBlock && TokenType is JsonTokenType.None && !_readerOptions.AllowMultipleValues) { ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedJsonTokens); } @@ -929,7 +930,7 @@ private bool HasMoreData() return false; } - if (_tokenType != JsonTokenType.EndArray && _tokenType != JsonTokenType.EndObject) + if (_tokenType is not JsonTokenType.EndArray and not JsonTokenType.EndObject) { ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.InvalidEndOfJsonNonPrimitive); } @@ -991,17 +992,13 @@ private bool ReadFirstToken(byte first) _tokenType = JsonTokenType.Number; _consumed += numberOfBytes; _bytePositionInLine += numberOfBytes; - return true; } else if (!ConsumeValue(first)) { return false; } - if (_tokenType == JsonTokenType.StartObject || _tokenType == JsonTokenType.StartArray) - { - _isNotPrimitive = true; - } + _isNotPrimitive = _tokenType is JsonTokenType.StartObject or JsonTokenType.StartArray; // Intentionally fall out of the if-block to return true } return true; @@ -1016,10 +1013,10 @@ private void SkipWhiteSpace() byte val = localBuffer[_consumed]; // JSON RFC 8259 section 2 says only these 4 characters count, not all of the Unicode definitions of whitespace. - if (val != JsonConstants.Space && - val != JsonConstants.CarriageReturn && - val != JsonConstants.LineFeed && - val != JsonConstants.Tab) + if (val is not JsonConstants.Space and + not JsonConstants.CarriageReturn and + not JsonConstants.LineFeed and + not JsonConstants.Tab) { break; } @@ -1747,6 +1744,11 @@ private ConsumeTokenResult ConsumeNextToken(byte marker) if (_bitStack.CurrentDepth == 0) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker); } @@ -1869,6 +1871,11 @@ private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentToken() if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstToken(first) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first); } @@ -2033,7 +2040,7 @@ private ConsumeTokenResult ConsumeNextTokenFromLastNonCommentToken() } else { - Debug.Assert(_tokenType == JsonTokenType.EndArray || _tokenType == JsonTokenType.EndObject); + Debug.Assert(_tokenType is JsonTokenType.EndArray or JsonTokenType.EndObject); if (_inObject) { Debug.Assert(first != JsonConstants.CloseBrace); @@ -2207,6 +2214,11 @@ private ConsumeTokenResult ConsumeNextTokenUntilAfterAllCommentsAreSkipped(byte } else if (_bitStack.CurrentDepth == 0) { + if (_readerOptions.AllowMultipleValues) + { + return ReadFirstToken(marker) ? ConsumeTokenResult.Success : ConsumeTokenResult.NotEnoughDataRollBackState; + } + ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker); } else if (marker == JsonConstants.ListSeparator) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/RootLevelListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/RootLevelListConverter.cs new file mode 100644 index 00000000000000..a6968cd3fd6e98 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/RootLevelListConverter.cs @@ -0,0 +1,73 @@ +// 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.Generic; +using System.Diagnostics; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + /// + /// A specialized converter implementation used for root-level value + /// streaming in the JsonSerializer.DeserializeAsyncEnumerable methods. + /// + internal sealed class RootLevelListConverter : JsonResumableConverter> + { + private readonly JsonTypeInfo _elementTypeInfo; + private protected sealed override ConverterStrategy GetDefaultConverterStrategy() => ConverterStrategy.Enumerable; + internal override Type? ElementType => typeof(T); + + public RootLevelListConverter(JsonTypeInfo elementTypeInfo) + { + IsRootLevelMultiContentStreamingConverter = true; + _elementTypeInfo = elementTypeInfo; + } + + internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, scoped ref ReadStack state, out List? value) + { + Debug.Assert(reader.AllowMultipleValues, "Can only be used by readers allowing trailing content."); + + JsonConverter elementConverter = _elementTypeInfo.EffectiveConverter; + state.Current.JsonPropertyInfo = _elementTypeInfo.PropertyInfoForTypeInfo; + var results = (List?)state.Current.ReturnValue; + + while (true) + { + if (state.Current.PropertyState < StackFramePropertyState.ReadValue) + { + if (!reader.TryAdvanceToNextRootLevelValueWithOptionalReadAhead(elementConverter.RequiresReadAhead, out bool isAtEndOfStream)) + { + if (isAtEndOfStream) + { + // No more root-level JSON values in the stream + // complete the deserialization process. + value = results; + return true; + } + + // New root-level JSON value found, need to read more data. + value = default; + return false; + } + + state.Current.PropertyState = StackFramePropertyState.ReadValue; + } + + // Deserialize the next root-level JSON value. + if (!elementConverter.TryRead(ref reader, typeof(T), options, ref state, out T? element, out _)) + { + value = default; + return false; + } + + if (results is null) + { + state.Current.ReturnValue = results = []; + } + + results.Add(element); + state.Current.EndElement(); + } + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index fafa21ad0485d5..261a6cf433d6be 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -82,13 +82,18 @@ internal ConverterStrategy ConverterStrategy /// /// The converter supports polymorphic writes; only reserved for System.Object types. /// - internal bool CanBePolymorphic { get; set; } + internal bool CanBePolymorphic { get; init; } /// /// The serializer must read ahead all contents of the next JSON value /// before calling into the converter for deserialization. /// - internal bool RequiresReadAhead { get; set; } + internal bool RequiresReadAhead { get; private protected set; } + + /// + /// Whether the converter is a special root-level value streaming converter. + /// + internal bool IsRootLevelMultiContentStreamingConverter { get; init; } /// /// Used to support JsonObject as an extension property in a loosely-typed, trimmable manner. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs index f25fe48595360c..7240239b8647ba 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.ReadCore.cs @@ -1,18 +1,21 @@ // 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; + namespace System.Text.Json.Serialization { public partial class JsonConverter { - internal T? ReadCore( + internal bool ReadCore( ref Utf8JsonReader reader, + out T? value, JsonSerializerOptions options, ref ReadStack state) { try { - if (!state.IsContinuation) + if (!state.IsContinuation && !IsRootLevelMultiContentStreamingConverter) { // This is first call to the converter -- advance the reader // to the first JSON token and perform a read-ahead if necessary. @@ -22,63 +25,68 @@ public partial class JsonConverter { // This branch is hit when deserialization has completed in an earlier call // but we're still processing trailing whitespace. Return the result stored in the state machine. - return (T)result; + Debug.Assert(!reader.AllowMultipleValues, "should not be entered by converters allowing multiple values."); + value = (T)result; + } + else + { + value = default; } - return default; + return reader.IsFinalBlock; } } - bool success = TryRead(ref reader, state.Current.JsonTypeInfo.Type, options, ref state, out T? value, out _); + bool success = TryRead(ref reader, state.Current.JsonTypeInfo.Type, options, ref state, out value, out _); if (success) { - // Read any trailing whitespace. This will throw if JsonCommentHandling=Disallow. - // Avoiding setting ReturnValue for the final block; reader.Read() returns 'false' even when this is the final block. - if (!reader.Read() && !reader.IsFinalBlock) + if (!reader.AllowMultipleValues) { - // This method will re-enter if so set `ReturnValue` which will be returned during re-entry. - state.Current.ReturnValue = value; + // Read any trailing whitespace. This will throw if JsonCommentHandling=Disallow. + // Avoiding setting ReturnValue for the final block; reader.Read() returns 'false' even when this is the final block. + if (!reader.Read() && !reader.IsFinalBlock) + { + // This method will re-enter if so set `ReturnValue` which will be returned during re-entry. + state.Current.ReturnValue = value; + success = false; + } } } - return value; - } - catch (JsonReaderException ex) - { - ThrowHelper.ReThrowWithPath(ref state, ex); - return default; - } - catch (FormatException ex) when (ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) - { - ThrowHelper.ReThrowWithPath(ref state, reader, ex); - return default; - } - catch (InvalidOperationException ex) when (ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException) - { - ThrowHelper.ReThrowWithPath(ref state, reader, ex); - return default; - } - catch (JsonException ex) when (ex.Path == null) - { - // JsonExceptions where the Path property is already set - // typically originate from nested calls to JsonSerializer; - // treat these cases as any other exception type and do not - // overwrite any exception information. - - ThrowHelper.AddJsonExceptionInformation(ref state, reader, ex); - throw; + return success; } - catch (NotSupportedException ex) + catch (Exception ex) { - // If the message already contains Path, just re-throw. This could occur in serializer re-entry cases. - // To get proper Path semantics in re-entry cases, APIs that take 'state' need to be used. - if (ex.Message.Contains(" Path: ")) + switch (ex) { - throw; + case JsonReaderException jsonReaderEx: + ThrowHelper.ReThrowWithPath(ref state, jsonReaderEx); + break; + + case FormatException when ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException: + ThrowHelper.ReThrowWithPath(ref state, reader, ex); + break; + + case InvalidOperationException when ex.Source == ThrowHelper.ExceptionSourceValueToRethrowAsJsonException: + ThrowHelper.ReThrowWithPath(ref state, reader, ex); + break; + + case JsonException jsonEx when jsonEx.Path is null: + // JsonExceptions where the Path property is already set + // typically originate from nested calls to JsonSerializer; + // treat these cases as any other exception type and do not + // overwrite any exception information. + ThrowHelper.AddJsonExceptionInformation(ref state, reader, jsonEx); + break; + + case NotSupportedException when !ex.Message.Contains(" Path: "): + // If the message already contains Path, just re-throw. This could occur in serializer re-entry cases. + // To get proper Path semantics in re-entry cases, APIs that take 'state' need to be used. + ThrowHelper.ThrowNotSupportedException(ref state, reader, ex); + break; } - ThrowHelper.ThrowNotSupportedException(ref state, reader, ex); - return default; + throw; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index cce660169ac28c..9ac6c6d3692dd2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -526,6 +526,10 @@ internal void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, break; + case JsonTokenType.None: + Debug.Assert(IsRootLevelMultiContentStreamingConverter); + break; + default: if (isValueConverter) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Span.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Span.cs index 7a9a2bc028acb5..a90f4f791b3608 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Span.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Span.cs @@ -159,8 +159,8 @@ public static partial class JsonSerializer TValue? value = jsonTypeInfo.Deserialize(ref reader, ref state); - // The reader should have thrown if we have remaining bytes. - Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length)); + // The reader should have thrown if we have remaining bytes, unless AllowMultipleValues is true. + Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length) || reader.CurrentState.Options.AllowMultipleValues); return value; } @@ -176,8 +176,8 @@ public static partial class JsonSerializer object? value = jsonTypeInfo.DeserializeAsObject(ref reader, ref state); - // The reader should have thrown if we have remaining bytes. - Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length)); + // The reader should have thrown if we have remaining bytes, unless AllowMultipleValues is true. + Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length) || reader.CurrentState.Options.AllowMultipleValues); return value; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs index f3a9ba2d4b0aa8..75328f71473c36 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Stream.cs @@ -424,6 +424,36 @@ public static partial class JsonSerializer Stream utf8Json, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) + { + return DeserializeAsyncEnumerable(utf8Json, topLevelValues: false, options, cancellationToken); + } + + /// + /// Wraps the UTF-8 encoded text into an + /// that can be used to deserialize sequences of JSON values in a streaming manner. + /// + /// The element type to deserialize asynchronously. + /// An representation of the provided JSON sequence. + /// JSON data to parse. + /// to deserialize from a sequence of top-level JSON values, or to deserialize from a single top-level array. + /// Options to control the behavior during reading. + /// The that can be used to cancel the read operation. + /// + /// is . + /// + /// + /// When is set to , treats the stream as a sequence of + /// whitespace separated top-level JSON values and attempts to deserialize each value into . + /// When is set to , treats the stream as a JSON array and + /// attempts to serialize each element into . + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] + public static IAsyncEnumerable DeserializeAsyncEnumerable( + Stream utf8Json, + bool topLevelValues, + JsonSerializerOptions? options = null, + CancellationToken cancellationToken = default) { if (utf8Json is null) { @@ -431,7 +461,7 @@ public static partial class JsonSerializer } JsonTypeInfo jsonTypeInfo = GetTypeInfo(options); - return DeserializeAsyncEnumerableCore(utf8Json, jsonTypeInfo, cancellationToken); + return DeserializeAsyncEnumerableCore(utf8Json, jsonTypeInfo, topLevelValues, cancellationToken); } /// @@ -450,6 +480,34 @@ public static partial class JsonSerializer Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + { + return DeserializeAsyncEnumerable(utf8Json, jsonTypeInfo, topLevelValues: false, cancellationToken); + } + + /// + /// Wraps the UTF-8 encoded text into an + /// that can be used to deserialize sequences of JSON values in a streaming manner. + /// + /// The element type to deserialize asynchronously. + /// An representation of the provided JSON sequence. + /// JSON data to parse. + /// Metadata about the element type to convert. + /// Whether to deserialize from a sequence of top-level JSON values. + /// The that can be used to cancel the read operation. + /// + /// or is . + /// + /// + /// When is set to , treats the stream as a sequence of + /// whitespace separated top-level JSON values and attempts to deserialize each value into . + /// When is set to , treats the stream as a JSON array and + /// attempts to serialize each element into . + /// + public static IAsyncEnumerable DeserializeAsyncEnumerable( + Stream utf8Json, + JsonTypeInfo jsonTypeInfo, + bool topLevelValues, + CancellationToken cancellationToken = default) { if (utf8Json is null) { @@ -462,49 +520,68 @@ public static partial class JsonSerializer } jsonTypeInfo.EnsureConfigured(); - return DeserializeAsyncEnumerableCore(utf8Json, jsonTypeInfo, cancellationToken); + return DeserializeAsyncEnumerableCore(utf8Json, jsonTypeInfo, topLevelValues, cancellationToken); } - private static IAsyncEnumerable DeserializeAsyncEnumerableCore(Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken) + private static IAsyncEnumerable DeserializeAsyncEnumerableCore( + Stream utf8Json, + JsonTypeInfo jsonTypeInfo, + bool topLevelValues, + CancellationToken cancellationToken) { Debug.Assert(jsonTypeInfo.IsConfigured); - JsonTypeInfo> queueTypeInfo = jsonTypeInfo._asyncEnumerableQueueTypeInfo is { } cachedQueueTypeInfo - ? (JsonTypeInfo>)cachedQueueTypeInfo - : CreateQueueTypeInfo(jsonTypeInfo); + JsonTypeInfo> listTypeInfo; + JsonReaderOptions readerOptions = jsonTypeInfo.Options.GetReaderOptions(); + if (topLevelValues) + { + listTypeInfo = GetOrAddListTypeInfoForRootLevelValueMode(jsonTypeInfo); + readerOptions.AllowMultipleValues = true; + } + else + { + listTypeInfo = GetOrAddListTypeInfoForArrayMode(jsonTypeInfo); + } - return CreateAsyncEnumerable(utf8Json, queueTypeInfo, cancellationToken); + return CreateAsyncEnumerableFromArray(utf8Json, listTypeInfo, readerOptions, cancellationToken); - static async IAsyncEnumerable CreateAsyncEnumerable(Stream utf8Json, JsonTypeInfo> queueTypeInfo, [EnumeratorCancellation] CancellationToken cancellationToken) + static async IAsyncEnumerable CreateAsyncEnumerableFromArray( + Stream utf8Json, + JsonTypeInfo> listTypeInfo, + JsonReaderOptions readerOptions, + [EnumeratorCancellation] CancellationToken cancellationToken) { - Debug.Assert(queueTypeInfo.IsConfigured); - JsonSerializerOptions options = queueTypeInfo.Options; - var bufferState = new ReadBufferState(options.DefaultBufferSize); - ReadStack readStack = default; - readStack.Initialize(queueTypeInfo, supportContinuation: true); + Debug.Assert(listTypeInfo.IsConfigured); - var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); + ReadBufferState bufferState = new(listTypeInfo.Options.DefaultBufferSize); + ReadStack readStack = default; + readStack.Initialize(listTypeInfo, supportContinuation: true); + JsonReaderState jsonReaderState = new(readerOptions); try { + bool success; do { bufferState = await bufferState.ReadFromStreamAsync(utf8Json, cancellationToken, fillBuffer: false).ConfigureAwait(false); - queueTypeInfo.ContinueDeserialize( + success = listTypeInfo.ContinueDeserialize( ref bufferState, ref jsonReaderState, - ref readStack); + ref readStack, + out List? _); if (readStack.Current.ReturnValue is { } returnValue) { - var queue = (Queue)returnValue!; - while (queue.TryDequeue(out T? element)) + var list = (List)returnValue; + foreach (T? item in list) { - yield return element; + yield return item; } + + list.Clear(); } - } - while (!bufferState.IsFinalBlock); + + } while (!success); } finally { @@ -512,19 +589,41 @@ static async IAsyncEnumerable CreateAsyncEnumerable(Stream utf8Json, JsonType } } - static JsonTypeInfo> CreateQueueTypeInfo(JsonTypeInfo jsonTypeInfo) + static JsonTypeInfo> GetOrAddListTypeInfoForArrayMode(JsonTypeInfo elementTypeInfo) + { + if (elementTypeInfo._asyncEnumerableArrayTypeInfo != null) + { + return (JsonTypeInfo>)elementTypeInfo._asyncEnumerableArrayTypeInfo; + } + + var converter = new ListOfTConverter, T>(); + var listTypeInfo = new JsonTypeInfo>(converter, elementTypeInfo.Options) + { + CreateObject = static () => new List(), + ElementTypeInfo = elementTypeInfo, + }; + + listTypeInfo.EnsureConfigured(); + elementTypeInfo._asyncEnumerableArrayTypeInfo = listTypeInfo; + return listTypeInfo; + } + + static JsonTypeInfo> GetOrAddListTypeInfoForRootLevelValueMode(JsonTypeInfo elementTypeInfo) { - var queueConverter = new QueueOfTConverter, T>(); - var queueTypeInfo = new JsonTypeInfo>(queueConverter, jsonTypeInfo.Options) + if (elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo != null) + { + return (JsonTypeInfo>)elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo; + } + + var converter = new RootLevelListConverter(elementTypeInfo); + var listTypeInfo = new JsonTypeInfo>(converter, elementTypeInfo.Options) { - CreateObject = static () => new Queue(), - ElementTypeInfo = jsonTypeInfo, - NumberHandling = jsonTypeInfo.Options.NumberHandling, + ElementTypeInfo = elementTypeInfo, }; - queueTypeInfo.EnsureConfigured(); - jsonTypeInfo._asyncEnumerableQueueTypeInfo = queueTypeInfo; - return queueTypeInfo; + listTypeInfo.EnsureConfigured(); + elementTypeInfo._asyncEnumerableRootLevelValueTypeInfo = listTypeInfo; + return listTypeInfo; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.ReadHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.ReadHelper.cs index c2d1e73d18cb91..bfe0f394d42717 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.ReadHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.ReadHelper.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.CompilerServices; -using System.Text.Json.Serialization.Converters; using System.Threading; using System.Threading.Tasks; @@ -19,7 +17,9 @@ public partial class JsonTypeInfo internal T? Deserialize(ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(IsConfigured); - return EffectiveConverter.ReadCore(ref reader, Options, ref state); + bool success = EffectiveConverter.ReadCore(ref reader, out T? result, Options, ref state); + Debug.Assert(success, "Should only return false for async deserialization"); + return result; } internal async ValueTask DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken) @@ -36,9 +36,13 @@ public partial class JsonTypeInfo while (true) { bufferState = await bufferState.ReadFromStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); - T? value = ContinueDeserialize(ref bufferState, ref jsonReaderState, ref readStack); + bool success = ContinueDeserialize( + ref bufferState, + ref jsonReaderState, + ref readStack, + out T? value); - if (bufferState.IsFinalBlock) + if (success) { return value; } @@ -64,9 +68,13 @@ public partial class JsonTypeInfo while (true) { bufferState.ReadFromStream(utf8Json); - T? value = ContinueDeserialize(ref bufferState, ref jsonReaderState, ref readStack); + bool success = ContinueDeserialize( + ref bufferState, + ref jsonReaderState, + ref readStack, + out T? value); - if (bufferState.IsFinalBlock) + if (success) { return value; } @@ -79,11 +87,12 @@ public partial class JsonTypeInfo } /// - /// Caches a JsonTypeInfo<Queue<T>> instance used by the DeserializeAsyncEnumerable method. + /// Caches JsonTypeInfo<List<T>> instances used by the DeserializeAsyncEnumerable method. /// Store as a non-generic type to avoid triggering generic recursion in the AOT compiler. /// cf. https://github.com/dotnet/runtime/issues/85184 /// - internal JsonTypeInfo? _asyncEnumerableQueueTypeInfo; + internal JsonTypeInfo? _asyncEnumerableArrayTypeInfo; + internal JsonTypeInfo? _asyncEnumerableRootLevelValueTypeInfo; internal sealed override object? DeserializeAsObject(ref Utf8JsonReader reader, ref ReadStack state) => Deserialize(ref reader, ref state); @@ -97,18 +106,22 @@ public partial class JsonTypeInfo internal sealed override object? DeserializeAsObject(Stream utf8Json) => Deserialize(utf8Json); - internal T? ContinueDeserialize( + internal bool ContinueDeserialize( ref ReadBufferState bufferState, ref JsonReaderState jsonReaderState, - ref ReadStack readStack) + ref ReadStack readStack, + out T? value) { var reader = new Utf8JsonReader(bufferState.Bytes, bufferState.IsFinalBlock, jsonReaderState); + bool success = EffectiveConverter.ReadCore(ref reader, out value, Options, ref readStack); - T? value = EffectiveConverter.ReadCore(ref reader, Options, ref readStack); Debug.Assert(reader.BytesConsumed <= bufferState.Bytes.Length); + Debug.Assert(!bufferState.IsFinalBlock || reader.AllowMultipleValues || reader.BytesConsumed == bufferState.Bytes.Length, + "The reader should have thrown if we have remaining bytes."); + bufferState.AdvanceBuffer((int)reader.BytesConsumed); jsonReaderState = reader.CurrentState; - return value; + return success; } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs index dc624235377a3f..56f8d9e7207218 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs @@ -105,7 +105,6 @@ public void ReadFromStream(Stream utf8Json) public void AdvanceBuffer(int bytesConsumed) { Debug.Assert(bytesConsumed <= _count); - Debug.Assert(!_isFinalBlock || _count == bytesConsumed, "The reader should have thrown if we have remaining bytes."); _count -= bytesConsumed; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 226e60c80c2828..fa2fcea774d9ea 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -558,9 +558,9 @@ public static void ThrowInvalidOperationException_NodeJsonObjectCustomConverterN } [DoesNotReturn] - public static void ThrowNotSupportedException(scoped ref ReadStack state, in Utf8JsonReader reader, NotSupportedException ex) + public static void ThrowNotSupportedException(scoped ref ReadStack state, in Utf8JsonReader reader, Exception innerException) { - string message = ex.Message; + string message = innerException.Message; // The caller should check to ensure path is not already set. Debug.Assert(!message.Contains(" Path: ")); @@ -582,7 +582,7 @@ public static void ThrowNotSupportedException(scoped ref ReadStack state, in Utf long bytePositionInLine = reader.CurrentState._bytePositionInLine; message += $" Path: {state.JsonPath()} | LineNumber: {lineNumber} | BytePositionInLine: {bytePositionInLine}."; - throw new NotSupportedException(message, ex); + throw new NotSupportedException(message, innerException); } [DoesNotReturn] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs index 74aee9393a2f23..dae312271def5c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs @@ -77,6 +77,8 @@ public static void ContextWithAllOptionsSet_GeneratesExpectedOptions() PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonNamingPolicy.KebabCaseUpper, ReadCommentHandling = JsonCommentHandling.Skip, + RespectNullableAnnotations = true, + RespectRequiredConstructorParameters = true, UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, WriteIndented = true, @@ -108,6 +110,8 @@ public static void ContextWithAllOptionsSet_GeneratesExpectedOptions() PropertyNameCaseInsensitive = true, PropertyNamingPolicy = JsonKnownNamingPolicy.KebabCaseUpper, ReadCommentHandling = JsonCommentHandling.Skip, + RespectNullableAnnotations = true, + RespectRequiredConstructorParameters = true, UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, WriteIndented = true, diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs index 8d8d281876487d..518ab0f46e3cf5 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs @@ -3406,6 +3406,37 @@ public static void ParseReaderTerminatesAfterPropertyName(int segmentCount) Assert.Equal(startPosition, reader.BytesConsumed); } + [Fact] + public static void ParseValue_AllowMultipleValues_TrailingJson() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] [43]"u8, options); + + using JsonDocument doc1 = JsonDocument.ParseValue(ref reader); + Assert.Equal("[null,false,42,{},[1]]", doc1.RootElement.GetRawText()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + Assert.True(reader.Read()); + using JsonDocument doc2 = JsonDocument.ParseValue(ref reader); + Assert.Equal("[43]", doc2.RootElement.GetRawText()); + + Assert.False(reader.Read()); + } + + + [Fact] + public static void ParseValue_AllowMultipleValues_TrailingContent() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] "u8, options); + + using JsonDocument doc = JsonDocument.ParseValue(ref reader); + Assert.Equal("[null,false,42,{},[1]]", doc.RootElement.GetRawText()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); + } + [Fact] public static void EnsureResizeSucceeds() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonElementParseTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonElementParseTests.cs index 3ff2d37be74289..15a399c01fbada 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonElementParseTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonElementParseTests.cs @@ -49,6 +49,38 @@ public static void TryParseValue(string json, JsonValueKind kind) Assert.False(element!.Value.SniffDocument().IsDisposable()); } + [Fact] + public static void ParseValue_AllowMultipleValues_TrailingJson() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] [43]"u8, options); + + JsonElement element; + element = JsonElement.ParseValue(ref reader); + Assert.Equal("[null,false,42,{},[1]]", element.GetRawText()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + Assert.True(reader.Read()); + element = JsonElement.ParseValue(ref reader); + Assert.Equal("[43]", element.GetRawText()); + + Assert.False(reader.Read()); + } + + + [Fact] + public static void ParseValue_AllowMultipleValues_TrailingContent() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] "u8, options); + + JsonElement element = JsonElement.ParseValue(ref reader); + Assert.Equal("[null,false,42,{},[1]]", element.GetRawText()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); + } + public static IEnumerable ElementParsePartialDataCases { get diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs index 3e398477c82c8e..b04f3209061054 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonNodeTests.cs @@ -47,6 +47,37 @@ public static void JsonTypes_Deserialize() JsonType_Deserializes_Null(); } + [Fact] + public static void Parse_AllowMultipleValues_TrailingJson() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] [43]"u8, options); + + JsonNode node = JsonNode.Parse(ref reader); + Assert.Equal("[null,false,42,{},[1]]", node.ToJsonString()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + Assert.True(reader.Read()); + node = JsonNode.Parse(ref reader); + Assert.Equal("[43]", node.ToJsonString()); + + Assert.False(reader.Read()); + } + + + [Fact] + public static void Parse_AllowMultipleValues_TrailingContent() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + var reader = new Utf8JsonReader("[null,false,42,{},[1]] "u8, options); + + JsonNode node = JsonNode.Parse(ref reader); + Assert.Equal("[null,false,42,{},[1]]", node.ToJsonString()); + Assert.Equal(JsonTokenType.EndArray, reader.TokenType); + + JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); + } + private static void JsonType_Deserializes_Null() where TNode : JsonNode { Assert.Null(JsonSerializer.Deserialize("null")); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs index adaff17da693c2..a3c139ee4831ee 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs @@ -186,6 +186,12 @@ public static ReadOnlySequence CreateSegments(byte[] data, int firstSplit, return new ReadOnlySequence(firstSegment, 0, thirdSegment, thirdMem.Length); } + public static ReadOnlySequence GetSequence(string json, int segmentSize) + { + byte[] encoding = Encoding.UTF8.GetBytes(json); + return GetSequence(encoding, segmentSize); + } + public static ReadOnlySequence GetSequence(byte[] dataUtf8, int segmentSize) { int numberOfSegments = dataUtf8.Length / segmentSize + 1; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs index db896fa6367828..fd74be8191817e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Collections.Generic; using System.IO; using Xunit; @@ -758,6 +759,28 @@ static void ReadAndValidate(byte[] utf8, JsonSerializerOptions options, JsonRead } } } + + [Fact] + public static void ReadSimpleList_AllowMultipleValues_TrailingJson() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + ReadOnlySpan json = "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] null"u8; + var reader = new Utf8JsonReader(json, options); + + List result = JsonSerializer.Deserialize>(ref reader); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], result); + } + + [Fact] + public static void ReadSimpleList_AllowMultipleValues_TrailingContent() + { + var options = new JsonReaderOptions { AllowMultipleValues = true }; + ReadOnlySpan json = "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] !!!!"u8; + var reader = new Utf8JsonReader(json, options); + + List result = JsonSerializer.Deserialize>(ref reader); + Assert.Equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], result); + } } // From https://github.com/dotnet/runtime/issues/882 diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs index aa578a51e9c8ec..d9da4a9e572222 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs @@ -99,6 +99,111 @@ public static async Task DeserializeAsyncEnumerable_ShouldStreamPartialData(Dese } } + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(5)] + [InlineData(100)] + [InlineData(1000)] + public static async Task DeserializeAsyncEnumerable_Object_TopLevelValues(int count) + { + JsonSerializerOptions options = new() { DefaultBufferSize = 1 }; + string json = GenerateJsonTopLevelValues(count); + using var stream = new Utf8MemoryStream(json); + + IAsyncEnumerable asyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable(stream, topLevelValues:true, options); + + int i = 0; + await foreach (SimpleTestClass item in asyncEnumerable) + { + Assert.Equal(i++, item.MyInt32); + item.MyInt32 = 2; // Put correct value back for Verify() + item.Verify(); + } + + Assert.Equal(count, i); + + static string GenerateJsonTopLevelValues(int count) + { + StringBuilder sb = new(); + for (int i = 0; i < count; i++) + { + var obj = new SimpleTestClass(); + obj.Initialize(); + obj.MyInt32 = i; // verify order correctness + + sb.Append(JsonSerializer.Serialize(obj)); + sb.Append((i % 5) switch { 0 => "", 1 => " ", 2 => "\t", 3 => "\r\n", _ => " \n\n\n\n\n " }); + } + + return sb.ToString(); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(5)] + [InlineData(100)] + [InlineData(1000)] + public static async Task DeserializeAsyncEnumerable_Array_TopLevelValues(int count) + { + JsonSerializerOptions options = new() { DefaultBufferSize = 1 }; + string json = GenerateJsonTopLevelValues(count); + using var stream = new Utf8MemoryStream(json); + + IAsyncEnumerable?> asyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable?>(stream, topLevelValues:true, options); + + int i = 0; + await foreach (List? item in asyncEnumerable) + { + switch (i++ % 4) + { + case 0: + Assert.Null(item); + break; + case 1: + Assert.Equal([], item); + break; + case 2: + Assert.Equal([1], item); + break; + case 3: + Assert.Equal([1, 2, 3], item); + break; + } + } + + Assert.Equal(count, i); + + static string GenerateJsonTopLevelValues(int count) + { + StringBuilder sb = new(); + for (int i = 0; i < count; i++) + { + sb.Append((i % 4) switch { 0 => " null", 1 => "[]", 2 => "[1]", _ => "[1,2,3]" }); + sb.Append((i % 5) switch { 0 => "", 1 => " ", 2 => "\t", 3 => "\r\n", _ => " \n\n\n\n\n " }); + } + + return sb.ToString(); + } + } + + [Fact] + public static async Task DeserializeAsyncEnumerable_TopLevelValues_TrailingData_ThrowsJsonException() + { + JsonSerializerOptions options = new() { DefaultBufferSize = 1 }; + using var stream = new Utf8MemoryStream("""[] [1] [1,2,3] """); + + IAsyncEnumerable> asyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable>(stream, topLevelValues:true, options); + await using var asyncEnumerator = asyncEnumerable.GetAsyncEnumerator(); + + await Assert.ThrowsAnyAsync(async () => + { + while (await asyncEnumerator.MoveNextAsync()); + }); + } + [Theory] [InlineData(DeserializeAsyncEnumerableOverload.JsonSerializerOptions)] [InlineData(DeserializeAsyncEnumerableOverload.JsonTypeInfo)] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs index 34b4efec865938..f5f00d23116efa 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.MultiSegment.cs @@ -2314,5 +2314,158 @@ public static void TestTokenStartIndexMultiSegment_WithTrailingCommasAndComments Assert.Equal(utf8.Length - 1, reader.TokenStartIndex); } } + + [Theory] + [InlineData("{ } 1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("{ }1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("{ }\t\r\n 1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("1 3.14 false null", new[] { JsonTokenType.Number, JsonTokenType.Number, JsonTokenType.False, JsonTokenType.Null })] + [InlineData("42", new[] { JsonTokenType.Number })] + [InlineData("\"str\"\"str\"null", new[] { JsonTokenType.String, JsonTokenType.String, JsonTokenType.Null })] + [InlineData("[]{}[]", new[] { JsonTokenType.StartArray, JsonTokenType.EndArray, JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.StartArray, JsonTokenType.EndArray })] + public static void AllowMultipleValues_MultiSegment(string json, JsonTokenType[] expectedSequence) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new(JsonTestHelper.GetSequence(json, 1), options); + + Assert.Equal(JsonTokenType.None, reader.TokenType); + + foreach (JsonTokenType expected in expectedSequence) + { + Assert.True(reader.Read()); + Assert.Equal(expected, reader.TokenType); + } + + Assert.False(reader.Read()); + } + + [Fact] + public static void AllowMultipleValues_NonJsonTrailingData_ThrowsJsonException_MultiSegment() + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new(JsonTestHelper.GetSequence("{ } not JSON", 1), options); + Assert.Equal(JsonTokenType.None, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + + JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); + } + + [Theory] + [InlineData(JsonCommentHandling.Allow)] + [InlineData(JsonCommentHandling.Skip)] + public static void AllowMultipleValues_CommentSeparated_MultiSegment(JsonCommentHandling commentHandling) + { + JsonReaderOptions options = new() { AllowMultipleValues = true, CommentHandling = commentHandling }; + var reader = new Utf8JsonReader(JsonTestHelper.GetSequence("{ } /* I'm a comment */ 1", 1), options); + Assert.Equal(JsonTokenType.None, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + + Assert.True(reader.Read()); + + if (commentHandling is JsonCommentHandling.Allow) + { + Assert.Equal(JsonTokenType.Comment, reader.TokenType); + Assert.True(reader.Read()); + } + + Assert.Equal(JsonTokenType.Number, reader.TokenType); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_SkipMultipleRepeatingValues_MultiSegment(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + string payload = string.Join("\r\n", Enumerable.Repeat(jsonValue, 10)); + var reader = new Utf8JsonReader(JsonTestHelper.GetSequence(payload, 1), options); + + for (int i = 0; i < 10; i++) + { + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + } + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_PartialData_MultiSegment(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + JsonReaderState state = new(options); + Utf8JsonReader reader = new(JsonTestHelper.GetSequence(jsonValue + " ", 1), isFinalBlock: false, state); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + Assert.True(reader.TrySkip()); + Assert.False(reader.Read()); + + reader = new Utf8JsonReader(JsonTestHelper.GetSequence(jsonValue, 1), isFinalBlock: true, reader.CurrentState); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_Comments_PartialData_MultiSegment(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true, CommentHandling = JsonCommentHandling.Skip }; + JsonReaderState state = new(options); + Utf8JsonReader reader = new(JsonTestHelper.GetSequence(jsonValue + "/* comment */", 1), isFinalBlock: false, state); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + Assert.True(reader.TrySkip()); + Assert.False(reader.Read()); + + reader = new Utf8JsonReader(JsonTestHelper.GetSequence(jsonValue, 1), isFinalBlock: true, reader.CurrentState); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("")] + [InlineData("\t\r\n")] + [InlineData(" \t\t ")] + public static void AllowMultipleValues_NoJsonContent_ReturnsFalse_MultiSegment(string json) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new(JsonTestHelper.GetSequence(json, 1), options); + + Assert.True(reader.IsFinalBlock); + Assert.False(reader.Read()); + Assert.Equal(JsonTokenType.None, reader.TokenType); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs index 98fc08a82740cf..c49c65ef291d71 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonReaderTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using Microsoft.DotNet.XUnitExtensions; using Newtonsoft.Json; using Xunit; @@ -31,6 +32,7 @@ public static void DefaultUtf8JsonReader() Assert.Equal(0, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); Assert.False(json.Read()); @@ -129,6 +131,7 @@ public static void InitialState() Assert.Equal(64, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); Assert.True(json.Read()); @@ -153,6 +156,7 @@ public static void InitialStateSimpleCtor() Assert.Equal(64, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); Assert.True(json.Read()); @@ -178,6 +182,7 @@ public static void StateRecovery() Assert.Equal(64, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); Assert.True(json.Read()); @@ -195,6 +200,7 @@ public static void StateRecovery() Assert.Equal(64, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); JsonReaderState state = json.CurrentState; @@ -214,6 +220,7 @@ public static void StateRecovery() Assert.Equal(64, json.CurrentState.Options.MaxDepth); Assert.False(json.CurrentState.Options.AllowTrailingCommas); + Assert.False(json.CurrentState.Options.AllowMultipleValues); Assert.Equal(JsonCommentHandling.Disallow, json.CurrentState.Options.CommentHandling); Assert.True(json.Read()); @@ -3911,6 +3918,159 @@ static void Test(ref Utf8JsonReader reader) } } + [Theory] + [InlineData("{ } 1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("{ }1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("{ }\t\r\n 1", new[] { JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.Number })] + [InlineData("1 3.14 false null", new[] { JsonTokenType.Number, JsonTokenType.Number, JsonTokenType.False, JsonTokenType.Null })] + [InlineData("42", new[] { JsonTokenType.Number })] + [InlineData("\"str\"\"str\"null", new[] { JsonTokenType.String, JsonTokenType.String, JsonTokenType.Null })] + [InlineData("[]{}[]", new[] { JsonTokenType.StartArray, JsonTokenType.EndArray, JsonTokenType.StartObject, JsonTokenType.EndObject, JsonTokenType.StartArray, JsonTokenType.EndArray })] + public static void AllowMultipleValues(string json, JsonTokenType[] expectedSequence) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json), options); + + Assert.Equal(JsonTokenType.None, reader.TokenType); + + foreach (JsonTokenType expected in expectedSequence) + { + Assert.True(reader.Read()); + Assert.Equal(expected, reader.TokenType); + } + + Assert.False(reader.Read()); + } + + [Fact] + public static void AllowMultipleValues_NonJsonTrailingData_ThrowsJsonException() + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new("{ } not JSON"u8, options); + Assert.Equal(JsonTokenType.None, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + + JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); + } + + [Theory] + [InlineData(JsonCommentHandling.Allow)] + [InlineData(JsonCommentHandling.Skip)] + public static void AllowMultipleValues_CommentSeparated(JsonCommentHandling commentHandling) + { + JsonReaderOptions options = new() { AllowMultipleValues = true, CommentHandling = commentHandling }; + Utf8JsonReader reader = new("{ } /* I'm a comment */ 1"u8, options); + Assert.Equal(JsonTokenType.None, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.StartObject, reader.TokenType); + Assert.True(reader.Read()); + Assert.Equal(JsonTokenType.EndObject, reader.TokenType); + + Assert.True(reader.Read()); + + if (commentHandling is JsonCommentHandling.Allow) + { + Assert.Equal(JsonTokenType.Comment, reader.TokenType); + Assert.True(reader.Read()); + } + + Assert.Equal(JsonTokenType.Number, reader.TokenType); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_SkipMultipleRepeatingValues(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + string payload = string.Join("\r\n", Enumerable.Repeat(jsonValue, 10)); + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(payload), options); + + for (int i = 0; i < 10; i++) + { + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + } + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_PartialData(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + JsonReaderState state = new(options); + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(jsonValue + " "), isFinalBlock: false, state); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + Assert.True(reader.TrySkip()); + Assert.False(reader.Read()); + + reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(jsonValue), isFinalBlock: true, reader.CurrentState); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("null", JsonTokenType.Null)] + [InlineData("false", JsonTokenType.False)] + [InlineData("true", JsonTokenType.True)] + [InlineData("42", JsonTokenType.Number)] + [InlineData("\"string\"", JsonTokenType.String)] + [InlineData("""{ "key" : "value" }""", JsonTokenType.StartObject)] + [InlineData("""[{},1,"string",false, true]""", JsonTokenType.StartArray)] + public static void AllowMultipleValues_Comments_PartialData(string jsonValue, JsonTokenType firstTokenType) + { + JsonReaderOptions options = new() { AllowMultipleValues = true, CommentHandling = JsonCommentHandling.Skip }; + JsonReaderState state = new(options); + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(jsonValue + "/* comment */"), isFinalBlock: false, state); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + Assert.True(reader.TrySkip()); + Assert.False(reader.Read()); + + reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(jsonValue), isFinalBlock: true, reader.CurrentState); + + Assert.True(reader.Read()); + Assert.Equal(firstTokenType, reader.TokenType); + reader.Skip(); + Assert.False(reader.Read()); + } + + [Theory] + [InlineData("")] + [InlineData("\t\r\n")] + [InlineData(" \t\t ")] + public static void AllowMultipleValues_NoJsonContent_ReturnsFalse(string json) + { + JsonReaderOptions options = new() { AllowMultipleValues = true }; + Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json), options); + + Assert.True(reader.IsFinalBlock); + Assert.False(reader.Read()); + Assert.Equal(JsonTokenType.None, reader.TokenType); + } + public static IEnumerable TestCases { get From 7fae7f80e2c6f26cdadd6d404c551e4ced281561 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 4 Jul 2024 12:19:09 -0400 Subject: [PATCH 13/72] Improve build error on systems that have a 32-bit time_t (#104368) We now require a 64-bit time_t, even on ARM32. This adds a static_assert to ensure time_t is 64-bit, and if not, produce a compile time error. This also uses a constant instead of `(time_t)INT_MAX + 1` since, if that overflows, it is UB because time_t is a signed type. Contributes to #104333 --- .../libs/System.Security.Cryptography.Native/opensslshim.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.c b/src/native/libs/System.Security.Cryptography.Native/opensslshim.c index f3b425bf36ce3b..8d48552136b61e 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.c +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.c @@ -233,9 +233,11 @@ void InitializeOpenSSLShim(void) } #if defined(TARGET_ARM) && defined(TARGET_LINUX) + c_static_assert_msg(sizeof(time_t) == 8, "Build requires 64-bit time_t."); + // This value will represent a time in year 2038 if 64-bit time is used, // or 1901 if the lower 32 bits are interpreted as a 32-bit time_t value. - time_t timeVal = (time_t)INT_MAX + 1; + time_t timeVal = (time_t)0x80000000U; struct tm tmVal = { 0 }; // Detect whether openssl is using 32-bit or 64-bit time_t. From 2d0dff44ea5c6d3c7d42b80b77bf8abc90072049 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 4 Jul 2024 09:31:43 -0700 Subject: [PATCH 14/72] Use the maskBaseType when consuming a mask directly as part of KORTEST (#104364) --- src/coreclr/jit/lowerxarch.cpp | 44 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index c87be188763bb9..cfb2b2bd97c90c 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -2309,10 +2309,24 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm GenTree* op2 = node->Op(2); GenCondition cmpCnd = (cmpOp == GT_EQ) ? GenCondition::EQ : GenCondition::NE; + // We may need to change the base type to match the underlying mask size to ensure + // the right instruction variant is picked. If the op_Equality was for TYP_INT but + // the mask was for TYP_DOUBLE then we'd pick kortestw when we really want kortestb. + // Changing the size is fine in some scenarios, such as comparison against Zero or + // AllBitsSet, but not in other scenarios such as against an arbitrary mask. + + CorInfoType maskBaseJitType = simdBaseJitType; + var_types maskBaseType = simdBaseType; + if (op1Msk->OperIsHWIntrinsic(NI_EVEX_ConvertMaskToVector)) { - op1Msk = op1Msk->AsHWIntrinsic()->Op(1); + GenTreeHWIntrinsic* cvtMaskToVector = op1Msk->AsHWIntrinsic(); + + op1Msk = cvtMaskToVector->Op(1); assert(varTypeIsMask(op1Msk)); + + maskBaseJitType = cvtMaskToVector->GetSimdBaseJitType(); + maskBaseType = cvtMaskToVector->GetSimdBaseType(); } if (!varTypeIsFloating(simdBaseType) && (simdSize != 64) && op2->IsVectorZero() && @@ -2465,7 +2479,7 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm GenTree* nextNode = node->gtNext; NamedIntrinsic maskIntrinsicId = NI_EVEX_CompareEqualMask; - uint32_t count = simdSize / genTypeSize(simdBaseType); + uint32_t count = simdSize / genTypeSize(maskBaseType); // KORTEST does a bitwise or on the result and sets ZF if it is zero and CF if it is all // bits set. Because of this, when we have at least 8 elements to compare we can use a @@ -2650,14 +2664,14 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm GenTree* cnsNode; maskNode = comp->gtNewSimdHWIntrinsicNode(TYP_MASK, maskNode, NI_EVEX_NotMask, - simdBaseJitType, simdSize); + maskBaseJitType, simdSize); BlockRange().InsertBefore(node, maskNode); cnsNode = comp->gtNewIconNode(8 - count); BlockRange().InsertAfter(maskNode, cnsNode); maskNode = comp->gtNewSimdHWIntrinsicNode(TYP_MASK, maskNode, cnsNode, - NI_EVEX_ShiftLeftMask, simdBaseJitType, simdSize); + NI_EVEX_ShiftLeftMask, maskBaseJitType, simdSize); BlockRange().InsertAfter(cnsNode, maskNode); LowerNode(maskNode); @@ -2666,7 +2680,7 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm maskNode = comp->gtNewSimdHWIntrinsicNode(TYP_MASK, maskNode, cnsNode, NI_EVEX_ShiftRightMask, - simdBaseJitType, simdSize); + maskBaseJitType, simdSize); BlockRange().InsertAfter(cnsNode, maskNode); maskIntrinsicId = NI_EVEX_ShiftRightMask; @@ -2791,6 +2805,11 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm { assert(node == maskNode); + // We're not consuming the underlying mask directly, even if one exists, + // so ensure that we track the base type as the one we'll be producing + // via the vector comparison introduced here. + maskBaseJitType = simdBaseJitType; + // We have `x == y` or `x != y` both of which where we want to find `AllBitsSet` in the mask since // we can directly do the relevant comparison. Given the above tables then when we have a full mask // we can simply check against `CF == 1` for `op_Equality` and `ZF == 0` for `op_Inequality`. @@ -2830,7 +2849,7 @@ GenTree* Lowering::LowerHWIntrinsicCmpOp(GenTreeHWIntrinsic* node, genTreeOps cm { GenTreeHWIntrinsic* cc; - cc = comp->gtNewSimdHWIntrinsicNode(simdType, maskNode, NI_EVEX_KORTEST, simdBaseJitType, simdSize); + cc = comp->gtNewSimdHWIntrinsicNode(simdType, maskNode, NI_EVEX_KORTEST, maskBaseJitType, simdSize); BlockRange().InsertBefore(nextNode, cc); use.ReplaceWith(cc); @@ -3052,8 +3071,19 @@ GenTree* Lowering::LowerHWIntrinsicCndSel(GenTreeHWIntrinsic* node) if (isOp1CvtMaskToVector) { - maskNode = op1->AsHWIntrinsic()->Op(1); + GenTreeHWIntrinsic* cvtMaskToVector = op1->AsHWIntrinsic(); + + maskNode = cvtMaskToVector->Op(1); BlockRange().Remove(op1); + + // We need to change the base type to match the underlying mask size to ensure + // the right instruction variant is picked. If the CndSel was for TYP_INT but + // the mask was for TYP_DOUBLE then we'd generate vpblendmd when we really want + // vpblendmq. Changing the size is fine since CndSel itself is bitwise and the + // the mask is just representing entire elements at a given size. + + CorInfoType maskBaseJitType = cvtMaskToVector->GetSimdBaseJitType(); + node->SetSimdBaseJitType(maskBaseJitType); } else { From e78b72b1fdf43d9678877400bcfe801b38c14681 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 4 Jul 2024 10:50:30 -0700 Subject: [PATCH 15/72] Fix certificate test failures on iOS(-likes) Also don't run ExportMultiplePrivateKeys on platforms that don't have Exportable --- .../X509CertificateLoader.iOS.cs | 24 ++++++++++++++++++- .../tests/X509Certificates/CollectionTests.cs | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs index 11e253150053cf..3dc0df1700b567 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509CertificateLoader.iOS.cs @@ -22,7 +22,29 @@ private static partial ICertificatePal LoadCertificatePal(ReadOnlySpan dat throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } - return LoadX509(data); + ICertificatePal? result = null; + + // If the data starts with 0x30, only try the DER loader. + // Otherwise, try PEM. + // If it's not PEM and not 0x30, still call the DER loader to get the system error. + if (data[0] != 0x30) + { + AppleCertificatePal.TryDecodePem( + data, + (derData, contentType) => + { + if (contentType != X509ContentType.Cert) + { + // true: keep looking + return true; + } + + result = LoadX509(derData); + return false; + }); + } + + return result ?? LoadX509(data); } private static partial ICertificatePal LoadCertificatePalFromFile(string path) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionTests.cs index ac871b95631532..caf9ed53ea1be8 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CollectionTests.cs @@ -800,6 +800,7 @@ public static void MultipleImport() } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "The PKCS#12 Exportable flag is not supported on iOS/MacCatalyst/tvOS")] public static void ExportMultiplePrivateKeys() { var collection = new X509Certificate2Collection(); From 4071a312c407c52dd891f208de5c935d7869745a Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 4 Jul 2024 21:17:22 +0200 Subject: [PATCH 16/72] JIT: Extract StructSegments to its own file (#104432) Also move the function to compute the significant segments for a class handle from `Promotion` to `Compiler`. --- src/coreclr/jit/CMakeLists.txt | 2 + src/coreclr/jit/compiler.cpp | 62 ++++ src/coreclr/jit/compiler.h | 6 + src/coreclr/jit/promotion.cpp | 361 +-------------------- src/coreclr/jit/promotion.h | 56 +--- src/coreclr/jit/promotiondecomposition.cpp | 2 +- src/coreclr/jit/structsegments.cpp | 303 +++++++++++++++++ src/coreclr/jit/structsegments.h | 56 ++++ 8 files changed, 432 insertions(+), 416 deletions(-) create mode 100644 src/coreclr/jit/structsegments.cpp create mode 100644 src/coreclr/jit/structsegments.h diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index 7932e0d452c43f..68155021d8eb78 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -178,6 +178,7 @@ set( JIT_SOURCES ssabuilder.cpp ssarenamestate.cpp stacklevelsetter.cpp + structsegments.cpp switchrecognition.cpp treelifeupdater.cpp unwind.cpp @@ -379,6 +380,7 @@ set( JIT_HEADERS ssaconfig.h ssarenamestate.h stacklevelsetter.h + structsegments.h target.h targetx86.h targetamd64.h diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 299d65f4f33cda..f8080a73cc2119 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1990,6 +1990,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, m_outlinedCompositeSsaNums = nullptr; m_nodeToLoopMemoryBlockMap = nullptr; m_signatureToLookupInfoMap = nullptr; + m_significantSegmentsMap = nullptr; fgSsaPassesCompleted = 0; fgSsaValid = false; fgVNPassesCompleted = 0; @@ -8373,6 +8374,67 @@ void Compiler::TransferTestDataToNode(GenTree* from, GenTree* to) #endif // DEBUG +//------------------------------------------------------------------------ +// GetSignificantSegments: +// Compute a segment tree containing all significant (non-padding) segments +// for the specified class layout. +// +// Parameters: +// layout - The layout +// +// Returns: +// Segment tree containing all significant parts of the layout. +// +const StructSegments& Compiler::GetSignificantSegments(ClassLayout* layout) +{ + StructSegments* cached; + if ((m_significantSegmentsMap != nullptr) && m_significantSegmentsMap->Lookup(layout, &cached)) + { + return *cached; + } + + COMP_HANDLE compHnd = info.compCompHnd; + + StructSegments* newSegments = new (this, CMK_Promotion) StructSegments(getAllocator(CMK_Promotion)); + + if (layout->IsBlockLayout()) + { + newSegments->Add(StructSegments::Segment(0, layout->GetSize())); + } + else + { + CORINFO_TYPE_LAYOUT_NODE nodes[256]; + size_t numNodes = ArrLen(nodes); + GetTypeLayoutResult result = compHnd->getTypeLayout(layout->GetClassHandle(), nodes, &numNodes); + + if (result != GetTypeLayoutResult::Success) + { + newSegments->Add(StructSegments::Segment(0, layout->GetSize())); + } + else + { + for (size_t i = 0; i < numNodes; i++) + { + const CORINFO_TYPE_LAYOUT_NODE& node = nodes[i]; + if ((node.type != CORINFO_TYPE_VALUECLASS) || (node.simdTypeHnd != NO_CLASS_HANDLE) || + node.hasSignificantPadding) + { + newSegments->Add(StructSegments::Segment(node.offset, node.offset + node.size)); + } + } + } + } + + if (m_significantSegmentsMap == nullptr) + { + m_significantSegmentsMap = new (this, CMK_Promotion) ClassLayoutStructSegmentsMap(getAllocator(CMK_Promotion)); + } + + m_significantSegmentsMap->Set(layout, newSegments); + + return *newSegments; +} + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 488d6a01de9e1e..fb5950694f5fd1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -43,6 +43,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "valuenum.h" #include "scev.h" #include "namedintrinsiclist.h" +#include "structsegments.h" #ifdef LATE_DISASM #include "disasm.h" #endif @@ -5630,6 +5631,11 @@ class Compiler return m_signatureToLookupInfoMap; } + const StructSegments& GetSignificantSegments(ClassLayout* layout); + + typedef JitHashTable, class StructSegments*> ClassLayoutStructSegmentsMap; + ClassLayoutStructSegmentsMap* m_significantSegmentsMap; + #ifdef SWIFT_SUPPORT typedef JitHashTable, CORINFO_SWIFT_LOWERING*> SwiftLoweringMap; SwiftLoweringMap* m_swiftLoweringCache; diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index b656f1576d0d9d..c6a1eb0b3f4e6e 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1245,7 +1245,7 @@ class LocalsUseVisitor : public GenTreeVisitor } #endif - agg->Unpromoted = m_prom->SignificantSegments(m_compiler->lvaGetDesc(agg->LclNum)->GetLayout()); + agg->Unpromoted = m_compiler->GetSignificantSegments(m_compiler->lvaGetDesc(agg->LclNum)->GetLayout()); for (Replacement& rep : reps) { agg->Unpromoted.Subtract(StructSegments::Segment(rep.Offset, rep.Offset + genTypeSize(rep.AccessType))); @@ -1484,365 +1484,6 @@ bool Replacement::Overlaps(unsigned otherStart, unsigned otherSize) const return true; } -//------------------------------------------------------------------------ -// IntersectsOrAdjacent: -// Check if this segment intersects or is adjacent to another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::IntersectsOrAdjacent(const Segment& other) const -{ - if (End < other.Start) - { - return false; - } - - if (other.End < Start) - { - return false; - } - - return true; -} - -//------------------------------------------------------------------------ -// Intersects: -// Check if this segment intersects another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::Intersects(const Segment& other) const -{ - if (End <= other.Start) - { - return false; - } - - if (other.End <= Start) - { - return false; - } - - return true; -} - -//------------------------------------------------------------------------ -// Contains: -// Check if this segment contains another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::Contains(const Segment& other) const -{ - return (other.Start >= Start) && (other.End <= End); -} - -//------------------------------------------------------------------------ -// Merge: -// Update this segment to also contain another segment. -// -// Parameters: -// other - The other segment. -// -void StructSegments::Segment::Merge(const Segment& other) -{ - Start = min(Start, other.Start); - End = max(End, other.End); -} - -//------------------------------------------------------------------------ -// Add: -// Add a segment to the data structure. -// -// Parameters: -// segment - The segment to add. -// -void StructSegments::Add(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - - if ((ssize_t)index < 0) - { - index = ~index; - } - - m_segments.insert(m_segments.begin() + index, segment); - size_t endIndex; - for (endIndex = index + 1; endIndex < m_segments.size(); endIndex++) - { - if (!m_segments[index].IntersectsOrAdjacent(m_segments[endIndex])) - { - break; - } - - m_segments[index].Merge(m_segments[endIndex]); - } - - m_segments.erase(m_segments.begin() + index + 1, m_segments.begin() + endIndex); -} - -//------------------------------------------------------------------------ -// Subtract: -// Subtract a segment from the data structure. -// -// Parameters: -// segment - The segment to subtract. -// -void StructSegments::Subtract(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - if ((ssize_t)index < 0) - { - index = ~index; - } - else - { - // Start == segment[index].End, which makes it non-interesting. - index++; - } - - if (index >= m_segments.size()) - { - return; - } - - // Here we know Start < segment[index].End. Do they not intersect at all? - if (m_segments[index].Start >= segment.End) - { - // Does not intersect any segment. - return; - } - - assert(m_segments[index].Intersects(segment)); - - if (m_segments[index].Contains(segment)) - { - if (segment.Start > m_segments[index].Start) - { - // New segment (existing.Start, segment.Start) - if (segment.End < m_segments[index].End) - { - m_segments.insert(m_segments.begin() + index, Segment(m_segments[index].Start, segment.Start)); - - // And new segment (segment.End, existing.End) - m_segments[index + 1].Start = segment.End; - return; - } - - m_segments[index].End = segment.Start; - return; - } - if (segment.End < m_segments[index].End) - { - // New segment (segment.End, existing.End) - m_segments[index].Start = segment.End; - return; - } - - // Full segment is being removed - m_segments.erase(m_segments.begin() + index); - return; - } - - if (segment.Start > m_segments[index].Start) - { - m_segments[index].End = segment.Start; - index++; - } - - size_t endIndex = Promotion::BinarySearch(m_segments, segment.End); - if ((ssize_t)endIndex >= 0) - { - m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex + 1); - return; - } - - endIndex = ~endIndex; - if (endIndex == m_segments.size()) - { - m_segments.erase(m_segments.begin() + index, m_segments.end()); - return; - } - - if (segment.End > m_segments[endIndex].Start) - { - m_segments[endIndex].Start = segment.End; - } - - m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex); -} - -//------------------------------------------------------------------------ -// IsEmpty: -// Check if the segment tree is empty. -// -// Returns: -// True if so. -// -bool StructSegments::IsEmpty() -{ - return m_segments.size() == 0; -} - -//------------------------------------------------------------------------ -// CoveringSegment: -// Compute a segment that covers all contained segments in this segment tree. -// -// Parameters: -// result - [out] The single segment. Only valid if the method returns true. -// -// Returns: -// True if this segment tree was non-empty; otherwise false. -// -bool StructSegments::CoveringSegment(Segment* result) -{ - if (m_segments.size() == 0) - { - return false; - } - - result->Start = m_segments[0].Start; - result->End = m_segments[m_segments.size() - 1].End; - return true; -} - -//------------------------------------------------------------------------ -// Intersects: -// Check if a segment intersects with any segment in this segment tree. -// -// Parameters: -// segment - The segment. -// -// Returns: -// True if the input segment intersects with any segment in the tree; -// otherwise false. -// -bool StructSegments::Intersects(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - if ((ssize_t)index < 0) - { - index = ~index; - } - else - { - // Start == segment[index].End, which makes it non-interesting. - index++; - } - - if (index >= m_segments.size()) - { - return false; - } - - // Here we know Start < segment[index].End. Do they not intersect at all? - if (m_segments[index].Start >= segment.End) - { - // Does not intersect any segment. - return false; - } - - assert(m_segments[index].Intersects(segment)); - return true; -} - -#ifdef DEBUG -//------------------------------------------------------------------------ -// Dump: -// Dump a string representation of the segment tree to stdout. -// -void StructSegments::Dump() -{ - if (m_segments.size() == 0) - { - printf(""); - } - else - { - const char* sep = ""; - for (const Segment& segment : m_segments) - { - printf("%s[%03u..%03u)", sep, segment.Start, segment.End); - sep = " "; - } - } -} -#endif - -//------------------------------------------------------------------------ -// SignificantSegments: -// Compute a segment tree containing all significant (non-padding) segments -// for the specified class layout. -// -// Parameters: -// layout - The layout -// -// Returns: -// Segment tree containing all significant parts of the layout. -// -StructSegments Promotion::SignificantSegments(ClassLayout* layout) -{ - StructSegments* cached; - if ((m_significantSegmentsCache != nullptr) && m_significantSegmentsCache->Lookup(layout, &cached)) - { - return StructSegments(*cached); - } - - COMP_HANDLE compHnd = m_compiler->info.compCompHnd; - - StructSegments segments(m_compiler->getAllocator(CMK_Promotion)); - - if (layout->IsBlockLayout()) - { - segments.Add(StructSegments::Segment(0, layout->GetSize())); - } - else - { - CORINFO_TYPE_LAYOUT_NODE nodes[256]; - size_t numNodes = ArrLen(nodes); - GetTypeLayoutResult result = compHnd->getTypeLayout(layout->GetClassHandle(), nodes, &numNodes); - - if (result != GetTypeLayoutResult::Success) - { - segments.Add(StructSegments::Segment(0, layout->GetSize())); - } - else - { - for (size_t i = 0; i < numNodes; i++) - { - const CORINFO_TYPE_LAYOUT_NODE& node = nodes[i]; - if ((node.type != CORINFO_TYPE_VALUECLASS) || (node.simdTypeHnd != NO_CLASS_HANDLE) || - node.hasSignificantPadding) - { - segments.Add(StructSegments::Segment(node.offset, node.offset + node.size)); - } - } - } - } - - if (m_significantSegmentsCache == nullptr) - { - m_significantSegmentsCache = - new (m_compiler, CMK_Promotion) ClassLayoutStructSegmentsMap(m_compiler->getAllocator(CMK_Promotion)); - } - - m_significantSegmentsCache->Set(layout, new (m_compiler, CMK_Promotion) StructSegments(segments)); - - return segments; -} - //------------------------------------------------------------------------ // CreateWriteBack: // Create IR that writes a replacement local's value back to its struct local: diff --git a/src/coreclr/jit/promotion.h b/src/coreclr/jit/promotion.h index 89097d78cd1061..ba5f6fbfa9c67d 100644 --- a/src/coreclr/jit/promotion.h +++ b/src/coreclr/jit/promotion.h @@ -40,55 +40,6 @@ struct Replacement bool Overlaps(unsigned otherStart, unsigned otherSize) const; }; -// Represents significant segments of a struct operation. -// -// Essentially a segment tree (but not stored as a tree) that supports boolean -// Add/Subtract operations of segments. Used to compute the remainder after -// replacements have been handled as part of a decomposed block operation. -class StructSegments -{ -public: - struct Segment - { - unsigned Start = 0; - unsigned End = 0; - - Segment() - { - } - - Segment(unsigned start, unsigned end) - : Start(start) - , End(end) - { - } - - bool IntersectsOrAdjacent(const Segment& other) const; - bool Intersects(const Segment& other) const; - bool Contains(const Segment& other) const; - void Merge(const Segment& other); - }; - -private: - jitstd::vector m_segments; - -public: - explicit StructSegments(CompAllocator allocator) - : m_segments(allocator) - { - } - - void Add(const Segment& segment); - void Subtract(const Segment& segment); - bool IsEmpty(); - bool CoveringSegment(Segment* result); - bool Intersects(const Segment& segment); - -#ifdef DEBUG - void Dump(); -#endif -}; - // Represents information about an aggregate that now has replacements in it. struct AggregateInfo { @@ -137,12 +88,9 @@ class AggregateInfoMap } }; -typedef JitHashTable, class StructSegments*> ClassLayoutStructSegmentsMap; - class Promotion { - Compiler* m_compiler; - ClassLayoutStructSegmentsMap* m_significantSegmentsCache = nullptr; + Compiler* m_compiler; friend class LocalUses; friend class LocalsUseVisitor; @@ -152,8 +100,6 @@ class Promotion friend class DecompositionPlan; friend class StructSegments; - StructSegments SignificantSegments(ClassLayout* layout); - void ExplicitlyZeroInitReplacementLocals(unsigned lclNum, const jitstd::vector& replacements, Statement** prevStmt); diff --git a/src/coreclr/jit/promotiondecomposition.cpp b/src/coreclr/jit/promotiondecomposition.cpp index d4f71b99835208..bf89852547fd7b 100644 --- a/src/coreclr/jit/promotiondecomposition.cpp +++ b/src/coreclr/jit/promotiondecomposition.cpp @@ -238,7 +238,7 @@ class DecompositionPlan { ClassLayout* dstLayout = m_store->GetLayout(m_compiler); - StructSegments segments = m_promotion->SignificantSegments(dstLayout); + StructSegments segments = m_compiler->GetSignificantSegments(dstLayout); for (int i = 0; i < m_entries.Height(); i++) { diff --git a/src/coreclr/jit/structsegments.cpp b/src/coreclr/jit/structsegments.cpp new file mode 100644 index 00000000000000..bbd54af6500e63 --- /dev/null +++ b/src/coreclr/jit/structsegments.cpp @@ -0,0 +1,303 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "jitpch.h" +#include "structsegments.h" +#include "promotion.h" + +//------------------------------------------------------------------------ +// IntersectsOrAdjacent: +// Check if this segment intersects or is adjacent to another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::IntersectsOrAdjacent(const Segment& other) const +{ + if (End < other.Start) + { + return false; + } + + if (other.End < Start) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------ +// Intersects: +// Check if this segment intersects another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::Intersects(const Segment& other) const +{ + if (End <= other.Start) + { + return false; + } + + if (other.End <= Start) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------ +// Contains: +// Check if this segment contains another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::Contains(const Segment& other) const +{ + return (other.Start >= Start) && (other.End <= End); +} + +//------------------------------------------------------------------------ +// Merge: +// Update this segment to also contain another segment. +// +// Parameters: +// other - The other segment. +// +void StructSegments::Segment::Merge(const Segment& other) +{ + Start = min(Start, other.Start); + End = max(End, other.End); +} + +//------------------------------------------------------------------------ +// Add: +// Add a segment to the data structure. +// +// Parameters: +// segment - The segment to add. +// +void StructSegments::Add(const Segment& segment) +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + + if ((ssize_t)index < 0) + { + index = ~index; + } + + m_segments.insert(m_segments.begin() + index, segment); + size_t endIndex; + for (endIndex = index + 1; endIndex < m_segments.size(); endIndex++) + { + if (!m_segments[index].IntersectsOrAdjacent(m_segments[endIndex])) + { + break; + } + + m_segments[index].Merge(m_segments[endIndex]); + } + + m_segments.erase(m_segments.begin() + index + 1, m_segments.begin() + endIndex); +} + +//------------------------------------------------------------------------ +// Subtract: +// Subtract a segment from the data structure. +// +// Parameters: +// segment - The segment to subtract. +// +void StructSegments::Subtract(const Segment& segment) +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + if ((ssize_t)index < 0) + { + index = ~index; + } + else + { + // Start == segment[index].End, which makes it non-interesting. + index++; + } + + if (index >= m_segments.size()) + { + return; + } + + // Here we know Start < segment[index].End. Do they not intersect at all? + if (m_segments[index].Start >= segment.End) + { + // Does not intersect any segment. + return; + } + + assert(m_segments[index].Intersects(segment)); + + if (m_segments[index].Contains(segment)) + { + if (segment.Start > m_segments[index].Start) + { + // New segment (existing.Start, segment.Start) + if (segment.End < m_segments[index].End) + { + m_segments.insert(m_segments.begin() + index, Segment(m_segments[index].Start, segment.Start)); + + // And new segment (segment.End, existing.End) + m_segments[index + 1].Start = segment.End; + return; + } + + m_segments[index].End = segment.Start; + return; + } + if (segment.End < m_segments[index].End) + { + // New segment (segment.End, existing.End) + m_segments[index].Start = segment.End; + return; + } + + // Full segment is being removed + m_segments.erase(m_segments.begin() + index); + return; + } + + if (segment.Start > m_segments[index].Start) + { + m_segments[index].End = segment.Start; + index++; + } + + size_t endIndex = Promotion::BinarySearch(m_segments, segment.End); + if ((ssize_t)endIndex >= 0) + { + m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex + 1); + return; + } + + endIndex = ~endIndex; + if (endIndex == m_segments.size()) + { + m_segments.erase(m_segments.begin() + index, m_segments.end()); + return; + } + + if (segment.End > m_segments[endIndex].Start) + { + m_segments[endIndex].Start = segment.End; + } + + m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex); +} + +//------------------------------------------------------------------------ +// IsEmpty: +// Check if the segment tree is empty. +// +// Returns: +// True if so. +// +bool StructSegments::IsEmpty() const +{ + return m_segments.size() == 0; +} + +//------------------------------------------------------------------------ +// CoveringSegment: +// Compute a segment that covers all contained segments in this segment tree. +// +// Parameters: +// result - [out] The single segment. Only valid if the method returns true. +// +// Returns: +// True if this segment tree was non-empty; otherwise false. +// +bool StructSegments::CoveringSegment(Segment* result) const +{ + if (m_segments.size() == 0) + { + return false; + } + + result->Start = m_segments[0].Start; + result->End = m_segments[m_segments.size() - 1].End; + return true; +} + +//------------------------------------------------------------------------ +// Intersects: +// Check if a segment intersects with any segment in this segment tree. +// +// Parameters: +// segment - The segment. +// +// Returns: +// True if the input segment intersects with any segment in the tree; +// otherwise false. +// +bool StructSegments::Intersects(const Segment& segment) const +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + if ((ssize_t)index < 0) + { + index = ~index; + } + else + { + // Start == segment[index].End, which makes it non-interesting. + index++; + } + + if (index >= m_segments.size()) + { + return false; + } + + // Here we know Start < segment[index].End. Do they not intersect at all? + if (m_segments[index].Start >= segment.End) + { + // Does not intersect any segment. + return false; + } + + assert(m_segments[index].Intersects(segment)); + return true; +} + +#ifdef DEBUG +//------------------------------------------------------------------------ +// Dump: +// Dump a string representation of the segment tree to stdout. +// +void StructSegments::Dump() +{ + if (m_segments.size() == 0) + { + printf(""); + } + else + { + const char* sep = ""; + for (const Segment& segment : m_segments) + { + printf("%s[%03u..%03u)", sep, segment.Start, segment.End); + sep = " "; + } + } +} +#endif diff --git a/src/coreclr/jit/structsegments.h b/src/coreclr/jit/structsegments.h new file mode 100644 index 00000000000000..2a78a181a5b183 --- /dev/null +++ b/src/coreclr/jit/structsegments.h @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "alloc.h" +#include "jitstd/vector.h" + +// Represents significant segments of a struct operation. +// +// Essentially a segment tree (but not stored as a tree) that supports boolean +// Add/Subtract operations of segments. Used to compute the remainder after +// replacements have been handled as part of a decomposed block operation. +class StructSegments +{ +public: + struct Segment + { + unsigned Start = 0; + unsigned End = 0; + + Segment() + { + } + + Segment(unsigned start, unsigned end) + : Start(start) + , End(end) + { + } + + bool IntersectsOrAdjacent(const Segment& other) const; + bool Intersects(const Segment& other) const; + bool Contains(const Segment& other) const; + void Merge(const Segment& other); + }; + +private: + jitstd::vector m_segments; + +public: + explicit StructSegments(CompAllocator allocator) + : m_segments(allocator) + { + } + + void Add(const Segment& segment); + void Subtract(const Segment& segment); + bool IsEmpty() const; + bool CoveringSegment(Segment* result) const; + bool Intersects(const Segment& segment) const; + +#ifdef DEBUG + void Dump(); +#endif +}; From 64efe2654c8455e7591aa07e7e8505064f571fc4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 4 Jul 2024 22:57:20 +0200 Subject: [PATCH 17/72] [HttpClientFactory] Do not log query string by default (#103769) Query strings may often contain sensitive information, we should avoid logging them by default. --- .../src/Logging/LogHelper.cs | 193 ++++++++++++ .../src/Logging/LoggingHttpMessageHandler.cs | 72 +---- .../Logging/LoggingScopeHttpMessageHandler.cs | 73 +---- .../Logging/LoggingUriOutputTests.cs | 280 ++++++++++-------- .../RedactedLogValueIntegrationTest.cs | 25 +- .../Microsoft.Extensions.Http.Tests.csproj | 3 + 6 files changed, 374 insertions(+), 272 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.Http/src/Logging/LogHelper.cs diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LogHelper.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LogHelper.cs new file mode 100644 index 00000000000000..2a37f6b3f3d1f8 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LogHelper.cs @@ -0,0 +1,193 @@ +// 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.Diagnostics; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Internal; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Extensions.Http.Logging +{ + internal static class LogHelper + { + private static readonly LogDefineOptions s_skipEnabledCheckLogDefineOptions = new LogDefineOptions() { SkipEnabledCheck = true }; + private static readonly bool s_disableUriRedaction = GetDisableUriRedactionSettingValue(); + + private static class EventIds + { + public static readonly EventId RequestStart = new EventId(100, "RequestStart"); + public static readonly EventId RequestEnd = new EventId(101, "RequestEnd"); + + public static readonly EventId RequestHeader = new EventId(102, "RequestHeader"); + public static readonly EventId ResponseHeader = new EventId(103, "ResponseHeader"); + + public static readonly EventId PipelineStart = new EventId(100, "RequestPipelineStart"); + public static readonly EventId PipelineEnd = new EventId(101, "RequestPipelineEnd"); + + public static readonly EventId RequestPipelineRequestHeader = new EventId(102, "RequestPipelineRequestHeader"); + public static readonly EventId RequestPipelineResponseHeader = new EventId(103, "RequestPipelineResponseHeader"); + } + + private static readonly Action _requestStart = LoggerMessage.Define( + LogLevel.Information, + EventIds.RequestStart, + "Sending HTTP request {HttpMethod} {Uri}", + s_skipEnabledCheckLogDefineOptions); + + private static readonly Action _requestEnd = LoggerMessage.Define( + LogLevel.Information, + EventIds.RequestEnd, + "Received HTTP response headers after {ElapsedMilliseconds}ms - {StatusCode}"); + + private static readonly Func _beginRequestPipelineScope = LoggerMessage.DefineScope("HTTP {HttpMethod} {Uri}"); + + private static readonly Action _requestPipelineStart = LoggerMessage.Define( + LogLevel.Information, + EventIds.PipelineStart, + "Start processing HTTP request {HttpMethod} {Uri}"); + + private static readonly Action _requestPipelineEnd = LoggerMessage.Define( + LogLevel.Information, + EventIds.PipelineEnd, + "End processing HTTP request after {ElapsedMilliseconds}ms - {StatusCode}"); + + private static bool GetDisableUriRedactionSettingValue() + { + if (AppContext.TryGetSwitch("System.Net.Http.DisableUriRedaction", out bool value)) + { + return value; + } + + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION"); + + if (bool.TryParse(envVar, out value)) + { + return value; + } + else if (uint.TryParse(envVar, out uint intVal)) + { + return intVal != 0; + } + + return false; + } + + public static void LogRequestStart(this ILogger logger, HttpRequestMessage request, Func shouldRedactHeaderValue) + { + // We check here to avoid allocating in the GetRedactedUriString call unnecessarily + if (logger.IsEnabled(LogLevel.Information)) + { + _requestStart(logger, request.Method, GetRedactedUriString(request.RequestUri), null); + } + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.Log( + LogLevel.Trace, + EventIds.RequestHeader, + new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Request, request.Headers, request.Content?.Headers, shouldRedactHeaderValue), + null, + (state, ex) => state.ToString()); + } + } + + public static void LogRequestEnd(this ILogger logger, HttpResponseMessage response, TimeSpan duration, Func shouldRedactHeaderValue) + { + _requestEnd(logger, duration.TotalMilliseconds, (int)response.StatusCode, null); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.Log( + LogLevel.Trace, + EventIds.ResponseHeader, + new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Response, response.Headers, response.Content?.Headers, shouldRedactHeaderValue), + null, + (state, ex) => state.ToString()); + } + } + + public static IDisposable? BeginRequestPipelineScope(this ILogger logger, HttpRequestMessage request, out string? formattedUri) + { + formattedUri = GetRedactedUriString(request.RequestUri); + return _beginRequestPipelineScope(logger, request.Method, formattedUri); + } + + public static void LogRequestPipelineStart(this ILogger logger, HttpRequestMessage request, string? formattedUri, Func shouldRedactHeaderValue) + { + _requestPipelineStart(logger, request.Method, formattedUri, null); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.Log( + LogLevel.Trace, + EventIds.RequestPipelineRequestHeader, + new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Request, request.Headers, request.Content?.Headers, shouldRedactHeaderValue), + null, + (state, ex) => state.ToString()); + } + } + + public static void LogRequestPipelineEnd(this ILogger logger, HttpResponseMessage response, TimeSpan duration, Func shouldRedactHeaderValue) + { + _requestPipelineEnd(logger, duration.TotalMilliseconds, (int)response.StatusCode, null); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.Log( + LogLevel.Trace, + EventIds.RequestPipelineResponseHeader, + new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Response, response.Headers, response.Content?.Headers, shouldRedactHeaderValue), + null, + (state, ex) => state.ToString()); + } + } + + internal static string? GetRedactedUriString(Uri? uri) + { + if (uri is null) + { + return null; + } + + if (s_disableUriRedaction) + { + return uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString(); + } + + if (!uri.IsAbsoluteUri) + { + // We cannot guarantee the redaction of UserInfo for relative Uris without implementing some subset of Uri parsing in this package. + // To avoid this, we redact the whole Uri. Seeing a relative Uri in LoggingHttpMessageHandler or LoggingScopeHttpMessageHandler + // requires a custom handler chain with custom expansion logic implemented by the user's HttpMessageHandler. + // In such advanced scenarios we recommend users to log the Uri in their handler. + return "*"; + } + + string pathAndQuery = uri.PathAndQuery; + int queryIndex = pathAndQuery.IndexOf('?'); + + bool redactQuery = queryIndex >= 0 && // Query is present. + queryIndex < pathAndQuery.Length - 1; // Query is not empty. + + return (redactQuery, uri.IsDefaultPort) switch + { + (true, true) => $"{uri.Scheme}://{uri.Host}{GetPath(pathAndQuery, queryIndex)}*", + (true, false) => $"{uri.Scheme}://{uri.Host}:{uri.Port}{GetPath(pathAndQuery, queryIndex)}*", + (false, true) => $"{uri.Scheme}://{uri.Host}{pathAndQuery}", + (false, false) => $"{uri.Scheme}://{uri.Host}:{uri.Port}{pathAndQuery}" + }; + +#if NET + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ReadOnlySpan GetPath(string pathAndQuery, int queryIndex) => pathAndQuery.AsSpan(0, queryIndex + 1); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static string GetPath(string pathAndQuery, int queryIndex) => pathAndQuery.Substring(0, queryIndex + 1); +#endif + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs index e592e7d236fbb4..c86d149a0f2c69 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandler.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -58,7 +59,7 @@ async Task Core(HttpRequestMessage request, bool useAsync, // Not using a scope here because we always expect this to be at the end of the pipeline, thus there's // not really anything to surround. - Log.RequestStart(_logger, request, shouldRedactHeaderValue); + _logger.LogRequestStart(request, shouldRedactHeaderValue); var stopwatch = ValueStopwatch.StartNew(); HttpResponseMessage response = useAsync ? await base.SendAsync(request, cancellationToken).ConfigureAwait(false) @@ -67,7 +68,7 @@ async Task Core(HttpRequestMessage request, bool useAsync, #else : throw new NotImplementedException("Unreachable code"); #endif - Log.RequestEnd(_logger, response, stopwatch.GetElapsedTime(), shouldRedactHeaderValue); + _logger.LogRequestEnd(response, stopwatch.GetElapsedTime(), shouldRedactHeaderValue); return response; } @@ -84,72 +85,5 @@ protected override Task SendAsync(HttpRequestMessage reques protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => SendCoreAsync(request, useAsync: false, cancellationToken).GetAwaiter().GetResult(); #endif - - // Used in tests. - internal static class Log - { - public static class EventIds - { - public static readonly EventId RequestStart = new EventId(100, "RequestStart"); - public static readonly EventId RequestEnd = new EventId(101, "RequestEnd"); - - public static readonly EventId RequestHeader = new EventId(102, "RequestHeader"); - public static readonly EventId ResponseHeader = new EventId(103, "ResponseHeader"); - } - - private static readonly LogDefineOptions _skipEnabledCheckLogDefineOptions = new LogDefineOptions() { SkipEnabledCheck = true }; - - private static readonly Action _requestStart = LoggerMessage.Define( - LogLevel.Information, - EventIds.RequestStart, - "Sending HTTP request {HttpMethod} {Uri}", - _skipEnabledCheckLogDefineOptions); - - private static readonly Action _requestEnd = LoggerMessage.Define( - LogLevel.Information, - EventIds.RequestEnd, - "Received HTTP response headers after {ElapsedMilliseconds}ms - {StatusCode}"); - - public static void RequestStart(ILogger logger, HttpRequestMessage request, Func shouldRedactHeaderValue) - { - // We check here to avoid allocating in the GetUriString call unnecessarily - if (logger.IsEnabled(LogLevel.Information)) - { - _requestStart(logger, request.Method, GetUriString(request.RequestUri), null); - } - - if (logger.IsEnabled(LogLevel.Trace)) - { - logger.Log( - LogLevel.Trace, - EventIds.RequestHeader, - new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Request, request.Headers, request.Content?.Headers, shouldRedactHeaderValue), - null, - (state, ex) => state.ToString()); - } - } - - public static void RequestEnd(ILogger logger, HttpResponseMessage response, TimeSpan duration, Func shouldRedactHeaderValue) - { - _requestEnd(logger, duration.TotalMilliseconds, (int)response.StatusCode, null); - - if (logger.IsEnabled(LogLevel.Trace)) - { - logger.Log( - LogLevel.Trace, - EventIds.ResponseHeader, - new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Response, response.Headers, response.Content?.Headers, shouldRedactHeaderValue), - null, - (state, ex) => state.ToString()); - } - } - - private static string? GetUriString(Uri? requestUri) - { - return requestUri?.IsAbsoluteUri == true - ? requestUri.AbsoluteUri - : requestUri?.ToString(); - } - } } } diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs index 1894a005f84a68..a25a9366ac8e29 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingScopeHttpMessageHandler.cs @@ -58,9 +58,9 @@ async Task Core(HttpRequestMessage request, bool useAsync, Func shouldRedactHeaderValue = _options?.ShouldRedactHeaderValue ?? _shouldNotRedactHeaderValue; - using (Log.BeginRequestPipelineScope(_logger, request)) + using (_logger.BeginRequestPipelineScope(request, out string? formattedUri)) { - Log.RequestPipelineStart(_logger, request, shouldRedactHeaderValue); + _logger.LogRequestPipelineStart(request, formattedUri, shouldRedactHeaderValue); HttpResponseMessage response = useAsync ? await base.SendAsync(request, cancellationToken).ConfigureAwait(false) #if NET @@ -68,7 +68,7 @@ async Task Core(HttpRequestMessage request, bool useAsync, #else : throw new NotImplementedException("Unreachable code"); #endif - Log.RequestPipelineEnd(_logger, response, stopwatch.GetElapsedTime(), shouldRedactHeaderValue); + _logger.LogRequestPipelineEnd(response, stopwatch.GetElapsedTime(), shouldRedactHeaderValue); return response; } @@ -86,72 +86,5 @@ protected override Task SendAsync(HttpRequestMessage reques protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) => SendCoreAsync(request, useAsync: false, cancellationToken).GetAwaiter().GetResult(); #endif - - // Used in tests - internal static class Log - { - public static class EventIds - { - public static readonly EventId PipelineStart = new EventId(100, "RequestPipelineStart"); - public static readonly EventId PipelineEnd = new EventId(101, "RequestPipelineEnd"); - - public static readonly EventId RequestHeader = new EventId(102, "RequestPipelineRequestHeader"); - public static readonly EventId ResponseHeader = new EventId(103, "RequestPipelineResponseHeader"); - } - - private static readonly Func _beginRequestPipelineScope = LoggerMessage.DefineScope("HTTP {HttpMethod} {Uri}"); - - private static readonly Action _requestPipelineStart = LoggerMessage.Define( - LogLevel.Information, - EventIds.PipelineStart, - "Start processing HTTP request {HttpMethod} {Uri}"); - - private static readonly Action _requestPipelineEnd = LoggerMessage.Define( - LogLevel.Information, - EventIds.PipelineEnd, - "End processing HTTP request after {ElapsedMilliseconds}ms - {StatusCode}"); - - public static IDisposable? BeginRequestPipelineScope(ILogger logger, HttpRequestMessage request) - { - return _beginRequestPipelineScope(logger, request.Method, GetUriString(request.RequestUri)); - } - - public static void RequestPipelineStart(ILogger logger, HttpRequestMessage request, Func shouldRedactHeaderValue) - { - _requestPipelineStart(logger, request.Method, GetUriString(request.RequestUri), null); - - if (logger.IsEnabled(LogLevel.Trace)) - { - logger.Log( - LogLevel.Trace, - EventIds.RequestHeader, - new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Request, request.Headers, request.Content?.Headers, shouldRedactHeaderValue), - null, - (state, ex) => state.ToString()); - } - } - - public static void RequestPipelineEnd(ILogger logger, HttpResponseMessage response, TimeSpan duration, Func shouldRedactHeaderValue) - { - _requestPipelineEnd(logger, duration.TotalMilliseconds, (int)response.StatusCode, null); - - if (logger.IsEnabled(LogLevel.Trace)) - { - logger.Log( - LogLevel.Trace, - EventIds.ResponseHeader, - new HttpHeadersLogValue(HttpHeadersLogValue.Kind.Response, response.Headers, response.Content?.Headers, shouldRedactHeaderValue), - null, - (state, ex) => state.ToString()); - } - } - - private static string? GetUriString(Uri? requestUri) - { - return requestUri?.IsAbsoluteUri == true - ? requestUri.AbsoluteUri - : requestUri?.ToString(); - } - } } } diff --git a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/LoggingUriOutputTests.cs b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/LoggingUriOutputTests.cs index bdd204f709df39..2e71063d1334dc 100644 --- a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/LoggingUriOutputTests.cs +++ b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/LoggingUriOutputTests.cs @@ -4,8 +4,8 @@ using System; using System.Linq; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Http.Logging; using Microsoft.Extensions.Logging; @@ -16,156 +16,186 @@ namespace Microsoft.Extensions.Http.Tests.Logging { public class LoggingUriOutputTests { - [Fact] - public async Task LoggingHttpMessageHandler_LogsAbsoluteUri() + private static class EventIds { - // Arrange - var sink = new TestSink(); + public static readonly EventId RequestStart = new EventId(100, "RequestStart"); + public static readonly EventId RequestEnd = new EventId(101, "RequestEnd"); - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - - serviceCollection - .AddHttpClient("test") - .ConfigurePrimaryHttpMessageHandler(() => new TestMessageHandler()); - - var services = serviceCollection.BuildServiceProvider(); - - var client = services.GetRequiredService().CreateClient("test"); - - - // Act - var request = new HttpRequestMessage(HttpMethod.Get, "http://api.example.com/search?term=Western%20Australia"); - - await client.SendAsync(request); - - // Assert - var messages = sink.Writes.ToArray(); - - var message = Assert.Single(messages.Where(m => - { - return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestStart && - m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; - })); - - Assert.Equal("Sending HTTP request GET http://api.example.com/search?term=Western%20Australia", message.Message); + public static readonly EventId PipelineStart = new EventId(100, "RequestPipelineStart"); + public static readonly EventId PipelineEnd = new EventId(101, "RequestPipelineEnd"); } - [Fact] - public async Task LoggingScopeHttpMessageHandler_LogsAbsoluteUri() + public static readonly TheoryData GetRedactedUriString_Data = new TheoryData() { - // Arrange - var sink = new TestSink(); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - - serviceCollection - .AddHttpClient("test") - .ConfigurePrimaryHttpMessageHandler(() => new TestMessageHandler()); - - var services = serviceCollection.BuildServiceProvider(); - - var client = services.GetRequiredService().CreateClient("test"); - - - // Act - var request = new HttpRequestMessage(HttpMethod.Get, "http://api.example.com/search?term=Western%20Australia"); - - await client.SendAsync(request); + { null, null }, + { "http://q.app/foo", "http://q.app/foo" }, + { "http://q.app:123/foo", "http://q.app:123/foo" }, + { "http://user:xxx@q.app/foo", "http://q.app/foo" }, // has user info + { "http://q.app/foo?", "http://q.app/foo?" }, + { "http://q.app/foo?XXX", "http://q.app/foo?*" }, + { "http://q.app/a/b/c?a=b%20c&x=1", "http://q.app/a/b/c?*" }, + { "http://q.app:4242/a/b/c?a=b%20c&x=1", "http://q.app:4242/a/b/c?*" }, + { "/cat/1/2", "*" }, // Relative Uris are fully redacted. + { "/cat/1/2?a=b%20c&x=1", "*" }, + }; + + [Theory] + [MemberData(nameof(GetRedactedUriString_Data))] + public void GetRedactedUriString_RedactsUriByDefault(string original, string expected) + { + Uri? uri = original != null ? new Uri(original, UriKind.RelativeOrAbsolute) : null; + string? actual = LogHelper.GetRedactedUriString(uri); - // Assert - var messages = sink.Writes.ToArray(); + Assert.Equal(expected, actual); + } - var message = Assert.Single(messages.Where(m => + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData("AppCtx")] // AppContext switch System.Net.Http.DisableUriRedaction = true + [InlineData("EnvVar1")] // Env. var DOTNET_SYSTEM_NET_DISABLEURIREDACTION = "1" + [InlineData("EnvVarTrue")] // Env. var DOTNET_SYSTEM_NET_DISABLEURIREDACTION = "true" + public void GetRedactedUriString_DisableUriRedaction_DoesNotRedactUri(string queryRedactionDisabler) + { + RemoteExecutor.Invoke(static queryRedactionDisabler => { - return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.PipelineStart && - m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; - })); - - Assert.Equal("Start processing HTTP request GET http://api.example.com/search?term=Western%20Australia", message.Message); - Assert.Equal("HTTP GET http://api.example.com/search?term=Western%20Australia", message.Scope.ToString()); + switch (queryRedactionDisabler) + { + case "AppCtx": + AppContext.SetSwitch("System.Net.Http.DisableUriRedaction", true); + break; + case "EnvVarTrue": + Environment.SetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION", "true"); + break; + case "EnvVar1": + Environment.SetEnvironmentVariable("DOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION", "1"); + break; + } + + Uri[] uris = GetRedactedUriString_Data.Select(a => a[0] == null ? null : new Uri((string)a[0], UriKind.RelativeOrAbsolute)).ToArray(); + + foreach (Uri uri in uris) + { + string? expected = uri != null ? uri.IsAbsoluteUri ? uri.AbsoluteUri : uri.ToString() : null; + string? actual = LogHelper.GetRedactedUriString(uri); + Assert.Equal(expected, actual); + } + }, queryRedactionDisabler).Dispose(); } + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(false, false)] + [InlineData(false, true)] #if NET - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] - public void LoggingHttpMessageHandler_LogsAbsoluteUri_Sync() + [InlineData(true, false)] + [InlineData(true, true)] +#endif + public async Task Handlers_LogExpectedUri(bool syncApi, bool scopeHandler) { - // Arrange - var sink = new TestSink(); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - - serviceCollection - .AddHttpClient("test") - .ConfigurePrimaryHttpMessageHandler(() => new TestMessageHandler()); - - var services = serviceCollection.BuildServiceProvider(); - - var client = services.GetRequiredService().CreateClient("test"); - + await RemoteExecutor.Invoke(static async (syncApiStr, scopeHandlerStr) => + { + bool syncApi = bool.Parse(syncApiStr); + bool scopeHandler = bool.Parse(scopeHandlerStr); - // Act - var request = new HttpRequestMessage(HttpMethod.Get, "http://api.example.com/search?term=Western%20Australia"); + string baseUri = "http://api.example.com/search"; + const string queryString = "term=Western%20Australia"; + string destinationUri = $"{baseUri}?{queryString}"; - client.Send(request); + var sink = new TestSink(); + var logger = new TestLogger("test", sink, enabled: true); - // Assert - var messages = sink.Writes.ToArray(); + DelegatingHandler handler = scopeHandler ? new LoggingScopeHttpMessageHandler(logger) : new LoggingHttpMessageHandler(logger); + handler.InnerHandler = new TestMessageHandler(); - var message = Assert.Single(messages.Where(m => - { - return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestStart && - m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; - })); + using HttpMessageInvoker invoker = new HttpMessageInvoker(handler); + using var request = new HttpRequestMessage(HttpMethod.Get, destinationUri); - Assert.Equal("Sending HTTP request GET http://api.example.com/search?term=Western%20Australia", message.Message); +#if NET + if (syncApi) + { + _ = invoker.Send(request, default); + await Task.Yield(); + } + else +#endif + { + _ = await invoker.SendAsync(request, default); + } + + if (scopeHandler) + { + var pipelineStartMessage = Assert.Single(sink.Writes.Where(m => m.EventId == EventIds.PipelineStart)); + Assert.Equal($"HTTP GET {baseUri}?*", pipelineStartMessage.Scope.ToString()); + Assert.Equal($"Start processing HTTP request GET {baseUri}?*", pipelineStartMessage.Message); + } + else + { + var requestStartMessage = Assert.Single(sink.Writes.Where(m => m.EventId == EventIds.RequestStart)); + Assert.Equal($"Sending HTTP request GET {baseUri}?*", requestStartMessage.Message); + } + }, syncApi.ToString(), scopeHandler.ToString()).DisposeAsync(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNetCore))] - public void LoggingScopeHttpMessageHandler_LogsAbsoluteUri_Sync() + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(false, false)] + [InlineData(false, true)] +#if NET + [InlineData(true, false)] + [InlineData(true, true)] +#endif + public async Task Integration_LogsExpectedAbsoluteUri(bool syncApi, bool disableUriQueryRedaction) { - // Arrange - var sink = new TestSink(); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); - - serviceCollection - .AddHttpClient("test") - .ConfigurePrimaryHttpMessageHandler(() => new TestMessageHandler()); - - var services = serviceCollection.BuildServiceProvider(); - - var client = services.GetRequiredService().CreateClient("test"); + const string baseUri = "http://api.example.com/search"; + const string queryString = "term=Western%20Australia"; + const string destinationUri = $"{baseUri}?{queryString}"; + await RemoteExecutor.Invoke(static async (syncApiStr, disableUriQueryRedactionStr) => + { + bool syncApi = bool.Parse(syncApiStr); + bool disableUriQueryRedaction = bool.Parse(disableUriQueryRedactionStr); + + if (disableUriQueryRedaction) + { + AppContext.SetSwitch("System.Net.Http.DisableUriRedaction", true); + } + + var sink = new TestSink(); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddSingleton(new TestLoggerFactory(sink, enabled: true)); + serviceCollection.AddTransient(); + serviceCollection.AddHttpClient("test").ConfigurePrimaryHttpMessageHandler(); + + var services = serviceCollection.BuildServiceProvider(); + var factory = services.GetRequiredService(); + var client = factory.CreateClient("test"); + + using var request = new HttpRequestMessage(HttpMethod.Get, destinationUri); +#if NET + if (syncApi) + { + _ = client.Send(request); + await Task.Yield(); + } + else +#endif + { + _ = await client.SendAsync(request); + } - // Act - var request = new HttpRequestMessage(HttpMethod.Get, "http://api.example.com/search?term=Western%20Australia"); + var pipelineStartMessage = Assert.Single(sink.Writes.Where(m => + m.EventId == EventIds.PipelineStart && + m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler")); - client.Send(request); + var requestStartMessage = Assert.Single(sink.Writes.Where(m => + m.EventId == EventIds.RequestStart && + m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler")); - // Assert - var messages = sink.Writes.ToArray(); + string expectedUri = disableUriQueryRedaction ? destinationUri : $"{baseUri}?*"; - var message = Assert.Single(messages.Where(m => - { - return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.PipelineStart && - m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; - })); + Assert.Equal($"HTTP GET {expectedUri}", pipelineStartMessage.Scope.ToString()); + Assert.Equal($"Start processing HTTP request GET {expectedUri}", pipelineStartMessage.Message); + Assert.Equal($"Sending HTTP request GET {expectedUri}", requestStartMessage.Message); - Assert.Equal("Start processing HTTP request GET http://api.example.com/search?term=Western%20Australia", message.Message); - Assert.Equal("HTTP GET http://api.example.com/search?term=Western%20Australia", message.Scope.ToString()); + }, syncApi.ToString(), disableUriQueryRedaction.ToString()).DisposeAsync(); } -#endif } } diff --git a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/RedactedLogValueIntegrationTest.cs b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/RedactedLogValueIntegrationTest.cs index 4a4c1327401c1f..20279de259856c 100644 --- a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/RedactedLogValueIntegrationTest.cs +++ b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Logging/RedactedLogValueIntegrationTest.cs @@ -16,6 +16,15 @@ namespace Microsoft.Extensions.Http.Logging { public class RedactedLogValueIntegrationTest { + private static class EventIds + { + public static readonly EventId RequestHeader = new EventId(102, "RequestHeader"); + public static readonly EventId ResponseHeader = new EventId(103, "ResponseHeader"); + + public static readonly EventId RequestPipelineRequestHeader = new EventId(102, "RequestPipelineRequestHeader"); + public static readonly EventId RequestPipelineResponseHeader = new EventId(103, "RequestPipelineResponseHeader"); + } + [Fact] public async Task RedactHeaderValueWithHeaderList_ValueIsRedactedBeforeLogging() { @@ -48,7 +57,7 @@ public async Task RedactHeaderValueWithHeaderList_ValueIsRedactedBeforeLogging() var message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.RequestHeader && + m.EventId == EventIds.RequestPipelineRequestHeader && m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -60,7 +69,7 @@ public async Task RedactHeaderValueWithHeaderList_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestHeader && + m.EventId == EventIds.RequestHeader && m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -72,7 +81,7 @@ public async Task RedactHeaderValueWithHeaderList_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.ResponseHeader && + m.EventId == EventIds.ResponseHeader && m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -84,7 +93,7 @@ public async Task RedactHeaderValueWithHeaderList_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.ResponseHeader && + m.EventId == EventIds.RequestPipelineResponseHeader && m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -129,7 +138,7 @@ public async Task RedactHeaderValueWithPredicate_ValueIsRedactedBeforeLogging() var message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.RequestHeader && + m.EventId == EventIds.RequestPipelineRequestHeader && m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -141,7 +150,7 @@ public async Task RedactHeaderValueWithPredicate_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.RequestHeader && + m.EventId == EventIds.RequestHeader && m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -153,7 +162,7 @@ public async Task RedactHeaderValueWithPredicate_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingHttpMessageHandler.Log.EventIds.ResponseHeader && + m.EventId == EventIds.ResponseHeader && m.LoggerName == "System.Net.Http.HttpClient.test.ClientHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( @@ -165,7 +174,7 @@ public async Task RedactHeaderValueWithPredicate_ValueIsRedactedBeforeLogging() message = Assert.Single(messages.Where(m => { return - m.EventId == LoggingScopeHttpMessageHandler.Log.EventIds.ResponseHeader && + m.EventId == EventIds.RequestPipelineResponseHeader && m.LoggerName == "System.Net.Http.HttpClient.test.LogicalHandler"; })); Assert.StartsWith(LineEndingsHelper.Normalize( diff --git a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Microsoft.Extensions.Http.Tests.csproj b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Microsoft.Extensions.Http.Tests.csproj index f81246bb66d002..b1a3e853dc5cbc 100644 --- a/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Microsoft.Extensions.Http.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.Http/tests/Microsoft.Extensions.Http.Tests/Microsoft.Extensions.Http.Tests.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent);$(NetFrameworkMinimum) true + true @@ -20,6 +21,8 @@ Link="tests\DI.Common\Common\src\TestLoggerFactory.cs" /> + From 85eb0b5c682ccbb25fa451694472500cbf5ee119 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 5 Jul 2024 04:03:33 +0100 Subject: [PATCH 18/72] Remove `virtual` modifer from `Calendar.CurrentEraValue` (#104453) --- .../System.Private.CoreLib/src/System/Globalization/Calendar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs index 49eb9e8cec98e4..594a967635c3e5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Calendar.cs @@ -100,7 +100,7 @@ internal void SetReadOnlyState(bool readOnly) /// /// This is used to convert CurrentEra(0) to an appropriate era value. /// - internal virtual int CurrentEraValue + internal int CurrentEraValue { get { From 220f05fbaec0519a02d78b8753e729aebb73a714 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 4 Jul 2024 22:14:40 -0500 Subject: [PATCH 19/72] Simplify Emit SignatureHelper WriteSimpleSignature (#104220) * Simplify Emit SignatureHelper WriteSimpleSignature Make the `default` case explicit and swap the early `return` to `break`s. * Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs Co-authored-by: Buyaa Namnan * Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs --------- Co-authored-by: Jan Kotas Co-authored-by: Buyaa Namnan --- .../System/Reflection/Emit/SignatureHelper.cs | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs index 826088b62ec4d6..5722f2de1cb791 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/SignatureHelper.cs @@ -235,62 +235,63 @@ private static void WriteSimpleSignature(SignatureTypeEncoder signature, Type ty { case CoreTypeId.Void: signature.Builder.WriteByte((byte)SignatureTypeCode.Void); - return; + break; case CoreTypeId.Boolean: signature.Boolean(); - return; + break; case CoreTypeId.Byte: signature.Byte(); - return; + break; case CoreTypeId.SByte: signature.SByte(); - return; + break; case CoreTypeId.Char: signature.Char(); - return; + break; case CoreTypeId.Int16: signature.Int16(); - return; + break; case CoreTypeId.UInt16: signature.UInt16(); - return; + break; case CoreTypeId.Int32: signature.Int32(); - return; + break; case CoreTypeId.UInt32: signature.UInt32(); - return; + break; case CoreTypeId.Int64: signature.Int64(); - return; + break; case CoreTypeId.UInt64: signature.UInt64(); - return; + break; case CoreTypeId.Single: signature.Single(); - return; + break; case CoreTypeId.Double: signature.Double(); - return; + break; case CoreTypeId.IntPtr: signature.IntPtr(); - return; + break; case CoreTypeId.UIntPtr: signature.UIntPtr(); - return; + break; case CoreTypeId.Object: signature.Object(); - return; + break; case CoreTypeId.String: signature.String(); - return; + break; case CoreTypeId.TypedReference: signature.TypedReference(); - return; + break; + default: // handles null and all other types + EntityHandle typeHandle = module.GetTypeHandle(type); + signature.Type(typeHandle, type.IsValueType); + break; } - - EntityHandle typeHandle = module.GetTypeHandle(type); - signature.Type(typeHandle, type.IsValueType); } } From 0afc500946f8c50fb8120d108a387b57e143eab3 Mon Sep 17 00:00:00 2001 From: Juan Hoyos <19413848+hoyosjs@users.noreply.github.com> Date: Thu, 4 Jul 2024 21:36:20 -0700 Subject: [PATCH 20/72] Fix GetStaticsBases (#104412) * Fix GetStaticsBases * Enum all the statics info memory Co-authored-by: Jan Kotas --------- Co-authored-by: Mike McLaughlin Co-authored-by: Jan Kotas --- src/coreclr/vm/methodtable.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index bb8a1646e511b2..f93b3b2a1f4dcc 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -7507,16 +7507,6 @@ MethodTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) pMTParent->EnumMemoryRegions(flags); } - if (HasNonVirtualSlots()) - { - DacEnumMemoryRegion(dac_cast(MethodTableAuxiliaryData::GetNonVirtualSlotsArray(GetAuxiliaryData())) - GetNonVirtualSlotsArraySize(), GetNonVirtualSlotsArraySize()); - } - - if (HasGenericsStaticsInfo()) - { - DacEnumMemoryRegion(dac_cast(GetGenericsStaticsInfo()), sizeof(GenericsStaticsInfo)); - } - if (HasInterfaceMap()) { #ifdef FEATURE_COMINTEROP @@ -7551,6 +7541,23 @@ MethodTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) if (pAuxiliaryData.IsValid()) { pAuxiliaryData.EnumMem(); + + if (HasGenericsStaticsInfo()) + { + MethodTableAuxiliaryData::GetGenericStaticsInfo(pAuxiliaryData).EnumMem(); + } + if (IsDynamicStatics()) + { + MethodTableAuxiliaryData::GetDynamicStaticsInfo(pAuxiliaryData).EnumMem(); + } + if (GetNumThreadStaticFields() > 0) + { + MethodTableAuxiliaryData::GetThreadStaticsInfo(pAuxiliaryData).EnumMem(); + } + if (HasNonVirtualSlots()) + { + DacEnumMemoryRegion(dac_cast(MethodTableAuxiliaryData::GetNonVirtualSlotsArray(pAuxiliaryData)) - GetNonVirtualSlotsArraySize(), GetNonVirtualSlotsArraySize()); + } } if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2) From ceac9f48c610d62857a66f5f0d63901dd43c3bcb Mon Sep 17 00:00:00 2001 From: yurai007 Date: Fri, 5 Jul 2024 07:15:58 +0200 Subject: [PATCH 21/72] [RISC-V] Fix coreclr test readytorun/coreroot_determinism/coreroot_determinism/coreroot_determinism.sh (#104140) To make test passing we increase TimeoutMilliseconds to 30 min. --- src/tests/readytorun/coreroot_determinism/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/readytorun/coreroot_determinism/Program.cs b/src/tests/readytorun/coreroot_determinism/Program.cs index 1a53f284c534af..242f9c434d1bf0 100644 --- a/src/tests/readytorun/coreroot_determinism/Program.cs +++ b/src/tests/readytorun/coreroot_determinism/Program.cs @@ -94,7 +94,7 @@ public static bool CompileWithSeed(int seed, string coreRootPath, string compila Directory.Delete(outDir, true); } Directory.CreateDirectory(outDir); - ProcessStartInfo processStartInfo = new ProcessStartInfo(coreRunPath, $"{superIlcPath} compile-directory -cr {coreRootPath} -in {compilationInputFolder} --nojit --noexe --large-bubble --release --nocleanup -out {outDir}"); + ProcessStartInfo processStartInfo = new ProcessStartInfo(coreRunPath, $"{superIlcPath} compile-directory -cr {coreRootPath} -in {compilationInputFolder} --nojit --noexe --large-bubble --release --nocleanup -ct 30 -out {outDir}"); var process = Process.Start(processStartInfo); process.WaitForExit(); if (process.ExitCode != 0) From 49757a5a106f8dc75bb2cc210f44eec65d8bd26f Mon Sep 17 00:00:00 2001 From: Matous Kozak <55735845+matouskozak@users.noreply.github.com> Date: Fri, 5 Jul 2024 09:01:09 +0200 Subject: [PATCH 22/72] Disable EmptyStructs tests for Apple mobile (#104433) --- src/tests/JIT/Directed/StructABI/EmptyStructs.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj b/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj index 372d01156084c0..d2670bae8018d1 100644 --- a/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj +++ b/src/tests/JIT/Directed/StructABI/EmptyStructs.csproj @@ -3,6 +3,8 @@ true true + + true PdbOnly From 0dac63563156579c7b3027ee15a51f1f19e590c2 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 5 Jul 2024 10:04:10 +0200 Subject: [PATCH 23/72] JIT: Fix "elaborated type specifier" for enum class in JIT (#104450) It appears even scoped enums should just get the `enum` keyword in these cases. Fix #104442 --- src/coreclr/jit/gentree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b17480b6d296a1..04c5605fe7bfdf 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4658,7 +4658,7 @@ struct NewCallArg // The class handle if SignatureType == TYP_STRUCT. CORINFO_CLASS_HANDLE SignatureClsHnd = NO_CLASS_HANDLE; // The type of well known arg - enum class WellKnownArg WellKnownArg = ::WellKnownArg::None; + enum WellKnownArg WellKnownArg = ::WellKnownArg::None; NewCallArg WellKnown(::WellKnownArg type) const { From 116f5fd624c6981a66c2a03c5ea5f8fa037ef57f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Fri, 5 Jul 2024 09:52:05 +0000 Subject: [PATCH 24/72] [wasm] Fix a typo in browser-bench wasm app (#104434) --- src/mono/sample/wasm/browser-bench/String.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/sample/wasm/browser-bench/String.cs b/src/mono/sample/wasm/browser-bench/String.cs index c00448632fc16d..1bdd23c2032c2d 100644 --- a/src/mono/sample/wasm/browser-bench/String.cs +++ b/src/mono/sample/wasm/browser-bench/String.cs @@ -136,7 +136,7 @@ public class TextInfoToUpper : TextInfoMeasurement public class TextInfoToTitleCase : TextInfoMeasurement { - public override string Name => "TextInfo ToTileCase"; + public override string Name => "TextInfo ToTitleCase"; public override void RunStep() => textInfo.ToTitleCase(str); } From bd752c34ef4ff382a3480da020def17906701199 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Fri, 5 Jul 2024 13:01:42 +0200 Subject: [PATCH 25/72] Avoid unobserved task exceptions in Http tests (#104384) * Improve HttpProtocolTests * Avoid unobserved exceptions from H2's read loop * More test changes * Actually do what the test name suggests * Fix Dispose_DisposingHandlerCancelsActiveOperationsWithoutResponses * Revert some changes that Browser doesn't like --- .../HttpClientHandlerTest.AutoRedirect.cs | 6 +- .../HttpClientHandlerTest.Cancellation.cs | 85 +++++-------------- .../Net/Http/HttpClientHandlerTest.Proxy.cs | 27 +----- .../System/Net/Http/HttpClientHandlerTest.cs | 52 +++++------- .../Net/Http/HttpClientHandlerTestBase.cs | 16 ++++ .../System/Net/Http/HttpProtocolTests.cs | 75 ++++++---------- .../SocketsHttpHandler/Http2Connection.cs | 1 + .../tests/FunctionalTests/DiagnosticsTests.cs | 2 - .../HttpClientHandlerTest.Http2.cs | 8 +- .../tests/FunctionalTests/HttpClientTest.cs | 63 ++++---------- .../tests/FunctionalTests/HttpContentTest.cs | 36 +------- .../tests/FunctionalTests/MetricsTest.cs | 20 ++--- ...etsHttpHandlerTest.Http2ExtendedConnect.cs | 16 +--- .../FunctionalTests/SocketsHttpHandlerTest.cs | 43 +++------- .../tests/FunctionalTests/SocksProxyTest.cs | 9 +- 15 files changed, 134 insertions(+), 325 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs index 1fa1642bd4e89c..d94b57c5b825a0 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs @@ -182,11 +182,13 @@ public async Task GetAsync_AllowAutoRedirectTrue_NonRedirectStatusCode_LocationH { await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => { + Task redirectTask = null; + await LoopbackServer.CreateServerAsync(async (redirectServer, redirectUrl) => { Task getResponseTask = client.GetAsync(TestAsync, origUrl); - Task redirectTask = redirectServer.AcceptConnectionSendResponseAndCloseAsync(); + redirectTask = redirectServer.AcceptConnectionSendResponseAndCloseAsync(); await TestHelper.WhenAllCompletedOrAnyFailed( getResponseTask, @@ -199,6 +201,8 @@ await TestHelper.WhenAllCompletedOrAnyFailed( Assert.False(redirectTask.IsCompleted, "Should not have redirected to Location"); } }); + + await IgnoreExceptions(redirectTask); }); } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs index dea298f2ee90eb..c5bed9f4567390 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Cancellation.cs @@ -86,14 +86,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => } }, async server => { - try - { - await server.AcceptConnectionAsync(connection => serverRelease.Task); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.AcceptConnectionAsync(connection => serverRelease.Task)); }); } @@ -142,15 +135,9 @@ await ValidateClientCancellationAsync(async () => await getResponse; }); - try - { - clientFinished.SetResult(true); - await serverTask; - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + clientFinished.SetResult(true); + + await IgnoreExceptions(serverTask); }); } } @@ -203,15 +190,9 @@ await ValidateClientCancellationAsync(async () => await getResponse; }); - try - { - clientFinished.SetResult(true); - await serverTask; - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + clientFinished.SetResult(true); + + await IgnoreExceptions(serverTask); }); } } @@ -279,15 +260,10 @@ await ValidateClientCancellationAsync(async () => cts.Cancel(); await readTask; }).WaitAsync(TimeSpan.FromSeconds(30)); - try - { - clientFinished.SetResult(true); - await serverTask; - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + + clientFinished.SetResult(true); + + await IgnoreExceptions(serverTask); }); } } @@ -425,39 +401,18 @@ await LoopbackServer.CreateServerAsync(async (server, url) => [Fact] public async Task SendAsync_Cancel_CancellationTokenPropagates() { - TaskCompletionSource clientCanceled = new TaskCompletionSource(); - await LoopbackServerFactory.CreateClientAndServerAsync( - async uri => - { - var cts = new CancellationTokenSource(); - cts.Cancel(); + var cts = new CancellationTokenSource(); + cts.Cancel(); - using (HttpClient client = CreateHttpClient()) - { - OperationCanceledException ex = null; - try - { - await client.GetAsync(uri, cts.Token); - } - catch (OperationCanceledException e) - { - ex = e; - } - Assert.True(ex != null, "Expected OperationCancelledException, but no exception was thrown."); + using HttpClient client = CreateHttpClient(); - Assert.True(cts.Token.IsCancellationRequested, "cts token IsCancellationRequested"); + OperationCanceledException oce = await Assert.ThrowsAnyAsync( + () => client.GetAsync(TestAsync, InvalidUri, cancellationToken: cts.Token)); - // .NET Framework has bug where it doesn't propagate token information. - Assert.True(ex.CancellationToken.IsCancellationRequested, "exception token IsCancellationRequested"); - - clientCanceled.SetResult(true); - } - }, - async server => - { - Task serverTask = server.HandleRequestAsync(); - await clientCanceled.Task; - }); + // .NET Framework has bug where it doesn't propagate token information. +#if !NETFRAMEWORK + Assert.Equal(cts.Token, oce.CancellationToken); +#endif } public static IEnumerable PostAsync_Cancel_CancellationTokenPassedToContent_MemberData() diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index 1946228bf7fad8..a7bab6a78b6f0f 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -573,14 +573,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try - { - await client.GetAsync(uri); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(client.GetAsync(uri)); } }, server => server.AcceptConnectionAsync(async connection => { @@ -613,14 +606,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try - { - await client.GetAsync(addressUri); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(client.GetAsync(addressUri)); } }, server => server.AcceptConnectionAsync(async connection => { @@ -647,14 +633,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try - { - await client.GetAsync(addressUri); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(client.GetAsync(addressUri)); } }, server => server.AcceptConnectionAsync(async connection => { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 1bd610ef10dda1..b45f2a5b232b72 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -247,14 +247,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri => using (HttpClient client = CreateHttpClient(handler)) { handler.Proxy = new WebProxy(proxyUri); - try - { - await client.GetAsync(ipv6Address); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(client.GetAsync(ipv6Address)); } }, server => server.AcceptConnectionAsync(async connection => { @@ -299,14 +292,8 @@ await LoopbackServer.CreateClientAndServerAsync(async url => // we could not create SslStream in browser, [ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } - try - { - await client.GetAsync(url); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + + await IgnoreExceptions(client.GetAsync(url)); } }, server => server.AcceptConnectionAsync(async connection => { @@ -909,18 +896,15 @@ await LoopbackServer.CreateServerAsync(async (server, url) => Task serverTask = server.AcceptConnectionAsync(async connection => { await connection.ReadRequestHeaderAndSendCustomResponseAsync("HTTP/1.1 200 OK\r\n" + LoopbackServer.CorsHeaders + "Transfer-Encoding: chunked\r\n\r\n"); - try + + await IgnoreExceptions(async () => { while (!cts.IsCancellationRequested) // infinite to make sure implementation doesn't OOM { await connection.WriteStringAsync(new string(' ', 10000)); await Task.Delay(1); } - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + }); }); await Assert.ThrowsAsync(() => client.GetAsync(url)); @@ -1437,6 +1421,11 @@ public async Task Dispose_DisposingHandlerCancelsActiveOperationsWithoutResponse return; } + // Tasks are stored and awaited outside of the CreateServerAsync callbacks + // to let server disposal abort any pending operations. + Task serverTask1 = null; + Task serverTask2 = null; + await LoopbackServerFactory.CreateServerAsync(async (server1, url1) => { await LoopbackServerFactory.CreateServerAsync(async (server2, url2) => @@ -1446,13 +1435,13 @@ await LoopbackServerFactory.CreateServerAsync(async (server3, url3) => var unblockServers = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); // First server connects but doesn't send any response yet - Task serverTask1 = server1.AcceptConnectionAsync(async connection1 => + serverTask1 = server1.AcceptConnectionAsync(async connection1 => { await unblockServers.Task; }); // Second server connects and sends some but not all headers - Task serverTask2 = server2.AcceptConnectionAsync(async connection2 => + serverTask2 = server2.AcceptConnectionAsync(async connection2 => { await connection2.ReadRequestDataAsync(); await connection2.SendPartialResponseHeadersAsync(HttpStatusCode.OK); @@ -1489,9 +1478,14 @@ await LoopbackServerFactory.CreateServerAsync(async (server3, url3) => { Assert.Equal("12345678901234567890", await response3.Content.ReadAsStringAsync()); } + + await serverTask3; }); }); }); + + await IgnoreExceptions(serverTask1); + await IgnoreExceptions(serverTask2); } [Theory] @@ -1806,15 +1800,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => { await server.AcceptConnectionAsync(async connection => { - try - { - await connection.ReadRequestDataAsync(readBody: true); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } - await clientFinished.Task.WaitAsync(TimeSpan.FromMinutes(2)); + await IgnoreExceptions(connection.ReadRequestDataAsync(readBody: true)); }); }); } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs index 689a6d6821eb62..f2874862d08226 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTestBase.cs @@ -16,6 +16,8 @@ namespace System.Net.Http.Functional.Tests public abstract partial class HttpClientHandlerTestBase : FileCleanupTestBase { + protected static readonly Uri InvalidUri = new("http://nosuchhost.invalid"); + // This file is shared with the WinHttpHandler implementation, which supports .NET Framework // So, define this so derived tests can use it. public static readonly Version HttpVersion30 = new Version(3, 0); @@ -31,6 +33,20 @@ public HttpClientHandlerTestBase(ITestOutputHelper output) _output = output; } + protected async Task IgnoreExceptions(Func func) + { + try + { + await func(); + } + catch (Exception ex) + { + _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); + } + } + + protected Task IgnoreExceptions(Task task) => IgnoreExceptions(() => task); + protected virtual HttpClient CreateHttpClient() => CreateHttpClient(CreateHttpClientHandler()); protected HttpClient CreateHttpClient(HttpMessageHandler handler) => diff --git a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs index c75c6de51f6c91..9c1838a2ea9225 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpProtocolTests.cs @@ -65,28 +65,6 @@ await LoopbackServer.CreateServerAsync(async (server, url) => }, new LoopbackServer.Options { StreamWrapper = GetStream }); } - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(9)] - public async Task GetAsync_RequestVersion0X_ThrowsNotSupportedException(int minorVersion) - { - if (IsWinHttpHandler) - { - return; - } - - using (HttpClient client = CreateHttpClient()) - { - HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://nosuchhost.invalid"); - request.Version = new Version(0, minorVersion); - - Task getResponseTask = client.SendAsync(TestAsync, request); - - await Assert.ThrowsAsync(() => getResponseTask); - } - } - [Theory] [InlineData(2, 0)] // This is plain HTTP (not HTTPS), so 2.0 is not supported and should degrade to 1.1 [InlineData(3, 0)] @@ -117,6 +95,8 @@ await LoopbackServer.CreateServerAsync(async (server, url) => [Theory] [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(0, 9)] [InlineData(1, 2)] [InlineData(1, 6)] [InlineData(2, 1)] @@ -137,11 +117,9 @@ await LoopbackServer.CreateServerAsync(async (server, url) => { HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); request.Version = version; - Task getResponseTask = client.SendAsync(TestAsync, request); - Task> serverTask = server.AcceptConnectionSendResponseAndCloseAsync(); - await Assert.ThrowsAsync(() => getResponseTask); + await Assert.ThrowsAsync(() => client.SendAsync(TestAsync, request)); } - }, new LoopbackServer.Options { StreamWrapper = GetStream }); + }); } [Theory] @@ -216,23 +194,23 @@ await LoopbackServer.CreateServerAsync(async (server, url) => [InlineData(2, 1)] [InlineData(3, 0)] [InlineData(4, 2)] - public async Task GetAsyncVersion11_BadResponseVersion_ThrowsOr00(int responseMajorVersion, int responseMinorVersion) + public async Task GetAsyncVersion11_BadResponseVersion_Throws(int responseMajorVersion, int responseMinorVersion) { - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClient client = CreateHttpClient()) + await LoopbackServer.CreateClientAndServerAsync( + async uri => { - HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url); - request.Version = HttpVersion.Version11; + using HttpClient client = CreateHttpClient(); - Task getResponseTask = client.SendAsync(TestAsync, request); - Task> serverTask = - server.AcceptConnectionSendCustomResponseAndCloseAsync( - $"HTTP/{responseMajorVersion}.{responseMinorVersion} 200 OK\r\nConnection: close\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n"); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri); + request.Version = HttpVersion.Version11; - await Assert.ThrowsAsync(() => getResponseTask); - } - }, new LoopbackServer.Options { StreamWrapper = GetStream }); + await Assert.ThrowsAsync(() => client.SendAsync(TestAsync, request)); + }, + async server => + { + await IgnoreExceptions(server.AcceptConnectionSendCustomResponseAndCloseAsync( + $"HTTP/{responseMajorVersion}.{responseMinorVersion} 200 OK\r\nConnection: close\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n")); + }, new LoopbackServer.Options { StreamWrapper = GetStream }); } [Theory] @@ -374,16 +352,17 @@ public async Task GetAsync_InvalidStatusLine_ThrowsExceptionOnSocketsHttpHandler private async Task GetAsyncThrowsExceptionHelper(string responseString) { - await LoopbackServer.CreateServerAsync(async (server, url) => - { - using (HttpClient client = CreateHttpClient()) + await LoopbackServer.CreateClientAndServerAsync( + async uri => { - Task ignoredServerTask = server.AcceptConnectionSendCustomResponseAndCloseAsync( - responseString + "\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"); - - await Assert.ThrowsAsync(() => client.GetAsync(TestAsync, url)); - } - }, new LoopbackServer.Options { StreamWrapper = GetStream }); + using HttpClient client = CreateHttpClient(); + await Assert.ThrowsAsync(() => client.GetAsync(TestAsync, uri)); + }, + async server => + { + await IgnoreExceptions(server.AcceptConnectionSendCustomResponseAndCloseAsync( + responseString + "\r\nConnection: close\r\nContent-Length: 0\r\n\r\n")); + }, new LoopbackServer.Options { StreamWrapper = GetStream }); } [Theory] diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 2575c76201ec34..40202ff846d4a3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -496,6 +496,7 @@ private async Task ProcessIncomingFramesAsync() catch (HttpProtocolException e) { InitialSettingsReceived.TrySetException(e); + LogExceptions(InitialSettingsReceived.Task); throw; } catch (Exception e) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs index 9059a05d49f6bf..a46d376157fc15 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/DiagnosticsTests.cs @@ -26,8 +26,6 @@ public abstract class DiagnosticsTest : HttpClientHandlerTestBase private static bool EnableActivityPropagationEnvironmentVariableIsNotSetAndRemoteExecutorSupported => string.IsNullOrEmpty(Environment.GetEnvironmentVariable(EnableActivityPropagationEnvironmentVariableSettingName)) && RemoteExecutor.IsSupported; - private static readonly Uri InvalidUri = new("http://nosuchhost.invalid"); - public DiagnosticsTest(ITestOutputHelper output) : base(output) { } [Fact] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index 3036935ef5533d..256a04e62da26f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -3276,15 +3276,11 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async url => // We should not reach retry limit without failing. Assert.NotEqual(0, maxCount); - try + await IgnoreExceptions(async () => { await connection.SendGoAway(streamId); await connection.WaitForConnectionShutdownAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + }); }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index bdf38ef6e7fb60..47fb4f3de6c786 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -429,14 +429,7 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try - { - await connection.ReadRequestHeaderAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.ReadRequestHeaderAsync()); cts.Cancel(); }); }); @@ -599,14 +592,7 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try - { - await connection.ReadRequestHeaderAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.ReadRequestHeaderAsync()); cts.Cancel(); }); }); @@ -676,14 +662,7 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try - { - await connection.ReadRequestHeaderAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.ReadRequestHeaderAsync()); cts.Cancel(); }); }); @@ -1053,16 +1032,12 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try + await IgnoreExceptions(async () => { await connection.ReadRequestHeaderAsync(); cts.Cancel(); await connection.ReadRequestBodyAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + }); }); }); } @@ -1103,15 +1078,7 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try - { - await connection.ReadRequestHeaderAsync(); - await connection.ReadRequestBodyAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.ReadRequestDataAsync()); }); }); } @@ -1151,7 +1118,7 @@ await LoopbackServer.CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - try + await IgnoreExceptions(async () => { await connection.ReadRequestDataAsync(); await connection.SendResponseAsync(headers: new List() { @@ -1163,11 +1130,7 @@ await server.AcceptConnectionAsync(async connection => await connection.WriteStringAsync(content); await Task.Delay(TimeSpan.FromSeconds(0.1)); } - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + }); }); }); } @@ -1183,9 +1146,7 @@ public async Task Send_TimeoutResponseContent_Throws() using var server = new LoopbackServer(); await server.ListenAsync(); - // Ignore all failures from the server. This includes being disposed of before ever accepting a connection, - // which is possible if the client times out so quickly that it hasn't initiated a connection yet. - _ = server.AcceptConnectionAsync(async connection => + Task serverTask = server.AcceptConnectionAsync(async connection => { await connection.ReadRequestDataAsync(); await connection.SendResponseAsync(headers: new[] { new HttpHeaderData("Content-Length", (Content.Length * 100).ToString()) }); @@ -1203,6 +1164,12 @@ public async Task Send_TimeoutResponseContent_Throws() HttpResponseMessage response = httpClient.Send(new HttpRequestMessage(HttpMethod.Get, server.Address)); }); Assert.IsType(ex.InnerException); + + server.Dispose(); + + // Ignore all failures from the server. This includes being disposed of before ever accepting a connection, + // which is possible if the client times out so quickly that it hasn't initiated a connection yet. + await IgnoreExceptions(serverTask); } public static IEnumerable VersionSelectionMemberData() diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index 74fb09117bd855..6cb10e6ca7460a 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -702,14 +702,7 @@ await LoopbackServer.CreateClientAndServerAsync( }, async server => { - try - { - await server.AcceptConnectionSendResponseAndCloseAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.AcceptConnectionSendResponseAndCloseAsync()); }); } @@ -738,14 +731,7 @@ await server.AcceptConnectionAsync(async connection => await Task.Delay(250); cts.Cancel(); await Task.Delay(500); - try - { - await connection.SendResponseAsync(new string('a', 100)); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.SendResponseAsync(new string('a', 100))); }); }); } @@ -797,14 +783,7 @@ await LoopbackServer.CreateClientAndServerAsync( }, async server => { - try - { - await server.AcceptConnectionSendResponseAndCloseAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.AcceptConnectionSendResponseAndCloseAsync()); }); } @@ -833,14 +812,7 @@ await server.AcceptConnectionAsync(async connection => await Task.Delay(250); cts.Cancel(); await Task.Delay(500); - try - { - await connection.SendResponseAsync(new string('a', 100)); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(connection.SendResponseAsync(new string('a', 100))); }); }); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs index e6c74540a9053f..af723f8cc73280 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs @@ -680,7 +680,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }, async server => { - try + await IgnoreExceptions(async () => { await server.AcceptConnectionAsync(async connection => { @@ -688,11 +688,7 @@ await server.AcceptConnectionAsync(async connection => requestReceived.SetResult(); await clientCompleted.Task.WaitAsync(TestHelper.PassingTestTimeout); }); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception: {ex}"); - } + }); }); } @@ -843,7 +839,7 @@ public Task RequestDuration_ErrorStatus_ErrorTypeRecorded(int statusCode) public async Task RequestDuration_ConnectionClosedWhileReceivingHeaders_Recorded() { using CancellationTokenSource cancelServerCts = new CancellationTokenSource(); - await LoopbackServerFactory.CreateClientAndServerAsync(async uri => + await LoopbackServer.CreateClientAndServerAsync(async uri => { using HttpMessageInvoker client = CreateHttpMessageInvoker(); using InstrumentRecorder recorder = SetupInstrumentRecorder(InstrumentNames.RequestDuration); @@ -862,15 +858,11 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => VerifyRequestDuration(m, uri, acceptedErrorTypes: [typeof(TaskCanceledException).FullName, "response_ended"]); }, async server => { - try + await IgnoreExceptions(async () => { - var connection = (LoopbackServer.Connection)await server.EstablishGenericConnectionAsync().WaitAsync(cancelServerCts.Token); + LoopbackServer.Connection connection = await server.EstablishConnectionAsync().WaitAsync(cancelServerCts.Token); connection.Socket.Shutdown(SocketShutdown.Send); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception: {ex}"); - } + }); }); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2ExtendedConnect.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2ExtendedConnect.cs index f3a46d4d08507e..5071bfcf29b27a 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2ExtendedConnect.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2ExtendedConnect.cs @@ -127,17 +127,13 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }, async server => { - try + await IgnoreExceptions(async () => { await server.AcceptConnectionAsync(async connection => { await clientCompleted.Task.WaitAsync(TestHelper.PassingTestTimeout); }); - } - catch (Exception ex) - { - _output.WriteLine($"Ignoring exception {ex}"); - } + }); }, options: new GenericLoopbackOptions { UseSsl = useSsl }); } @@ -157,7 +153,7 @@ public async Task Connect_Http11Endpoint_Throws(bool useSsl) Task serverTask = Task.Run(async () => { - try + await IgnoreExceptions(async () => { await server.AcceptConnectionAsync(async connection => { @@ -170,11 +166,7 @@ await server.AcceptConnectionAsync(async connection => await clientCompleted.Task.WaitAsync(TestHelper.PassingTestTimeout); } }); - } - catch (Exception ex) - { - _output.WriteLine($"Ignoring exception {ex}"); - } + }); }); Task clientTask = Task.Run(async () => diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 4c25eda1fdb184..ea34d7bf2479d6 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -451,13 +451,13 @@ public void MaxResponseDrainSize_InvalidArgument_Throws() } [Fact] - public void MaxResponseDrainSize_SetAfterUse_Throws() + public async Task MaxResponseDrainSize_SetAfterUse_Throws() { using (var handler = new SocketsHttpHandler()) using (HttpClient client = CreateHttpClient(handler)) { handler.MaxResponseDrainSize = 1; - _ = client.GetAsync($"http://{Guid.NewGuid():N}"); // ignoring failure + await Assert.ThrowsAnyAsync(() => client.GetAsync(InvalidUri)); Assert.Equal(1, handler.MaxResponseDrainSize); Assert.Throws(() => handler.MaxResponseDrainSize = 1); } @@ -494,13 +494,13 @@ public void MaxResponseDraiTime_InvalidArgument_Throws() } [Fact] - public void ResponseDrainTimeout_SetAfterUse_Throws() + public async Task ResponseDrainTimeout_SetAfterUse_Throws() { using (var handler = new SocketsHttpHandler()) using (HttpClient client = CreateHttpClient(handler)) { handler.ResponseDrainTimeout = TimeSpan.FromSeconds(42); - _ = client.GetAsync($"http://{Guid.NewGuid():N}"); // ignoring failure + await Assert.ThrowsAnyAsync(() => client.GetAsync(InvalidUri)); Assert.Equal(TimeSpan.FromSeconds(42), handler.ResponseDrainTimeout); Assert.Throws(() => handler.ResponseDrainTimeout = TimeSpan.FromSeconds(42)); } @@ -1343,13 +1343,13 @@ public void ConnectTimeout_ValidValues_Roundtrip(long ms) } [Fact] - public void ConnectTimeout_SetAfterUse_Throws() + public async Task ConnectTimeout_SetAfterUse_Throws() { using (var handler = new SocketsHttpHandler()) using (HttpClient client = CreateHttpClient(handler)) { handler.ConnectTimeout = TimeSpan.FromMilliseconds(int.MaxValue); - _ = client.GetAsync($"http://{Guid.NewGuid():N}"); // ignoring failure + await Assert.ThrowsAnyAsync(() => client.GetAsync(InvalidUri)); Assert.Equal(TimeSpan.FromMilliseconds(int.MaxValue), handler.ConnectTimeout); Assert.Throws(() => handler.ConnectTimeout = TimeSpan.FromMilliseconds(1)); } @@ -1390,13 +1390,13 @@ public void Expect100ContinueTimeout_ValidValues_Roundtrip(long ms) } [Fact] - public void Expect100ContinueTimeout_SetAfterUse_Throws() + public async Task Expect100ContinueTimeout_SetAfterUse_Throws() { using (var handler = new SocketsHttpHandler()) using (HttpClient client = CreateHttpClient(handler)) { handler.Expect100ContinueTimeout = TimeSpan.FromMilliseconds(int.MaxValue); - _ = client.GetAsync($"http://{Guid.NewGuid():N}"); // ignoring failure + await Assert.ThrowsAnyAsync(() => client.GetAsync(InvalidUri)); Assert.Equal(TimeSpan.FromMilliseconds(int.MaxValue), handler.Expect100ContinueTimeout); Assert.Throws(() => handler.Expect100ContinueTimeout = TimeSpan.FromMilliseconds(1)); } @@ -3880,14 +3880,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( }, async server => { - try - { - await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.AcceptConnectionSendResponseAndCloseAsync(content: "foo")); }, options: options); } @@ -3917,14 +3910,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync( }, async server => { - try - { - await server.AcceptConnectionSendResponseAndCloseAsync(content: "foo"); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.AcceptConnectionSendResponseAndCloseAsync(content: "foo")); }, options: options); } } @@ -4269,14 +4255,7 @@ await LoopbackServerFactory.CreateClientAndServerAsync(async uri => }, async server => { - try - { - await server.HandleRequestAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(server.HandleRequestAsync()); // On HTTP/1.x, an exception being thrown while sending the request content will result in the connection being closed. // This test is ensuring that a subsequent request can succeed on a new connection. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index 5916c82fb60b68..300ed02aff41ce 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -109,14 +109,7 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, Assert.Equal(exceptionMessage, innerException.Message); Assert.Equal("SocksException", innerException.GetType().Name); - try - { - await proxy.DisposeAsync(); - } - catch (Exception ex) - { - _output.WriteLine($"Ignored exception:{Environment.NewLine}{ex}"); - } + await IgnoreExceptions(proxy.DisposeAsync().AsTask()); } } From 8a99adad39fdf64df08993d9d5eefb1dc2168247 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 5 Jul 2024 13:41:37 +0200 Subject: [PATCH 26/72] JIT: Disable old promotion for structs with significant padding (#104438) When old promotion kicks in for structs with significant padding it generally results in dependent promotion as block morphing handles these conservatively. Disable this case and allow physical promotion to handle it instead. --- src/coreclr/jit/compiler.h | 8 -------- src/coreclr/jit/lclvars.cpp | 33 ++++++++++++++------------------- src/coreclr/jit/liveness.cpp | 2 +- src/coreclr/jit/morph.cpp | 11 +++++------ src/coreclr/jit/morphblock.cpp | 22 ---------------------- 5 files changed, 20 insertions(+), 56 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index fb5950694f5fd1..f2ea5ec78cab05 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -605,12 +605,6 @@ class LclVarDsc unsigned char lvIsStructField : 1; // Is this local var a field of a promoted struct local? unsigned char lvContainsHoles : 1; // Is this a promoted struct whose fields do not cover the struct local? - // True for a promoted struct that has significant padding in it. - // Significant padding is any data in the struct that is not covered by a - // promoted field and that the EE told us we need to preserve on block - // copies/inits. - unsigned char lvAnySignificantPadding : 1; - unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call @@ -4210,7 +4204,6 @@ class Compiler CORINFO_CLASS_HANDLE typeHnd; bool canPromote; bool containsHoles; - bool anySignificantPadding; bool fieldsSorted; unsigned char fieldCnt; lvaStructFieldInfo fields[MAX_NumOfFieldsInPromotableStruct]; @@ -4219,7 +4212,6 @@ class Compiler : typeHnd(typeHnd) , canPromote(false) , containsHoles(false) - , anySignificantPadding(false) , fieldsSorted(false) , fieldCnt(0) { diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 52623e920b5fa1..87f67e1e330c69 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2371,9 +2371,15 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE if (fieldsSize != treeNodes[0].size) { structPromotionInfo.containsHoles = true; - } - structPromotionInfo.anySignificantPadding = treeNodes[0].hasSignificantPadding && structPromotionInfo.containsHoles; + if (treeNodes[0].hasSignificantPadding) + { + // Struct has significant data not covered by fields we would promote; + // this would typically result in dependent promotion, so leave this + // struct to physical promotion. + return false; + } + } // Cool, this struct is promotable. @@ -2716,11 +2722,6 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) structPromotionInfo.fieldCnt, varDsc->lvFieldAccessed); shouldPromote = false; } - else if (varDsc->lvIsMultiRegRet && structPromotionInfo.anySignificantPadding) - { - JITDUMP("Not promoting multi-reg returned struct local V%02u with significant padding.\n", lclNum); - shouldPromote = false; - } #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) else if ((structPromotionInfo.fieldCnt == 2) && (varTypeIsFloating(structPromotionInfo.fields[0].fldType) || varTypeIsFloating(structPromotionInfo.fields[1].fldType))) @@ -2739,13 +2740,8 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) // multiple registers? if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs)) { - if (structPromotionInfo.anySignificantPadding) - { - JITDUMP("Not promoting multi-reg struct local V%02u with significant padding.\n", lclNum); - shouldPromote = false; - } - else if ((structPromotionInfo.fieldCnt != 2) && - !((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType))) + if ((structPromotionInfo.fieldCnt != 2) && + !((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType))) { JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true, #fields != 2 and it's " "not a single SIMD.\n", @@ -2834,11 +2830,10 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) assert(varDsc->GetLayout()->GetClassHandle() == structPromotionInfo.typeHnd); assert(structPromotionInfo.canPromote); - varDsc->lvFieldCnt = structPromotionInfo.fieldCnt; - varDsc->lvFieldLclStart = compiler->lvaCount; - varDsc->lvPromoted = true; - varDsc->lvContainsHoles = structPromotionInfo.containsHoles; - varDsc->lvAnySignificantPadding = structPromotionInfo.anySignificantPadding; + varDsc->lvFieldCnt = structPromotionInfo.fieldCnt; + varDsc->lvFieldLclStart = compiler->lvaCount; + varDsc->lvPromoted = true; + varDsc->lvContainsHoles = structPromotionInfo.containsHoles; #ifdef DEBUG // Don't stress this in LCL_FLD stress. diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index a9f566bd37bef8..575b85813ee8fa 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1024,7 +1024,7 @@ bool Compiler::fgComputeLifeUntrackedLocal(VARSET_TP& life, { // Do not consider this store dead if the parent local variable is an address exposed local or // if the struct has any significant padding we must retain the value of. - return !varDsc.IsAddressExposed() && !varDsc.lvAnySignificantPadding; + return !varDsc.IsAddressExposed(); } return false; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1805435c5ad3dc..6798832d1f8780 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14728,12 +14728,11 @@ PhaseStatus Compiler::fgRetypeImplicitByRefArgs() } // Copy the struct promotion annotations to the new temp. - LclVarDsc* newVarDsc = lvaGetDesc(newLclNum); - newVarDsc->lvPromoted = true; - newVarDsc->lvFieldLclStart = varDsc->lvFieldLclStart; - newVarDsc->lvFieldCnt = varDsc->lvFieldCnt; - newVarDsc->lvContainsHoles = varDsc->lvContainsHoles; - newVarDsc->lvAnySignificantPadding = varDsc->lvAnySignificantPadding; + LclVarDsc* newVarDsc = lvaGetDesc(newLclNum); + newVarDsc->lvPromoted = true; + newVarDsc->lvFieldLclStart = varDsc->lvFieldLclStart; + newVarDsc->lvFieldCnt = varDsc->lvFieldCnt; + newVarDsc->lvContainsHoles = varDsc->lvContainsHoles; #ifdef DEBUG newVarDsc->lvKeepType = true; #endif // DEBUG diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index 0f49e62c647f87..f21cc2355a4861 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -359,12 +359,6 @@ void MorphInitBlockHelper::TryInitFieldByField() return; } - if (destLclVar->lvAnySignificantPadding) - { - JITDUMP(" dest has significant padding.\n"); - return; - } - if (m_dstLclOffset != 0) { JITDUMP(" dest not at a zero offset.\n"); @@ -775,22 +769,6 @@ void MorphCopyBlockHelper::MorphStructCases() requiresCopyBlock = true; } - // Can we use field by field copy for the dest? - if (m_dstDoFldStore && m_dstVarDsc->lvAnySignificantPadding) - { - JITDUMP(" dest has significant padding"); - // C++ style CopyBlock with holes - requiresCopyBlock = true; - } - - // Can we use field by field copy for the src? - if (m_srcDoFldStore && m_srcVarDsc->lvAnySignificantPadding) - { - JITDUMP(" src has significant padding"); - // C++ style CopyBlock with holes - requiresCopyBlock = true; - } - #if defined(TARGET_ARM) if (m_store->OperIsIndir() && m_store->AsIndir()->IsUnaligned()) { From 41d854fd44dd24cec03bbed009c5e876b05e61d2 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 5 Jul 2024 13:41:59 +0200 Subject: [PATCH 27/72] JIT: Skip old promotion for retbuf defined locals (#104439) These locals end up being dependently promoted. Skip them and allow physical promotion to handle them instead. --- src/coreclr/jit/fginline.cpp | 13 +++++++++++++ src/coreclr/jit/importer.cpp | 6 ++++++ src/coreclr/jit/lclvars.cpp | 7 ++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 7a6246a988fda3..3d9e40e9fc22e4 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -387,6 +387,19 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorIsCall()) + { + CallArg* retBuffer = (*use)->AsCall()->gtArgs.GetRetBufferArg(); + if ((retBuffer != nullptr) && retBuffer->GetNode()->OperIs(GT_LCL_ADDR)) + { + m_compiler->lvaSetVarDoNotEnregister(retBuffer->GetNode()->AsLclVarCommon()->GetLclNum() + DEBUGARG(DoNotEnregisterReason::HiddenBufferStructArg)); + } + } + #if FEATURE_MULTIREG_RET // If an inline was rejected and the call returns a struct, we may // have deferred some work when importing call for cases where the diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 448ee995579af9..a84f3db3ff473d 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -851,6 +851,12 @@ GenTree* Compiler::impStoreStruct(GenTree* store, GenTree* destAddr = impGetNodeAddr(store, CHECK_SPILL_ALL, &indirFlags); NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); + if (destAddr->OperIs(GT_LCL_ADDR)) + { + lvaSetVarDoNotEnregister(destAddr->AsLclVarCommon()->GetLclNum() + DEBUGARG(DoNotEnregisterReason::HiddenBufferStructArg)); + } + #if !defined(TARGET_ARM) // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter if ((TargetOS::IsWindows || compUnixX86Abi()) && srcCall->IsUnmanaged()) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 87f67e1e330c69..74c8fd1add0ff0 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2570,9 +2570,11 @@ bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum) return false; } - if (varDsc->IsAddressExposed()) + if (varDsc->lvDoNotEnregister) { - JITDUMP(" struct promotion of V%02u is disabled because it has already been marked address exposed\n", lclNum); + // Promoting structs that are marked DNER will result in dependent + // promotion. Allow physical promotion to handle these. + JITDUMP(" struct promotion of V%02u is disabled because it has already been marked DNER\n", lclNum); return false; } @@ -3173,7 +3175,6 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister break; case DoNotEnregisterReason::HiddenBufferStructArg: JITDUMP("it is hidden buffer struct arg\n"); - assert(varDsc->IsHiddenBufferStructArg()); break; case DoNotEnregisterReason::DontEnregStructs: JITDUMP("struct enregistration is disabled\n"); From 8bcbe18072866a367d2041fe521c94766a4a9401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Fri, 5 Jul 2024 12:51:43 +0100 Subject: [PATCH 28/72] Unify debug views of immutable dictionaries (#100745) * Unify debug views of immutable dictionaries Fixes #94289 - Updates the way the debugger displays the remaining dictionaries (Frozen, Immutable, ImmutableSorted, Concurrent) to present their keys and values in separate columns. - Fixes debugger views of Builder classes of immutable collections. Previous custom implementations incorrectly treated the Builder classes as immutable. * Fixed tests of debugger attributes with immutable and concurrent generic dictionaries * Removed tests superseded by DebugView.Tests. * Fixed DebugView.Tests of cuncurrent and immutable generic dictionaries which failed on .Net Framework * Fix ns2.0 build. --------- Co-authored-by: Eirik Tsarpalis --- .../System/Collections/DebugView.Tests.cs | 62 +++++++++++++++++-- .../System/Diagnostics/DebuggerAttributes.cs | 1 - .../src/System.Collections.Concurrent.csproj | 2 + .../Concurrent/ConcurrentDictionary.cs | 11 +++- .../ConcurrentDictionaryTests.cs | 21 ------- .../src/System.Collections.Immutable.csproj | 4 +- .../ImmutableDictionary_2.Builder.cs | 34 +--------- .../ImmutableEnumerableDebuggerProxy.cs | 28 ++++++++- .../ImmutableSortedDictionary_2.Builder.cs | 33 +--------- .../tests/ImmutableDictionaryBuilderTest.cs | 21 ------- .../tests/ImmutableDictionaryTest.cs | 22 ------- .../ImmutableSortedDictionaryBuilderTest.cs | 21 ------- .../tests/ImmutableSortedDictionaryTest.cs | 22 ------- .../Generic/IDictionaryDebugView.cs | 12 +--- 14 files changed, 99 insertions(+), 195 deletions(-) diff --git a/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs b/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs index a6479be6e292d4..07b447c4c4de0f 100644 --- a/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs @@ -1,7 +1,10 @@ // 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.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; @@ -47,8 +50,8 @@ private static IEnumerable TestDebuggerAttributes_GenericDictionaries( new ("[\"Two\"]", "2"), } }; - CustomKeyedCollection collection = new (); - collection.GetKeyForItemHandler = value => (2 * value).ToString(); + CustomKeyedCollection collection = new(); + collection.GetKeyForItemHandler = value => (2 * value).ToString(); collection.InsertItem(0, 1); collection.InsertItem(1, 3); yield return new object[] { collection, @@ -58,6 +61,53 @@ private static IEnumerable TestDebuggerAttributes_GenericDictionaries( new ("[\"6\"]", "3"), } }; + + yield return new object[] { new ConcurrentDictionary(new KeyValuePair[] { new(1, "One"), new(2, "Two") }), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + } + + private static IEnumerable TestDebuggerAttributes_AdditionalGenericDictionaries() + { + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToFrozenDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableDictionary().ToBuilder(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableSortedDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableSortedDictionary().ToBuilder(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; } private static IEnumerable TestDebuggerAttributes_NonGenericDictionaries() @@ -162,12 +212,14 @@ private static IEnumerable TestDebuggerAttributes_ListInputs() public static IEnumerable TestDebuggerAttributes_InputsPresentedAsDictionary() { + var testCases = TestDebuggerAttributes_NonGenericDictionaries() + .Concat(TestDebuggerAttributes_AdditionalGenericDictionaries()); #if !NETFRAMEWORK - return TestDebuggerAttributes_NonGenericDictionaries() + return testCases .Concat(TestDebuggerAttributes_GenericDictionaries()); #else - // In .Net Framework only non-generic dictionaries are displayed in a dictionary format by the debugger. - return TestDebuggerAttributes_NonGenericDictionaries(); + // In .Net Framework, the generic dictionaries that are part of the framework are displayed in a list format by the debugger. + return testCases; #endif } diff --git a/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs b/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs index 0912d647d5e425..07b212f8a24ec7 100644 --- a/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs +++ b/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Data; using System.Linq; using System.Reflection; using System.Text; diff --git a/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj b/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj index 0dd332a6faead0..90b815473b8e8c 100644 --- a/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj +++ b/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj @@ -21,6 +21,8 @@ Link="System\Collections\HashHelpers.cs" /> + diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs index 7f63ffb0f8b209..5748725a154a31 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs @@ -2733,12 +2733,17 @@ public IDictionaryDebugView(IDictionary dictionary) } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Items + public DebugViewDictionaryItem[] Items { get { - var items = new KeyValuePair[_dictionary.Count]; - _dictionary.CopyTo(items, 0); + var keyValuePairs = new KeyValuePair[_dictionary.Count]; + _dictionary.CopyTo(keyValuePairs, 0); + var items = new DebugViewDictionaryItem[keyValuePairs.Length]; + for (int i = 0; i < items.Length; i++) + { + items[i] = new DebugViewDictionaryItem(keyValuePairs[i]); + } return items; } } diff --git a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs index ce05af556da9ca..c92689a6b034f4 100644 --- a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs +++ b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs @@ -629,27 +629,6 @@ public static void TestConstructor_ConcurrencyLevel(int concurrencyLevel) Assert.Equal(2, dictionary.Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(new ConcurrentDictionary()); - ConcurrentDictionary dict = new ConcurrentDictionary(); - dict.TryAdd("One", 1); - dict.TryAdd("Two", 2); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(new ConcurrentDictionary()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public static void TestNullComparer() { diff --git a/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj b/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj index 8f53404e87df36..47c5898a66c102 100644 --- a/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj +++ b/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj @@ -12,11 +12,11 @@ The System.Collections.Immutable library is built-in as part of the shared frame - - + + diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index fac2b6163c13ba..0476b66430d24e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -27,7 +27,7 @@ public sealed partial class ImmutableDictionary /// /// [DebuggerDisplay("Count = {Count}")] - [DebuggerTypeProxy(typeof(ImmutableDictionaryBuilderDebuggerProxy<,>))] + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] public sealed class Builder : IDictionary, IReadOnlyDictionary, IDictionary { /// @@ -709,36 +709,4 @@ private bool Apply(MutationResult result) } } } - - /// - /// A simple view of the immutable collection that the debugger can show to the developer. - /// - internal sealed class ImmutableDictionaryBuilderDebuggerProxy where TKey : notnull - { - /// - /// The collection to be enumerated. - /// - private readonly ImmutableDictionary.Builder _map; - - /// - /// The simple view of the collection. - /// - private KeyValuePair[]? _contents; - - /// - /// Initializes a new instance of the class. - /// - /// The collection to display in the debugger - public ImmutableDictionaryBuilderDebuggerProxy(ImmutableDictionary.Builder map) - { - Requires.NotNull(map, nameof(map)); - _map = map; - } - - /// - /// Gets a simple debugger-viewable collection. - /// - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Contents => _contents ??= _map.ToArray(_map.Count); - } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs index 3df4cd8e08b4ad..1522e0a8f05415 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs @@ -12,16 +12,38 @@ namespace System.Collections.Immutable /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - internal sealed class ImmutableDictionaryDebuggerProxy : ImmutableEnumerableDebuggerProxy> where TKey : notnull + /// + /// This class should only be used with immutable dictionaries, since it + /// caches the dictionary into an array for display in the debugger. + /// + internal sealed class ImmutableDictionaryDebuggerProxy where TKey : notnull { + /// + /// The dictionary to show to the debugger. + /// + private readonly IReadOnlyDictionary _dictionary; + + /// + /// The contents of the dictionary, cached into an array. + /// + private DebugViewDictionaryItem[]? _cachedContents; + /// /// Initializes a new instance of the class. /// - /// The enumerable to show in the debugger. + /// The dictionary to show in the debugger. public ImmutableDictionaryDebuggerProxy(IReadOnlyDictionary dictionary) - : base(enumerable: dictionary) { + Requires.NotNull(dictionary, nameof(dictionary)); + _dictionary = dictionary; } + + /// + /// Gets the contents of the dictionary for display in the debugger. + /// + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public DebugViewDictionaryItem[] Contents => _cachedContents + ??= _dictionary.Select(kv => new DebugViewDictionaryItem(kv)).ToArray(_dictionary.Count); } /// diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs index ce8e4a0f17996e..ff5fe9ae030587 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs @@ -25,7 +25,7 @@ public sealed partial class ImmutableSortedDictionary where TKey : /// /// [DebuggerDisplay("Count = {Count}")] - [DebuggerTypeProxy(typeof(ImmutableSortedDictionaryBuilderDebuggerProxy<,>))] + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] public sealed class Builder : IDictionary, IReadOnlyDictionary, IDictionary { /// @@ -645,35 +645,4 @@ public ImmutableSortedDictionary ToImmutable() #endregion } } - /// - /// A simple view of the immutable collection that the debugger can show to the developer. - /// - internal sealed class ImmutableSortedDictionaryBuilderDebuggerProxy where TKey : notnull - { - /// - /// The collection to be enumerated. - /// - private readonly ImmutableSortedDictionary.Builder _map; - - /// - /// The simple view of the collection. - /// - private KeyValuePair[]? _contents; - - /// - /// Initializes a new instance of the class. - /// - /// The collection to display in the debugger - public ImmutableSortedDictionaryBuilderDebuggerProxy(ImmutableSortedDictionary.Builder map) - { - Requires.NotNull(map, nameof(map)); - _map = map; - } - - /// - /// Gets a simple debugger-viewable collection. - /// - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Contents => _contents ??= _map.ToArray(_map.Count); - } } diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs index 60206a7238b821..82084ea9fbfadb 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs @@ -255,27 +255,6 @@ public void GetValueOrDefaultOfConcreteType() Assert.Equal(5, populated.GetValueOrDefault("a", 1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableDictionary.CreateBuilder()); - ImmutableDictionary.Builder builder = ImmutableDictionary.CreateBuilder(); - builder.Add(1, "One"); - builder.Add(2, "Two"); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(builder); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(builder, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableHashSet.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ToImmutableDictionary() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs index 586f4555c3d8b3..63dc7042088721 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs @@ -349,28 +349,6 @@ public void EnumeratorRecyclingMisuse() enumerator.Dispose(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableDictionary.Create()); - ImmutableDictionary dict = ImmutableDictionary.Create().Add("One", 1).Add("Two", 2); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - - object rootNode = DebuggerAttributes.GetFieldValue(ImmutableDictionary.Create(), "_root"); - DebuggerAttributes.ValidateDebuggerDisplayReferences(rootNode); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableHashSet.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void Clear_NoComparer_ReturnsEmptyWithoutComparer() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs index 6d360a4a343951..feb6a75e7233ad 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs @@ -255,27 +255,6 @@ public void GetValueOrDefaultOfConcreteType() Assert.Equal(5, populated.GetValueOrDefault("a", 1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableSortedDictionary.CreateBuilder()); - ImmutableSortedDictionary.Builder builder = ImmutableSortedDictionary.CreateBuilder(); - builder.Add(1, "One"); - builder.Add(2, "Two"); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(builder); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(builder, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableSortedDictionary.CreateBuilder()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ValueRef() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs index 492d319e2c3db0..47725fdb2ac991 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs @@ -402,28 +402,6 @@ public void Remove_EmptyDictionary_DoesNothing() Assert.Equal(0, dictionary.Remove(2).Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableSortedDictionary.Create()); - ImmutableSortedDictionary dict = ImmutableSortedDictionary.Create().Add(1, 2).Add(2, 3).Add(3, 4); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - - object rootNode = DebuggerAttributes.GetFieldValue(ImmutableSortedDictionary.Create(), "_root"); - DebuggerAttributes.ValidateDebuggerDisplayReferences(rootNode); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableSortedDictionary.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ValueRef() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs index c082d31a09ff8b..8ec7e86bcd0c68 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs @@ -11,9 +11,7 @@ internal sealed class IDictionaryDebugView where TKey : notnull public IDictionaryDebugView(IDictionary dictionary) { - ArgumentNullException.ThrowIfNull(dictionary); - - _dict = dictionary; + _dict = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -39,9 +37,7 @@ internal sealed class DictionaryKeyCollectionDebugView public DictionaryKeyCollectionDebugView(ICollection collection) { - ArgumentNullException.ThrowIfNull(collection); - - _collection = collection; + _collection = collection ?? throw new ArgumentNullException(nameof(collection)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -62,9 +58,7 @@ internal sealed class DictionaryValueCollectionDebugView public DictionaryValueCollectionDebugView(ICollection collection) { - ArgumentNullException.ThrowIfNull(collection); - - _collection = collection; + _collection = collection ?? throw new ArgumentNullException(nameof(collection)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] From c52177b21cb358d45a18a1f14d5855aedb61d0ec Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:55:05 +0100 Subject: [PATCH 29/72] Remove `virtual` modifier from `GetDatePart` (#104456) * Remove `virtual` modifier from `GetDatePart` * Update HebrewCalendar.cs --------- Co-authored-by: Stephen Toub --- .../src/System/Globalization/HebrewCalendar.cs | 2 +- .../src/System/Globalization/HijriCalendar.cs | 2 +- .../src/System/Globalization/JulianCalendar.cs | 2 +- .../src/System/Globalization/PersianCalendar.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs index 53dde1baa37dfd..95fec883d12a71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs @@ -432,7 +432,7 @@ internal static int GetLunarMonthDay(int gregorianYear, DateBuffer lunarDate) /// Returns a given date part of this DateTime. This method is used /// to compute the year, day-of-year, month, or day part. /// - internal virtual int GetDatePart(long ticks, int part) + private static int GetDatePart(long ticks, int part) { // The Gregorian year, month, day value for ticks. int hebrewYearType; // lunar year type diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.cs index 6f8b39be18dc22..1fa6e4b4feae9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/HijriCalendar.cs @@ -192,7 +192,7 @@ internal static void CheckYearMonthRange(int year, int month, int era) /// In order to get the exact Hijri year, we compare the exact absolute date for HijriYear and (HijriYear + 1). /// From here, we can get the correct Hijri year. /// - internal virtual int GetDatePart(long ticks, int part) + private int GetDatePart(long ticks, int part) { CheckTicksRange(ticks); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/JulianCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/JulianCalendar.cs index 0d14372214b78d..97d4875a4a4b44 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/JulianCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/JulianCalendar.cs @@ -111,7 +111,7 @@ internal static void CheckDayRange(int year, int month, int day) /// Returns a given date part of this DateTime. This method is used /// to compute the year, day-of-year, month, or day part. /// - internal static int GetDatePart(long ticks, int part) + private static int GetDatePart(long ticks, int part) { // Gregorian 1/1/0001 is Julian 1/3/0001. Remember DateTime(0) is referred to Gregorian 1/1/0001. // The following line convert Gregorian ticks to Julian ticks. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/PersianCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/PersianCalendar.cs index 89b0e6a4c8091e..cc21367542fab1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/PersianCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/PersianCalendar.cs @@ -141,7 +141,7 @@ private static int DaysInPreviousMonths(int month) return DaysToMonth[month]; } - internal int GetDatePart(long ticks, int part) + private int GetDatePart(long ticks, int part) { CheckTicksRange(ticks); From ebf21a425374edfdd69534db69a98d6d5cfcda96 Mon Sep 17 00:00:00 2001 From: AlexandreEXFO <154447827+AlexandreEXFO@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:01:41 -0400 Subject: [PATCH 30/72] Fix typo in RSASignaturePaddingMode.Pss apidoc --- .../src/System/Security/Cryptography/RSASignaturePaddingMode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs index a8a37bee458246..16dec397bd5155 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs @@ -21,7 +21,7 @@ public enum RSASignaturePaddingMode /// Probabilistic Signature Scheme. /// /// - /// This corresponds to the RSASSA-PKCS1-v1.5 signature scheme of the PKCS #1 RSA Encryption Standard. + /// This corresponds to the RRSASSA-PSS signature scheme of the PKCS #1 RSA Encryption Standard. /// It is recommended for new applications. /// Pss, From 06e00763dfff79f2b3ca532d3b0c4003986f4107 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 5 Jul 2024 12:10:43 -0400 Subject: [PATCH 31/72] Add RetryHelper to AIA test All other AIA tests use RetryHelper, but this one did not. So let's add it to this one, too. --- .../RevocationTests/AiaTests.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs index bd4ee0727a412a..d13255409a8308 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/RevocationTests/AiaTests.cs @@ -76,16 +76,18 @@ public static void AiaAcceptsCertTypesAndIgnoresNonCertTypes(AiaResponseKind aia { responder.AiaResponseKind = aiaResponseKind; - using (ChainHolder holder = new ChainHolder()) - { - X509Chain chain = holder.Chain; - chain.ChainPolicy.CustomTrustStore.Add(rootCert); - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); - chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; + RetryHelper.Execute(() => { + using (ChainHolder holder = new ChainHolder()) + { + X509Chain chain = holder.Chain; + chain.ChainPolicy.CustomTrustStore.Add(rootCert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chain.ChainPolicy.VerificationTime = endEntity.NotBefore.AddMinutes(1); + chain.ChainPolicy.UrlRetrievalTimeout = DynamicRevocationTests.s_urlRetrievalLimit; - Assert.NotEqual(mustIgnore, chain.Build(endEntity)); - } + Assert.NotEqual(mustIgnore, chain.Build(endEntity)); + } + }); } } From b9673cb8c3420ea97df42453b151bdc58c02aced Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 5 Jul 2024 10:09:16 -0700 Subject: [PATCH 32/72] Lower GetElement on arm64 to the correct access sequence (#104288) * Lower GetElement on arm64 to the correct access sequence * Use constant offset where possible * Ensure that lvaSIMDInitTempVarNum is marked as being used by LclAddrNode * Fix assert * Create a valid addr mode for Arm64 * Don't lower unnecessarily * Account for index 0 and scale 1 * Remove the offset constant node when it's unused --- src/coreclr/jit/hwintrinsiccodegenarm64.cpp | 105 ++------------ src/coreclr/jit/lowerarmarch.cpp | 143 +++++++++++++++++--- src/coreclr/jit/lsraarm64.cpp | 23 +--- 3 files changed, 138 insertions(+), 133 deletions(-) diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index 342620742d0ba3..706329ca038831 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -1654,6 +1654,10 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Vector128_GetElement: { assert(intrin.numOperands == 2); + assert(!intrin.op1->isContained()); + + assert(intrin.op2->OperIsConst()); + assert(intrin.op2->isContained()); var_types simdType = Compiler::getSIMDTypeForSize(node->GetSimdSize()); @@ -1663,109 +1667,22 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) simdType = TYP_SIMD16; } - if (!intrin.op2->OperIsConst()) - { - assert(!intrin.op2->isContained()); - - emitAttr baseTypeSize = emitTypeSize(intrin.baseType); - unsigned baseTypeScale = genLog2(EA_SIZE_IN_BYTES(baseTypeSize)); - - regNumber baseReg; - regNumber indexReg = op2Reg; - - // Optimize the case of op1 is in memory and trying to access i'th element. - if (!intrin.op1->isUsedFromReg()) - { - assert(intrin.op1->isContained()); - - if (intrin.op1->OperIsLocal()) - { - unsigned varNum = intrin.op1->AsLclVarCommon()->GetLclNum(); - baseReg = internalRegisters.Extract(node); - - // Load the address of varNum - GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, baseReg, varNum, 0); - } - else - { - // Require GT_IND addr to be not contained. - assert(intrin.op1->OperIs(GT_IND)); - - GenTree* addr = intrin.op1->AsIndir()->Addr(); - assert(!addr->isContained()); - baseReg = addr->GetRegNum(); - } - } - else - { - unsigned simdInitTempVarNum = compiler->lvaSIMDInitTempVarNum; - noway_assert(simdInitTempVarNum != BAD_VAR_NUM); - - baseReg = internalRegisters.Extract(node); - - // Load the address of simdInitTempVarNum - GetEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, baseReg, simdInitTempVarNum, 0); - - // Store the vector to simdInitTempVarNum - GetEmitter()->emitIns_R_R(INS_str, emitTypeSize(simdType), op1Reg, baseReg); - } - - assert(genIsValidIntReg(indexReg)); - assert(genIsValidIntReg(baseReg)); - assert(baseReg != indexReg); + ssize_t ival = intrin.op2->AsIntCon()->IconValue(); - // Load item at baseReg[index] - GetEmitter()->emitIns_R_R_R_Ext(ins_Load(intrin.baseType), baseTypeSize, targetReg, baseReg, - indexReg, INS_OPTS_LSL, baseTypeScale); - } - else if (!GetEmitter()->isValidVectorIndex(emitTypeSize(simdType), emitTypeSize(intrin.baseType), - intrin.op2->AsIntCon()->IconValue())) + if (!GetEmitter()->isValidVectorIndex(emitTypeSize(simdType), emitTypeSize(intrin.baseType), ival)) { // We only need to generate code for the get if the index is valid // If the index is invalid, previously generated for the range check will throw + break; } - else if (!intrin.op1->isUsedFromReg()) - { - assert(intrin.op1->isContained()); - assert(intrin.op2->IsCnsIntOrI()); - - int offset = (int)intrin.op2->AsIntCon()->IconValue() * genTypeSize(intrin.baseType); - instruction ins = ins_Load(intrin.baseType); - - assert(!intrin.op1->isUsedFromReg()); - - if (intrin.op1->OperIsLocal()) - { - unsigned varNum = intrin.op1->AsLclVarCommon()->GetLclNum(); - GetEmitter()->emitIns_R_S(ins, emitActualTypeSize(intrin.baseType), targetReg, varNum, offset); - } - else - { - assert(intrin.op1->OperIs(GT_IND)); - - GenTree* addr = intrin.op1->AsIndir()->Addr(); - assert(!addr->isContained()); - regNumber baseReg = addr->GetRegNum(); - // ldr targetReg, [baseReg, #offset] - GetEmitter()->emitIns_R_R_I(ins, emitActualTypeSize(intrin.baseType), targetReg, baseReg, - offset); - } - } - else + if ((varTypeIsFloating(intrin.baseType) && (targetReg == op1Reg) && (ival == 0))) { - assert(intrin.op2->IsCnsIntOrI()); - ssize_t indexValue = intrin.op2->AsIntCon()->IconValue(); - // no-op if vector is float/double, targetReg == op1Reg and fetching for 0th index. - if ((varTypeIsFloating(intrin.baseType) && (targetReg == op1Reg) && (indexValue == 0))) - { - break; - } - - GetEmitter()->emitIns_R_R_I(ins, emitTypeSize(intrin.baseType), targetReg, op1Reg, indexValue, - INS_OPTS_NONE); + break; } + + GetEmitter()->emitIns_R_R_I(ins, emitTypeSize(intrin.baseType), targetReg, op1Reg, ival, INS_OPTS_NONE); break; } diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index b739aeedab740a..6d99207ac875ab 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1254,6 +1254,128 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) return LowerHWIntrinsicDot(node); } + case NI_Vector64_GetElement: + case NI_Vector128_GetElement: + { + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + + bool isContainableMemory = IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1); + + if (isContainableMemory || !op2->OperIsConst()) + { + unsigned simdSize = node->GetSimdSize(); + var_types simdBaseType = node->GetSimdBaseType(); + var_types simdType = Compiler::getSIMDTypeForSize(simdSize); + + // We're either already loading from memory or we need to since + // we don't know what actual index is going to be retrieved. + + unsigned lclNum = BAD_VAR_NUM; + unsigned lclOffs = 0; + + if (!isContainableMemory) + { + // We aren't already in memory, so we need to spill there + + comp->getSIMDInitTempVarNum(simdType); + lclNum = comp->lvaSIMDInitTempVarNum; + + GenTree* storeLclVar = comp->gtNewStoreLclVarNode(lclNum, op1); + BlockRange().InsertBefore(node, storeLclVar); + LowerNode(storeLclVar); + } + else if (op1->IsLocal()) + { + // We're an existing local that is loaded from memory + GenTreeLclVarCommon* lclVar = op1->AsLclVarCommon(); + + lclNum = lclVar->GetLclNum(); + lclOffs = lclVar->GetLclOffs(); + + BlockRange().Remove(op1); + } + + if (lclNum != BAD_VAR_NUM) + { + // We need to get the address of the local + op1 = comp->gtNewLclAddrNode(lclNum, lclOffs, TYP_BYREF); + BlockRange().InsertBefore(node, op1); + LowerNode(op1); + } + else + { + assert(op1->isIndir()); + + // We need to get the underlying address + GenTree* addr = op1->AsIndir()->Addr(); + BlockRange().Remove(op1); + op1 = addr; + } + + GenTree* offset = op2; + unsigned baseTypeSize = genTypeSize(simdBaseType); + + if (offset->OperIsConst()) + { + // We have a constant index, so scale it up directly + GenTreeIntConCommon* index = offset->AsIntCon(); + index->SetIconValue(index->IconValue() * baseTypeSize); + } + else + { + // We have a non-constant index, so scale it up via mul but + // don't lower the GT_MUL node since the indir will try to + // create an addressing mode and will do folding itself. We + // do, however, skip the multiply for scale == 1 + + if (baseTypeSize != 1) + { + GenTreeIntConCommon* scale = comp->gtNewIconNode(baseTypeSize); + BlockRange().InsertBefore(node, scale); + + offset = comp->gtNewOperNode(GT_MUL, offset->TypeGet(), offset, scale); + BlockRange().InsertBefore(node, offset); + } + } + + // Add the offset, don't lower the GT_ADD node since the indir will + // try to create an addressing mode and will do folding itself. We + // do, however, skip the add for offset == 0 + GenTree* addr = op1; + + if (!offset->IsIntegralConst(0)) + { + addr = comp->gtNewOperNode(GT_ADD, addr->TypeGet(), addr, offset); + BlockRange().InsertBefore(node, addr); + } + else + { + BlockRange().Remove(offset); + } + + // Finally we can indirect the memory address to get the actual value + GenTreeIndir* indir = comp->gtNewIndir(simdBaseType, addr); + BlockRange().InsertBefore(node, indir); + + LIR::Use use; + if (BlockRange().TryGetUse(node, &use)) + { + use.ReplaceWith(indir); + } + else + { + indir->SetUnusedValue(); + } + + BlockRange().Remove(node); + return LowerNode(indir); + } + + assert(op2->OperIsConst()); + break; + } + case NI_Vector64_op_Equality: case NI_Vector128_op_Equality: { @@ -3318,24 +3440,11 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Vector64_GetElement: case NI_Vector128_GetElement: { - assert(varTypeIsIntegral(intrin.op2)); + assert(!IsContainableMemoryOp(intrin.op1) || !IsSafeToContainMem(node, intrin.op1)); + assert(intrin.op2->OperIsConst()); - if (intrin.op2->IsCnsIntOrI()) - { - MakeSrcContained(node, intrin.op2); - } - - // TODO: Codegen isn't currently handling this correctly - // - // if (IsContainableMemoryOp(intrin.op1) && IsSafeToContainMem(node, intrin.op1)) - // { - // MakeSrcContained(node, intrin.op1); - // - // if (intrin.op1->OperIs(GT_IND)) - // { - // intrin.op1->AsIndir()->Addr()->ClearContained(); - // } - // } + // Loading a constant index from register + MakeSrcContained(node, intrin.op2); break; } diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index c553edb9305ab7..8a5b20e4055052 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -1944,7 +1944,6 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou srcCount += BuildDelayFreeUses(intrin.op3, embOp2Node->Op(1)); } } - else if (intrin.op2 != nullptr) { // RMW intrinsic operands doesn't have to be delayFree when they can be assigned the same register as op1Reg @@ -1955,28 +1954,8 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou bool forceOp2DelayFree = false; SingleTypeRegSet lowVectorCandidates = RBM_NONE; size_t lowVectorOperandNum = 0; - if ((intrin.id == NI_Vector64_GetElement) || (intrin.id == NI_Vector128_GetElement)) - { - if (!intrin.op2->IsCnsIntOrI() && (!intrin.op1->isContained() || intrin.op1->OperIsLocal())) - { - // If the index is not a constant and the object is not contained or is a local - // we will need a general purpose register to calculate the address - // internal register must not clobber input index - // TODO-Cleanup: An internal register will never clobber a source; this code actually - // ensures that the index (op2) doesn't interfere with the target. - buildInternalIntRegisterDefForNode(intrinsicTree); - forceOp2DelayFree = true; - } - if (!intrin.op2->IsCnsIntOrI() && !intrin.op1->isContained()) - { - // If the index is not a constant or op1 is in register, - // we will use the SIMD temp location to store the vector. - var_types requiredSimdTempType = (intrin.id == NI_Vector64_GetElement) ? TYP_SIMD8 : TYP_SIMD16; - compiler->getSIMDInitTempVarNum(requiredSimdTempType); - } - } - else if (HWIntrinsicInfo::IsLowVectorOperation(intrin.id)) + if (HWIntrinsicInfo::IsLowVectorOperation(intrin.id)) { getLowVectorOperandAndCandidates(intrin, &lowVectorOperandNum, &lowVectorCandidates); } From 02bd1da6e3aa0c693f66460ed473130b20139a42 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 5 Jul 2024 13:37:34 -0400 Subject: [PATCH 33/72] Obsolete X509Certificate{2} constructors and X509Certificate2Collection.Import --- docs/project/list-of-diagnostics.md | 1 + .../Common/src/System/Obsoletions.cs | 3 ++ ...DirectoryServices.AccountManagement.csproj | 4 +++ .../AuthenticablePrincipal.cs | 3 +- ...stem.Net.Http.Json.Functional.Tests.csproj | 1 + ...ttp.WinHttpHandler.Functional.Tests.csproj | 1 + ....Net.Http.WinHttpHandler.Unit.Tests.csproj | 2 +- .../System.Net.Http.Functional.Tests.csproj | 1 + .../src/System/Net/Managed/HttpConnection.cs | 2 +- .../Windows/HttpListenerRequest.Windows.cs | 2 +- .../ListenerClientCertAsyncResult.Windows.cs | 2 +- ...NetworkInformation.Functional.Tests.csproj | 1 + .../QuicConnection.SslConnectionOptions.cs | 5 ++- .../System.Net.Quic.Functional.Tests.csproj | 1 + .../tests/System.Net.Requests.Tests.csproj | 2 +- .../Security/Pal.OSX/SafeDeleteSslContext.cs | 2 +- .../System.Net.Security.Tests.csproj | 1 + .../System.Net.Security.Unit.Tests.csproj | 2 +- .../tests/System.Net.WebClient.Tests.csproj | 2 +- .../System.Net.WebSockets.Client.Tests.csproj | 1 + .../Pal/AnyOS/ManagedPal.Decode.cs | 2 +- .../Pal/Windows/HelpersWindows.cs | 2 +- .../System.Security.Cryptography.Pkcs.csproj | 4 +++ .../Cryptography/Pkcs/Pkcs12CertBag.cs | 2 +- .../Security/Cryptography/Pkcs/SignedCms.cs | 8 +---- ...em.Security.Cryptography.Pkcs.Tests.csproj | 1 + .../System.Security.Cryptography.Xml.csproj | 4 +++ .../Cryptography/Xml/KeyInfoX509Data.cs | 14 ++++++-- ...tem.Security.Cryptography.Xml.Tests.csproj | 1 + .../ref/System.Security.Cryptography.cs | 35 ++++++++++++++++++- .../X509Certificates/CertificateRequest.cs | 2 +- .../OpenSslCertificateAssetDownloader.cs | 8 ++++- .../OpenSslDirectoryBasedStoreProvider.cs | 6 ++-- .../X509Certificates/X509Certificate.cs | 12 +++++++ .../X509Certificates/X509Certificate2.cs | 15 +++++++- .../X509Certificate2Collection.cs | 13 +++++-- .../System.Security.Cryptography.Tests.csproj | 4 +-- .../System.Security.Permissions.Tests.csproj | 4 +-- 38 files changed, 141 insertions(+), 35 deletions(-) diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md index fa109f57267978..928a53e0659ab7 100644 --- a/docs/project/list-of-diagnostics.md +++ b/docs/project/list-of-diagnostics.md @@ -110,6 +110,7 @@ The PR that reveals the implementation of the ` + + + + diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs index 10cb51c4828d02..10855d2d414b66 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthenticablePrincipal.cs @@ -551,7 +551,8 @@ private void LoadCertificateCollection(List certificatesToLoad) { try { - _certificates.Import(rawCert); + X509Certificate2 cert = X509CertificateLoader.LoadCertificate(rawCert); + _certificates.Add(cert); } catch (System.Security.Cryptography.CryptographicException) { diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj index bc58057edf7622..6cbef5664118ea 100644 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/System.Net.Http.Json.Functional.Tests.csproj @@ -3,6 +3,7 @@ $(NetCoreAppCurrent);$(NetFrameworkCurrent) true + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj index 25c0dcc9cfb7c7..89a7007b2f6f33 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csproj @@ -4,6 +4,7 @@ true $(DefineConstants);WINHTTPHANDLER_TEST true + $(NoWarn);SYSLIB0057 - $(NoWarn);0436 + $(NoWarn);0436;SYSLIB0057 true ../../src/Resources/Strings.resx $(NetCoreAppCurrent)-windows diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 5447195840f31f..e38b4780a34c59 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -9,6 +9,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-osx true true + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs index 10e75797cfd4ba..844209f787a0b3 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs @@ -91,7 +91,7 @@ public HttpConnection(Socket sock, HttpEndPointListener epl, bool secure, X509Ce return true; } - _clientCert = c as X509Certificate2 ?? new X509Certificate2(c.GetRawCertData()); + _clientCert = c as X509Certificate2 ?? X509CertificateLoader.LoadCertificate(c.GetRawCertData()); _clientCertErrors = new int[] { (int)e }; return true; }); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs index c1dca80c03d852..dcce980482ae01 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequest.Windows.cs @@ -477,7 +477,7 @@ private void GetClientCertificateCore() { byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); - ClientCertificate = new X509Certificate2(certEncoded); + ClientCertificate = X509CertificateLoader.LoadCertificate(certEncoded); } catch (CryptographicException exception) { diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerClientCertAsyncResult.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerClientCertAsyncResult.Windows.cs index 1f3c43351089d1..72db141777b726 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerClientCertAsyncResult.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/ListenerClientCertAsyncResult.Windows.cs @@ -123,7 +123,7 @@ private static unsafe void IOCompleted(ListenerClientCertAsyncResult asyncResult { byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); - result = httpListenerRequest.ClientCertificate = new X509Certificate2(certEncoded); + result = httpListenerRequest.ClientCertificate = X509CertificateLoader.LoadCertificate(certEncoded); } catch (CryptographicException exception) { diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj index afefb7a4fdeb67..a9608bd8793d38 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj @@ -6,6 +6,7 @@ true $(DefineConstants);NETWORKINFORMATION_TEST true + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs index 1b352f10045404..50600b09611688 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.SslConnectionOptions.cs @@ -129,7 +129,7 @@ internal async Task StartAsyncCertificateValidation(IntPtr certificatePtr, if (certData.Length > 0) { Debug.Assert(certificate == null); - certificate = new X509Certificate2(certData.Span); + certificate = X509CertificateLoader.LoadCertificate(certData.Span); } result = _connection._sslConnectionOptions.ValidateCertificate(certificate, certData.Span, chainData.Span); @@ -205,8 +205,11 @@ private QUIC_TLS_ALERT_CODES ValidateCertificate(X509Certificate2? certificate, if (chainData.Length > 0) { + Debug.Assert(X509Certificate2.GetCertContentType(chainData) is X509ContentType.Pkcs7); X509Certificate2Collection additionalCertificates = new X509Certificate2Collection(); +#pragma warning disable SYSLIB0057 additionalCertificates.Import(chainData); +#pragma warning restore SYSLIB0057 chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index c237c7d942a95c..78fb25cebb8bc0 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -5,6 +5,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-linux;$(NetCoreAppCurrent)-osx true ../../src/Resources/Strings.resx + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj index ea595a3dc09462..35e9a3f81011f0 100644 --- a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj +++ b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj @@ -6,7 +6,7 @@ $(DefineConstants);NETSTANDARD true - $(NoWarn);SYSLIB0014 + $(NoWarn);SYSLIB0014;SYSLIB0057 true true diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index efa5050171f31d..d2c81f51a25a13 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -384,7 +384,7 @@ internal static void SetCertificate(SafeSslHandle sslContext, SslStreamCertifica // The current value of intermediateCert is still in elements, which will // get Disposed at the end of this method. The new value will be // in the intermediate certs array, which also gets serially Disposed. - intermediateCert = new X509Certificate2(intermediateCert.RawDataMemory.Span); + intermediateCert = X509CertificateLoader.LoadCertificate(intermediateCert.RawDataMemory.Span); } ptrs[i + 1] = intermediateCert.Handle; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 5fffd852c7506b..da87847d317626 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -6,6 +6,7 @@ true true true + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj index 241461ac2817cf..261b61f16d653f 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj @@ -9,7 +9,7 @@ --> 436 - $(NoWarn);3021 + $(NoWarn);3021;SYSLIB0057 $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-android true diff --git a/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj b/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj index f37a74c688472f..6ce664d2838ebe 100644 --- a/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj +++ b/src/libraries/System.Net.WebClient/tests/System.Net.WebClient.Tests.csproj @@ -3,7 +3,7 @@ $(NetCoreAppCurrent) $(DefineConstants);NETSTANDARD - $(NoWarn);SYSLIB0014 + $(NoWarn);SYSLIB0014;SYSLIB0057 true diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index b321f77aa21f32..35780f9d7497c4 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -5,6 +5,7 @@ ../src/Resources/Strings.resx $(NetCoreAppCurrent);$(NetCoreAppCurrent)-browser $(DefineConstants);NETSTANDARD + $(NoWarn);SYSLIB0057 diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs index 6e934628ad7337..b9e73699a7e15f 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Decode.cs @@ -45,7 +45,7 @@ public override DecryptorPal Decode( { if (certChoice.Certificate != null) { - originatorCerts.Add(new X509Certificate2(certChoice.Certificate.Value.ToArray())); + originatorCerts.Add(X509CertificateLoader.LoadCertificate(certChoice.Certificate.Value.Span)); } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs index bf8c84f9fd3ff4..2ea444ed23690d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/HelpersWindows.cs @@ -131,7 +131,7 @@ public static X509Certificate2Collection GetOriginatorCerts(this SafeCryptMsgHan for (int index = 0; index < numCertificates; index++) { byte[] encodedCertificate = hCryptMsg.GetMsgParamAsByteArray(CryptMsgParamType.CMSG_CERT_PARAM, index); - X509Certificate2 cert = new X509Certificate2(encodedCertificate); + X509Certificate2 cert = X509CertificateLoader.LoadCertificate(encodedCertificate); certs.Add(cert); } return certs; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj index fb549df74f4b4c..d0c08ae01cd6cf 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System.Security.Cryptography.Pkcs.csproj @@ -663,6 +663,10 @@ System.Security.Cryptography.Pkcs.EnvelopedCms + + + + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs index 0c7eb16d2e9b06..dd6b037260bb2f 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs @@ -76,7 +76,7 @@ public X509Certificate2 GetCertificate() throw new InvalidOperationException(SR.Cryptography_Pkcs12_CertBagNotX509); } - return new X509Certificate2(PkcsHelpers.DecodeOctetString(_decoded.CertValue)); + return X509CertificateLoader.LoadCertificate(PkcsHelpers.DecodeOctetString(_decoded.CertValue)); } private static byte[] EncodeBagValue(Oid certificateType, ReadOnlyMemory encodedCertificate) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs index 51dccc6458d6d2..c959deede696c3 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/SignedCms.cs @@ -95,13 +95,7 @@ public X509Certificate2Collection Certificates { if (choice.Certificate.HasValue) { - coll.Add(new X509Certificate2(choice.Certificate.Value -#if NET - .Span -#else - .ToArray() -#endif - )); + coll.Add(X509CertificateLoader.LoadCertificate(choice.Certificate.Value.Span)); } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj b/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj index 5cbfda1f5e89fd..7accf609701ffc 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj @@ -2,6 +2,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);$(NetFrameworkCurrent) + $(NoWarn);SYSLIB0057 + + + + diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfoX509Data.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfoX509Data.cs index dd5fcd2af56a1b..3d88391ec779cf 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfoX509Data.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/KeyInfoX509Data.cs @@ -30,7 +30,15 @@ public KeyInfoX509Data() { } public KeyInfoX509Data(byte[] rgbCert) { - X509Certificate2 certificate = new X509Certificate2(rgbCert); + // Compat: this accepts null arrays for certificate data and would not throw. X509CertificateLoader throws + // for a null input. This uses the X509Certificate2 constructor for null inputs to preserve the existing + // behavior. Since the input is null and there is nothing to decode, the input is safe for the constructor. +#pragma warning disable SYSLIB0057 + X509Certificate2 certificate = rgbCert is null ? + new X509Certificate2((byte[])null!) : + X509CertificateLoader.LoadCertificate(rgbCert); +#pragma warning restore SYSLIB0057 + AddCertificate(certificate); } @@ -316,7 +324,9 @@ public override void LoadXml(XmlElement element) foreach (XmlNode node in x509CertificateNodes) { - AddCertificate(new X509Certificate2(Convert.FromBase64String(Utils.DiscardWhiteSpaces(node.InnerText)))); + AddCertificate( + X509CertificateLoader.LoadCertificate( + Convert.FromBase64String(Utils.DiscardWhiteSpaces(node.InnerText)))); } } } diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj b/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj index 56458c07a5a0f7..b8ad085cbadc1f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/tests/System.Security.Cryptography.Xml.Tests.csproj @@ -2,6 +2,7 @@ $(NetCoreAppCurrent);$(NetFrameworkMinimum) disable + $(NoWarn);SYSLIB0057 SerialNumberBytes { get { throw null; } } public string Subject { get { throw null; } } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static System.Security.Cryptography.X509Certificates.X509Certificate CreateFromCertFile(string filename) { throw null; } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static System.Security.Cryptography.X509Certificates.X509Certificate CreateFromSignedFile(string filename) { throw null; } public void Dispose() { } @@ -3159,41 +3171,54 @@ public partial class X509Certificate2 : System.Security.Cryptography.X509Certifi [System.ObsoleteAttribute("X509Certificate and X509Certificate2 are immutable. Use the appropriate constructor to create a new certificate.", DiagnosticId="SYSLIB0026", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2() { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(byte[] rawData) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(byte[] rawData, System.Security.SecureString? password) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(byte[] rawData, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(byte[] rawData, string? password) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(System.IntPtr handle) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(System.ReadOnlySpan rawData) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(System.ReadOnlySpan rawData, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected X509Certificate2(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(System.Security.Cryptography.X509Certificates.X509Certificate certificate) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName, System.Security.SecureString? password) { } [System.CLSCompliantAttribute(false)] + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName, System.Security.SecureString? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName, string? password) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public X509Certificate2(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) { } public bool Archived { get { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] set { } } @@ -3274,13 +3299,21 @@ public void AddRange(System.Security.Cryptography.X509Certificates.X509Certifica public string ExportPkcs7Pem() { throw null; } public System.Security.Cryptography.X509Certificates.X509Certificate2Collection Find(System.Security.Cryptography.X509Certificates.X509FindType findType, object findValue, bool validOnly) { throw null; } public new System.Security.Cryptography.X509Certificates.X509Certificate2Enumerator GetEnumerator() { throw null; } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(byte[] rawData) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(byte[] rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(System.ReadOnlySpan rawData) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(System.ReadOnlySpan rawData, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(System.ReadOnlySpan rawData, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(string fileName) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(string fileName, System.ReadOnlySpan password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } + [System.ObsoleteAttribute("Loading certificate data through the constructor or Import is obsolete. Use X509CertificateLoader instead to load certificates.", DiagnosticId="SYSLIB0057", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public void Import(string fileName, string? password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet) { } public void ImportFromPem(System.ReadOnlySpan certPem) { } public void ImportFromPemFile(string certPemFilePath) { } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs index 405d37188e3729..3924387b72f78b 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs @@ -890,7 +890,7 @@ public X509Certificate2 Create( }; certificate.Encode(writer); - X509Certificate2 ret = new X509Certificate2(writer.Encode()); + X509Certificate2 ret = X509CertificateLoader.LoadCertificate(writer.Encode()); CryptoPool.Return(normalizedSerial); return ret; } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCertificateAssetDownloader.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCertificateAssetDownloader.cs index 309df74ed074a2..b47ea354d8e77c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCertificateAssetDownloader.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslCertificateAssetDownloader.cs @@ -25,16 +25,22 @@ internal static class OpenSslCertificateAssetDownloader try { X509ContentType contentType = X509Certificate2.GetCertContentType(data); + X509Certificate2 certificate; + switch (contentType) { case X509ContentType.Cert: + certificate = X509CertificateLoader.LoadCertificate(data); + break; case X509ContentType.Pkcs7: +#pragma warning disable SYSLIB0057 // Content is known to be PKCS7. + certificate = new X509Certificate2(data); +#pragma warning restore SYSLIB0057 break; default: return null; } - X509Certificate2 certificate = new X509Certificate2(data); certificate.ThrowIfInvalid(); return certificate; } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslDirectoryBasedStoreProvider.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslDirectoryBasedStoreProvider.cs index f232e73cd5f79a..356641e6eb71b2 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslDirectoryBasedStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslDirectoryBasedStoreProvider.cs @@ -85,7 +85,7 @@ public void CloneTo(X509Certificate2Collection collection) { try { - var cert = new X509Certificate2(filePath); + X509Certificate2 cert = X509CertificateLoader.LoadPkcs12FromFile(filePath, null); // If we haven't already loaded a cert .Equal to this one, copy it to the collection. if (loadedCerts.Add(cert)) @@ -154,7 +154,7 @@ private void AddCertToStore(ICertificatePal certPal) try { - using (X509Certificate2 fromFile = new X509Certificate2(existingFilename)) + using (X509Certificate2 fromFile = X509CertificateLoader.LoadPkcs12FromFile(existingFilename, null)) { if (fromFile.HasPrivateKey) { @@ -264,7 +264,7 @@ public void Remove(ICertificatePal certPal) try { - using (X509Certificate2 candidate = new X509Certificate2(maybeMatch)) + using (X509Certificate2 candidate = X509CertificateLoader.LoadPkcs12FromFile(maybeMatch, null)) { if (candidate.Equals(cert)) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs index d2be71772e589f..b4f9af1318fd26 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs @@ -55,6 +55,7 @@ public X509Certificate() // Null turns into the empty span here, which is correct for compat. [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(byte[] data) : this(new ReadOnlySpan(data)) { @@ -73,6 +74,7 @@ private protected X509Certificate(ReadOnlySpan data) } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(byte[] rawData, string? password) : this(rawData, password, X509KeyStorageFlags.DefaultKeySet) { @@ -80,12 +82,14 @@ public X509Certificate(byte[] rawData, string? password) [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(byte[] rawData, SecureString? password) : this(rawData, password, X509KeyStorageFlags.DefaultKeySet) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags) { if (rawData == null || rawData.Length == 0) @@ -101,6 +105,7 @@ public X509Certificate(byte[] rawData, string? password, X509KeyStorageFlags key [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags) { if (rawData == null || rawData.Length == 0) @@ -140,12 +145,14 @@ internal X509Certificate(ICertificatePalCore pal) } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(string fileName) : this(fileName, (string?)null, X509KeyStorageFlags.DefaultKeySet) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(string fileName, string? password) : this(fileName, password, X509KeyStorageFlags.DefaultKeySet) { @@ -153,12 +160,14 @@ public X509Certificate(string fileName, string? password) [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(string fileName, SecureString? password) : this(fileName, password, X509KeyStorageFlags.DefaultKeySet) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate(string fileName, string? password, X509KeyStorageFlags keyStorageFlags) { ArgumentNullException.ThrowIfNull(fileName); @@ -185,6 +194,7 @@ private protected X509Certificate(string fileName, ReadOnlySpan password, [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] #pragma warning disable SYSLIB0026 public X509Certificate(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) : this() #pragma warning restore SYSLIB0026 @@ -220,12 +230,14 @@ public X509Certificate(SerializationInfo info, StreamingContext context) : this( } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static X509Certificate CreateFromCertFile(string filename) { return new X509Certificate(filename); } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static X509Certificate CreateFromSignedFile(string filename) { return new X509Certificate(filename); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index d6a9ba6ef0d5a5..ea56200fd74ba4 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -48,12 +48,14 @@ public X509Certificate2() } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(byte[] rawData) : base(rawData) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(byte[] rawData, string? password) : base(rawData, password) { @@ -61,12 +63,14 @@ public X509Certificate2(byte[] rawData, string? password) [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(byte[] rawData, SecureString? password) : base(rawData, password) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags) : base(rawData, password, keyStorageFlags) { @@ -74,6 +78,7 @@ public X509Certificate2(byte[] rawData, string? password, X509KeyStorageFlags ke [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(byte[] rawData, SecureString? password, X509KeyStorageFlags keyStorageFlags) : base(rawData, password, keyStorageFlags) { @@ -87,6 +92,7 @@ public X509Certificate2(byte[] rawData, SecureString? password, X509KeyStorageFl /// /// An error with the certificate occurs. [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(ReadOnlySpan rawData) : base(rawData) { @@ -107,6 +113,7 @@ public X509Certificate2(ReadOnlySpan rawData) /// /// An error with the certificate occurs. [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(ReadOnlySpan rawData, ReadOnlySpan password, X509KeyStorageFlags keyStorageFlags = 0) : base(rawData, password, keyStorageFlags) { @@ -124,12 +131,14 @@ internal X509Certificate2(ICertificatePal pal) } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName) : base(fileName) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName, string? password) : base(fileName, password) { @@ -137,12 +146,14 @@ public X509Certificate2(string fileName, string? password) [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName, SecureString? password) : base(fileName, password) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName, string? password, X509KeyStorageFlags keyStorageFlags) : base(fileName, password, keyStorageFlags) { @@ -150,12 +161,14 @@ public X509Certificate2(string fileName, string? password, X509KeyStorageFlags k [UnsupportedOSPlatform("browser")] [CLSCompliantAttribute(false)] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName, SecureString? password, X509KeyStorageFlags keyStorageFlags) : base(fileName, password, keyStorageFlags) { } [UnsupportedOSPlatform("browser")] + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public X509Certificate2(string fileName, ReadOnlySpan password, X509KeyStorageFlags keyStorageFlags = 0) : base(fileName, password, keyStorageFlags) { @@ -1096,7 +1109,7 @@ public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem) throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } - X509Certificate2 ret = new X509Certificate2(certData.Span); + X509Certificate2 ret = X509CertificateLoader.LoadCertificate(certData.Span); // Certs are public data, no need to clear. CryptoPool.Return(certBytes, clearSize: 0); return ret; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs index b51c24698bcbdb..19d6d84b7bd62d 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2Collection.cs @@ -136,6 +136,7 @@ public X509Certificate2Collection Find(X509FindType findType, object findValue, IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(byte[] rawData) { ArgumentNullException.ThrowIfNull(rawData); @@ -149,6 +150,7 @@ public void Import(byte[] rawData) /// /// The certificate data to read. /// + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(ReadOnlySpan rawData) { using (var safePasswordHandle = new SafePasswordHandle((string?)null, passwordProvided: false)) @@ -158,6 +160,7 @@ public void Import(ReadOnlySpan rawData) } } + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStorageFlags = 0) { ArgumentNullException.ThrowIfNull(rawData); @@ -177,6 +180,7 @@ public void Import(byte[] rawData, string? password, X509KeyStorageFlags keyStor /// /// A bitwise combination of the enumeration values that control where and how to import the certificate. /// + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(ReadOnlySpan rawData, string? password, X509KeyStorageFlags keyStorageFlags = 0) { Import(rawData, password.AsSpan(), keyStorageFlags); @@ -194,6 +198,7 @@ public void Import(ReadOnlySpan rawData, string? password, X509KeyStorageF /// /// A bitwise combination of the enumeration values that control where and how to import the certificate. /// + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(ReadOnlySpan rawData, ReadOnlySpan password, X509KeyStorageFlags keyStorageFlags = 0) { X509Certificate.ValidateKeyStorageFlags(keyStorageFlags); @@ -205,6 +210,7 @@ public void Import(ReadOnlySpan rawData, ReadOnlySpan password, X509 } } + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(string fileName) { ArgumentNullException.ThrowIfNull(fileName); @@ -216,6 +222,7 @@ public void Import(string fileName) } } + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(string fileName, string? password, X509KeyStorageFlags keyStorageFlags = 0) { ArgumentNullException.ThrowIfNull(fileName); @@ -241,6 +248,7 @@ public void Import(string fileName, string? password, X509KeyStorageFlags keySto /// /// A bitwise combination of the enumeration values that control where and how to import the certificate. /// + [Obsolete(Obsoletions.X509CtorCertDataObsoleteMessage, DiagnosticId = Obsoletions.X509CtorCertDataObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public void Import(string fileName, ReadOnlySpan password, X509KeyStorageFlags keyStorageFlags = 0) { ArgumentNullException.ThrowIfNull(fileName); @@ -396,8 +404,9 @@ public void ImportFromPem(ReadOnlySpan certPem) { throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate); } - - Import(certBytes); +#pragma warning disable CA1416 // X509CertificateLoader is not available on browser. + Add(X509CertificateLoader.LoadCertificate(certBytes)); +#pragma warning restore CA1416 added++; } } diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj index 699b36cc33ddf3..84f0fa2947c49c 100644 --- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj +++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj @@ -3,7 +3,7 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-unix;$(NetCoreAppCurrent)-android;$(NetCoreAppCurrent)-browser;$(NetCoreAppCurrent)-osx;$(NetCoreAppCurrent)-ios;$(NetCoreAppCurrent)-tvos true true - $(NoWarn);SYSLIB0021;SYSLIB0026;SYSLIB0027;SYSLIB0028 + $(NoWarn);SYSLIB0021;SYSLIB0026;SYSLIB0027;SYSLIB0028;SYSLIB0057 true @@ -20,7 +20,7 @@ + Link="Common\System\IO\MemoryMappedFiles\MemoryMappedFileMemoryManager.cs" /> $(NetCoreAppCurrent)-windows - $(NoWarn);SYSLIB0003 + $(NoWarn);SYSLIB0003;SYSLIB0057 @@ -30,4 +30,4 @@ - \ No newline at end of file + From 5b7e77d086214ab3124fd93a618923f03751bff8 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Fri, 5 Jul 2024 11:40:28 -0700 Subject: [PATCH 34/72] [wasm] Don't use SyncTextWriter in single-threaded wasm builds (#101221) Bypass SyncTextWriter on single-threaded wasm to avoid compiling synchronization code during startup when console is touched --- src/libraries/System.Console/tests/ReadAndWrite.cs | 4 +++- src/libraries/System.Console/tests/SyncTextWriter.cs | 3 ++- .../src/System.Private.CoreLib.Shared.projitems | 2 ++ .../System.Private.CoreLib/src/System/IO/TextWriter.cs | 4 ++++ .../tests/System.IO.Tests/StreamReader/StreamReader.cs | 3 ++- .../tests/System.IO.Tests/StreamWriter/StreamWriter.cs | 3 ++- .../tests/System.IO.Tests/TextWriter/TextWriterTests.cs | 3 ++- 7 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Console/tests/ReadAndWrite.cs b/src/libraries/System.Console/tests/ReadAndWrite.cs index d60fd6aa34cc04..913d85835dbb18 100644 --- a/src/libraries/System.Console/tests/ReadAndWrite.cs +++ b/src/libraries/System.Console/tests/ReadAndWrite.cs @@ -158,7 +158,9 @@ public static async Task OutWriteAndWriteLineOverloads() Console.SetOut(sw); TextWriter writer = Console.Out; Assert.NotNull(writer); - Assert.NotEqual(writer, sw); // the writer we provide gets wrapped + // Browser bypasses SyncTextWriter for faster startup + if (!OperatingSystem.IsBrowser()) + Assert.NotEqual(writer, sw); // the writer we provide gets wrapped // We just want to ensure none of these throw exceptions, we don't actually validate // what was written. diff --git a/src/libraries/System.Console/tests/SyncTextWriter.cs b/src/libraries/System.Console/tests/SyncTextWriter.cs index 7d0a2f3016b64d..772e1e2b44a915 100644 --- a/src/libraries/System.Console/tests/SyncTextWriter.cs +++ b/src/libraries/System.Console/tests/SyncTextWriter.cs @@ -12,7 +12,8 @@ public class SyncTextWriter { - [Fact] + // Browser bypasses SyncTextWriter for faster startup + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void SyncTextWriterLockedOnThis() { TextWriter oldWriter = Console.Out; diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 233d19f0d5b5f4..1ed17ee58cde75 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -22,6 +22,8 @@ true true true + true + $(DefineConstants);FEATURE_WASM_MANAGED_THREADS $(DefineConstants);BIGENDIAN diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs index a6070683047a33..d949017809f825 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs @@ -759,7 +759,11 @@ public static TextWriter Synchronized(TextWriter writer) { ArgumentNullException.ThrowIfNull(writer); +#if !TARGET_BROWSER || FEATURE_WASM_MANAGED_THREADS return writer is SyncTextWriter ? writer : new SyncTextWriter(writer); +#else + return writer; +#endif } internal sealed class SyncTextWriter : TextWriter, IDisposable diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs index bd455448efccaf..29677f512d9f73 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs @@ -29,7 +29,8 @@ public void ObjectClosedReadLineBaseStream() Assert.Throws(() => sr.ReadLine()); } - [Fact] + // Browser bypasses SyncTextWriter for faster startup + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Synchronized_NewObject() { using (Stream str = GetLargeStream()) diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs index 69edb13a8a1fce..cc09f1c0d2032c 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs @@ -7,7 +7,8 @@ namespace System.IO.Tests { public partial class WriteTests { - [Fact] + // Browser bypasses SyncTextWriter for faster startup + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Synchronized_NewObject() { using (Stream str = CreateStream()) diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs index 2299dc86f567db..47541635c8eef6 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs @@ -690,7 +690,8 @@ public void DisposeAsync_ExceptionReturnedInTask() Assert.Same(e, vt.AsTask().Exception.InnerException); } - [Fact] + // Browser bypasses SyncTextWriter for faster startup + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task FlushAsync_Precanceled() { Assert.Equal(TaskStatus.RanToCompletion, TextWriter.Null.FlushAsync(new CancellationToken(true)).Status); From ac25c23ecd087c823d71daea03a8b86e0d493ca9 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 5 Jul 2024 15:52:56 -0400 Subject: [PATCH 35/72] Fix typo in RSASignaturePaddingMode documentation Co-authored-by: joegoldman2 <147369450+joegoldman2@users.noreply.github.com> --- .../src/System/Security/Cryptography/RSASignaturePaddingMode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs index 16dec397bd5155..e0a3a8dabb05bd 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePaddingMode.cs @@ -21,7 +21,7 @@ public enum RSASignaturePaddingMode /// Probabilistic Signature Scheme. /// /// - /// This corresponds to the RRSASSA-PSS signature scheme of the PKCS #1 RSA Encryption Standard. + /// This corresponds to the RSASSA-PSS signature scheme of the PKCS #1 RSA Encryption Standard. /// It is recommended for new applications. /// Pss, From 84bda51cbe8ba5aa7b970dc8deea34e9f0d1912c Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 5 Jul 2024 22:13:43 +0100 Subject: [PATCH 36/72] Remove `virtual` modifier from `InternalFallback` (#104483) * `System.Text.DecoderFallbackBuffer` * `System.Text.EncoderFallbackBuffer` --- .../System.Private.CoreLib/src/System/Text/DecoderFallback.cs | 2 +- .../System.Private.CoreLib/src/System/Text/EncoderFallback.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs index a584f2fd65dbd6..c1d4209d4805cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/DecoderFallback.cs @@ -104,7 +104,7 @@ internal static DecoderFallbackBuffer CreateAndInitialize(Encoding encoding, Dec // Right now this has both bytes and bytes[], since we might have extra bytes, hence the // array, and we might need the index, hence the byte* // Don't touch ref chars unless we succeed - internal virtual unsafe bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars) + internal unsafe bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars) { Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize"); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderFallback.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderFallback.cs index 0607dd6ce62350..fdd585b02334ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderFallback.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderFallback.cs @@ -293,7 +293,7 @@ private Rune GetNextRune() // Note that this could also change the contents of this.encoder, which is the same // object that the caller is using, so the caller could mess up the encoder for us // if they aren't careful. - internal virtual unsafe bool InternalFallback(char ch, ref char* chars) + internal unsafe bool InternalFallback(char ch, ref char* chars) { // Shouldn't have null charStart Debug.Assert(charStart != null, From 5505150987bfe2704482213a502a42aba9bd8db3 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Fri, 5 Jul 2024 15:13:12 -0700 Subject: [PATCH 37/72] Arm64/Sve: Implement AbsoluteCompare* and Compare* APIs (#104464) * Add AbsoluteCompare*() APIs * Map API to instructions * Add test coverage * Add support for AbsoluteCompare * uncomment some other tests * Add CompareGreater* and CompareLess* APIs * Add remaining Compare* APIs * Map API to instructions * fix test cases * Add test coverage for Sve.CompareUnordered --- src/coreclr/jit/hwintrinsiccodegenarm64.cpp | 9 +- src/coreclr/jit/hwintrinsiclistarm64sve.h | 11 + src/coreclr/jit/lowerarmarch.cpp | 8 +- src/coreclr/jit/lsraarm64.cpp | 27 +- .../Arm/Sve.PlatformNotSupported.cs | 703 ++++++++++++++++- .../src/System/Runtime/Intrinsics/Arm/Sve.cs | 704 +++++++++++++++++- .../ref/System.Runtime.Intrinsics.cs | 108 +++ .../GenerateHWIntrinsicTests_Arm.cs | 111 ++- .../HardwareIntrinsics/Arm/Shared/Helpers.cs | 190 +++++ .../_SveBinaryRetMaskOpTestTemplate.template | 571 ++++++++++++++ 10 files changed, 2326 insertions(+), 116 deletions(-) create mode 100644 src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryRetMaskOpTestTemplate.template diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index 706329ca038831..4b47ad7412967c 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -586,7 +586,14 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case 2: { - assert(instrIsRMW); + if (!instrIsRMW) + { + // Perform the actual "predicated" operation so that `embMaskOp1Reg` is the first operand + // and `embMaskOp2Reg` is the second operand. + GetEmitter()->emitIns_R_R_R_R(insEmbMask, emitSize, targetReg, maskReg, embMaskOp1Reg, + embMaskOp2Reg, opt); + break; + } insScalableOpts sopt = INS_SCALABLE_OPTS_NONE; bool hasShift = false; diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index 6933f8e2f9b816..9288662fd003a8 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -17,6 +17,10 @@ // SVE Intrinsics #define FIRST_NI_Sve NI_Sve_Abs HARDWARE_INTRINSIC(Sve, Abs, -1, -1, false, {INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_abs, INS_invalid, INS_sve_fabs, INS_sve_fabs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, AbsoluteCompareGreaterThan, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_facgt, INS_sve_facgt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, AbsoluteCompareGreaterThanOrEqual, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_facge, INS_sve_facge}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, AbsoluteCompareLessThan, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_faclt, INS_sve_faclt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, AbsoluteCompareLessThanOrEqual, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_facle, INS_sve_facle}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(Sve, AbsoluteDifference, -1, -1, false, {INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_fabd, INS_sve_fabd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Add, -1, -1, false, {INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_fadd, INS_sve_fadd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, AddAcross, -1, 1, true, {INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_faddv, INS_sve_faddv}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) @@ -26,6 +30,13 @@ HARDWARE_INTRINSIC(Sve, AndAcross, HARDWARE_INTRINSIC(Sve, BitwiseClear, -1, -1, false, {INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_sve_bic, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, BooleanNot, -1, -1, false, {INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_sve_cnot, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Compact, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact, INS_sve_compact}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, CompareEqual, -1, -1, false, {INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_cmpeq, INS_sve_fcmeq, INS_sve_fcmeq}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareGreaterThan, -1, -1, false, {INS_sve_cmpgt, INS_sve_cmphi, INS_sve_cmpgt, INS_sve_cmphi, INS_sve_cmpgt, INS_sve_cmphi, INS_sve_cmpgt, INS_sve_cmphi, INS_sve_fcmgt, INS_sve_fcmgt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareGreaterThanOrEqual, -1, -1, false, {INS_sve_cmpge, INS_sve_cmphs, INS_sve_cmpge, INS_sve_cmphs, INS_sve_cmpge, INS_sve_cmphs, INS_sve_cmpge, INS_sve_cmphs, INS_sve_fcmge, INS_sve_fcmge}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareLessThan, -1, -1, false, {INS_sve_cmplt, INS_sve_cmplo, INS_sve_cmplt, INS_sve_cmplo, INS_sve_cmplt, INS_sve_cmplo, INS_sve_cmplt, INS_sve_cmplo, INS_sve_fcmlt, INS_sve_fcmlt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareLessThanOrEqual, -1, -1, false, {INS_sve_cmple, INS_sve_cmpls, INS_sve_cmple, INS_sve_cmpls, INS_sve_cmple, INS_sve_cmpls, INS_sve_cmple, INS_sve_cmpls, INS_sve_fcmle, INS_sve_fcmle}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareNotEqualTo, -1, -1, false, {INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_fcmne, INS_sve_fcmne}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) +HARDWARE_INTRINSIC(Sve, CompareUnordered, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcmuo, INS_sve_fcmuo}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_ReturnsPerElementMask) HARDWARE_INTRINSIC(Sve, Compute16BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, Compute32BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, Compute64BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 6d99207ac875ab..ca37c3a951c59a 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -1453,12 +1453,18 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) var_types simdType = Compiler::getSIMDTypeForSize(simdSize); GenTree* trueMask = comp->gtNewSimdAllTrueMaskNode(simdBaseJitType, simdSize); GenTree* falseVal = comp->gtNewZeroConNode(simdType); + var_types nodeType = simdType; + + if (HWIntrinsicInfo::ReturnsPerElementMask(node->GetHWIntrinsicId())) + { + nodeType = TYP_MASK; + } BlockRange().InsertBefore(node, trueMask); BlockRange().InsertBefore(node, falseVal); GenTreeHWIntrinsic* condSelNode = - comp->gtNewSimdHWIntrinsicNode(simdType, trueMask, node, falseVal, NI_Sve_ConditionalSelect, + comp->gtNewSimdHWIntrinsicNode(nodeType, trueMask, node, falseVal, NI_Sve_ConditionalSelect, simdBaseJitType, simdSize); BlockRange().InsertAfter(node, condSelNode); if (foundUse) diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 8a5b20e4055052..9aa2cc7dfbcefc 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -1960,30 +1960,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou getLowVectorOperandAndCandidates(intrin, &lowVectorOperandNum, &lowVectorCandidates); } - if ((intrin.id == NI_Sve_ConditionalSelect) && (intrin.op2->IsEmbMaskOp()) && - (intrin.op2->isRMWHWIntrinsic(compiler))) - { - // For ConditionalSelect, if there is an embedded operation, and the operation has RMW semantics - // then record delay-free for them. - GenTreeHWIntrinsic* intrinEmbOp2 = intrin.op2->AsHWIntrinsic(); - size_t numArgs = intrinEmbOp2->GetOperandCount(); - assert((numArgs == 1) || (numArgs == 2)); - const HWIntrinsic intrinEmb(intrinEmbOp2); - if (HWIntrinsicInfo::IsLowVectorOperation(intrinEmb.id)) - { - getLowVectorOperandAndCandidates(intrinEmb, &lowVectorOperandNum, &lowVectorCandidates); - } - - tgtPrefUse = BuildUse(intrinEmbOp2->Op(1)); - srcCount += 1; - - for (size_t argNum = 2; argNum <= numArgs; argNum++) - { - srcCount += BuildDelayFreeUses(intrinEmbOp2->Op(argNum), intrinEmbOp2->Op(1), - (argNum == lowVectorOperandNum) ? lowVectorCandidates : RBM_NONE); - } - } - else if (tgtPrefOp2) + if (tgtPrefOp2) { if (!intrin.op2->isContained()) { @@ -2038,7 +2015,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou { SingleTypeRegSet candidates = lowVectorOperandNum == 2 ? lowVectorCandidates : RBM_NONE; - if (intrin.op2->gtType == TYP_MASK) + if (intrin.op2->OperIsHWIntrinsic(NI_Sve_ConvertVectorToMask)) { assert(lowVectorOperandNum != 2); candidates = RBM_ALLMASK.GetPredicateRegSet(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index b56d0f795d59fc..10318cb7240409 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -75,6 +75,62 @@ internal Arm64() { } /// public static unsafe Vector Abs(Vector value) { throw new PlatformNotSupportedException(); } + /// Absolute compare greater than + + /// + /// svbool_t svacgt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svacgt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Absolute compare greater than or equal to + + /// + /// svbool_t svacge[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svacge[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Absolute compare less than + + /// + /// svbool_t svaclt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACLT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svaclt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACLT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Absolute compare less than or equal to + + /// + /// svbool_t svacle[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACLE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svacle[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACLE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// AbsoluteDifference : Absolute difference /// @@ -640,6 +696,614 @@ internal Arm64() { } /// public static unsafe Vector BooleanNot(Vector value) { throw new PlatformNotSupportedException(); } + /// Shuffle active elements of vector to the right and fill with zero + + /// + /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } + + /// Compare equal to + + /// + /// svbool_t svcmpeq[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpeq[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Compare greater than + + /// + /// svbool_t svcmpgt[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHI Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPHI Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGT Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPGT Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPGT Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGT Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPGT Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHI Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPHI Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHI Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPHI Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpgt[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHI Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// Compare greater than or equal to + + /// + /// svbool_t svcmpge[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHS Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPHS Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPGE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPGE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPGE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHS Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPHS Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHS Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPHS Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpge[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHS Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// Compare less than + + /// + /// svbool_t svcmplt[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHI Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPLO Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGT Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGT Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPLT Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGT Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPLT Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGT Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGT Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPLT Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGT Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHI Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPLO Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHI Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPLO Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmplt[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHI Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// Compare less than or equal to + + /// + /// svbool_t svcmple[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHS Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPLS Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGE Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGE Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPLE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGE Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPLE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGE Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGE Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPLE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGE Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHS Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPLS Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHS Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPLS Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmple[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHS Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// Compare not equal to + + /// + /// svbool_t svcmpne[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpne[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + + /// Compare unordered with + + /// + /// svbool_t svcmpuo[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMUO Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareUnordered(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + + /// + /// svbool_t svcmpuo[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMUO Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareUnordered(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// Compute vector addresses for 16-bit data /// @@ -747,45 +1411,6 @@ internal Arm64() { } /// public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } - /// Shuffle active elements of vector to the right and fill with zero - - /// - /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// - /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// - /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// - /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// - /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// - /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) { throw new PlatformNotSupportedException(); } - - /// Compare equal to /// ConditionalSelect : Conditionally select elements diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index 979a712112c65a..f12e1f4d48ba29 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -103,6 +103,63 @@ internal Arm64() { } /// public static unsafe Vector Abs(Vector value) => Abs(value); + + /// Absolute compare greater than + + /// + /// svbool_t svacgt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareGreaterThan(Vector left, Vector right) => AbsoluteCompareGreaterThan(left, right); + + /// + /// svbool_t svacgt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareGreaterThan(Vector left, Vector right) => AbsoluteCompareGreaterThan(left, right); + + /// Absolute compare greater than or equal to + + /// + /// svbool_t svacge[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareGreaterThanOrEqual(Vector left, Vector right) => AbsoluteCompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svacge[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareGreaterThanOrEqual(Vector left, Vector right) => AbsoluteCompareGreaterThanOrEqual(left, right); + + /// Absolute compare less than + + /// + /// svbool_t svaclt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACLT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareLessThan(Vector left, Vector right) => AbsoluteCompareLessThan(left, right); + + /// + /// svbool_t svaclt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACLT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareLessThan(Vector left, Vector right) => AbsoluteCompareLessThan(left, right); + + /// Absolute compare less than or equal to + + /// + /// svbool_t svacle[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FACLE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector AbsoluteCompareLessThanOrEqual(Vector left, Vector right) => AbsoluteCompareLessThanOrEqual(left, right); + + /// + /// svbool_t svacle[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FACLE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector AbsoluteCompareLessThanOrEqual(Vector left, Vector right) => AbsoluteCompareLessThanOrEqual(left, right); + /// AbsoluteDifference : Absolute difference /// @@ -669,6 +726,614 @@ internal Arm64() { } /// public static unsafe Vector BooleanNot(Vector value) => BooleanNot(value); + /// Shuffle active elements of vector to the right and fill with zero + + /// + /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) + /// COMPACT Zresult.S, Pg, Zop.S + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + /// + /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) + /// COMPACT Zresult.D, Pg, Zop.D + /// + public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); + + + /// Compare equal to + + /// + /// svbool_t svcmpeq[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPEQ Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPEQ Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPEQ Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// + /// svbool_t svcmpeq[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPEQ Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareEqual(Vector left, Vector right) => CompareEqual(left, right); + + /// Compare greater than + + /// + /// svbool_t svcmpgt[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHI Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPHI Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGT Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPGT Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPGT Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGT Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGT Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPGT Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGT Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHI Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPHI Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHI Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPHI Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + /// + /// svbool_t svcmpgt[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHI Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThan(Vector left, Vector right) => CompareGreaterThan(left, right); + + + /// Compare greater than or equal to + + /// + /// svbool_t svcmpge[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHS Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPHS Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPGE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPGE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPGE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHS Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPHS Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHS Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPHS Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// + /// svbool_t svcmpge[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHS Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareGreaterThanOrEqual(Vector left, Vector right) => CompareGreaterThanOrEqual(left, right); + + /// Compare less than + + /// + /// svbool_t svcmplt[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHI Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPLO Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGT Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGT Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPLT Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGT Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPLT Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGT Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGT Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPLT Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGT Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHI Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPLO Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHI Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPLO Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + /// + /// svbool_t svcmplt[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHI Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThan(Vector left, Vector right) => CompareLessThan(left, right); + + + /// Compare less than or equal to + + /// + /// svbool_t svcmple[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPHS Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_u8](svbool_t pg, svuint8_t op1, svuint64_t op2) + /// CMPLS Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMGE Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPGE Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPLE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPGE Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPLE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPGE Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPGE Presult.B, Pg/Z, Zop2.B, Zop1.B + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPLE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMGE Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPHS Presult.H, Pg/Z, Zop2.H, Zop1.H + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_u16](svbool_t pg, svuint16_t op1, svuint64_t op2) + /// CMPLS Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPHS Presult.S, Pg/Z, Zop2.S, Zop1.S + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple_wide[_u32](svbool_t pg, svuint32_t op1, svuint64_t op2) + /// CMPLS Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// + /// svbool_t svcmple[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPHS Presult.D, Pg/Z, Zop2.D, Zop1.D + /// + public static unsafe Vector CompareLessThanOrEqual(Vector left, Vector right) => CompareLessThanOrEqual(left, right); + + /// Compare not equal to + + /// + /// svbool_t svcmpne[_u8](svbool_t pg, svuint8_t op1, svuint8_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_s16](svbool_t pg, svint16_t op1, svint16_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne_wide[_s16](svbool_t pg, svint16_t op1, svint64_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_s32](svbool_t pg, svint32_t op1, svint32_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne_wide[_s32](svbool_t pg, svint32_t op1, svint64_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_s64](svbool_t pg, svint64_t op1, svint64_t op2) + /// CMPNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_s8](svbool_t pg, svint8_t op1, svint8_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.B + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne_wide[_s8](svbool_t pg, svint8_t op1, svint64_t op2) + /// CMPNE Presult.B, Pg/Z, Zop1.B, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_u16](svbool_t pg, svuint16_t op1, svuint16_t op2) + /// CMPNE Presult.H, Pg/Z, Zop1.H, Zop2.H + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_u32](svbool_t pg, svuint32_t op1, svuint32_t op2) + /// CMPNE Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + /// + /// svbool_t svcmpne[_u64](svbool_t pg, svuint64_t op1, svuint64_t op2) + /// CMPNE Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareNotEqualTo(Vector left, Vector right) => CompareNotEqualTo(left, right); + + + /// Compare unordered with + + /// + /// svbool_t svcmpuo[_f64](svbool_t pg, svfloat64_t op1, svfloat64_t op2) + /// FCMUO Presult.D, Pg/Z, Zop1.D, Zop2.D + /// + public static unsafe Vector CompareUnordered(Vector left, Vector right) => CompareUnordered(left, right); + + /// + /// svbool_t svcmpuo[_f32](svbool_t pg, svfloat32_t op1, svfloat32_t op2) + /// FCMUO Presult.S, Pg/Z, Zop1.S, Zop2.S + /// + public static unsafe Vector CompareUnordered(Vector left, Vector right) => CompareUnordered(left, right); + /// Compute vector addresses for 16-bit data /// @@ -776,45 +1441,6 @@ internal Arm64() { } /// public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); - /// Shuffle active elements of vector to the right and fill with zero - - /// - /// svfloat64_t svcompact[_f64](svbool_t pg, svfloat64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// - /// svint32_t svcompact[_s32](svbool_t pg, svint32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// - /// svint64_t svcompact[_s64](svbool_t pg, svint64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// - /// svfloat32_t svcompact[_f32](svbool_t pg, svfloat32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// - /// svuint32_t svcompact[_u32](svbool_t pg, svuint32_t op) - /// COMPACT Zresult.S, Pg, Zop.S - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// - /// svuint64_t svcompact[_u64](svbool_t pg, svuint64_t op) - /// COMPACT Zresult.D, Pg, Zop.D - /// - public static unsafe Vector Compact(Vector mask, Vector value) => Compact(mask, value); - - /// Compare equal to /// ConditionalSelect : Conditionally select elements diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 66fc0bdd4127ac..f4885977916aeb 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -4202,6 +4202,15 @@ internal Arm64() { } public static System.Numerics.Vector Abs(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector Abs(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AbsoluteCompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteCompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector AbsoluteDifference(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector AbsoluteDifference(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector AbsoluteDifference(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -4287,6 +4296,105 @@ internal Arm64() { } public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector Compact(System.Numerics.Vector mask, System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareGreaterThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThan(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareLessThanOrEqual(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareNotEqualTo(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + + public static System.Numerics.Vector CompareUnordered(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector CompareUnordered(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector Compute16BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 8f6bf8a33af236..1caaf29a9d226d 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -78,6 +78,16 @@ } }"; +const string SimpleVecOpTest_ValidationLogicForCndSelMask = @"for (var i = 0; i < RetElementCount; i++) + { + {Op1BaseType} iterResult = ({GetIterResult} != 0) ? trueVal[i] : falseVal[i]; + if (iterResult != result[i]) + { + succeeded = false; + break; + } + }"; + const string SimpleVecOpTest_ValidationLogicForCndSel_FalseValue = @"for (var i = 0; i < RetElementCount; i++) { {Op1BaseType} iterResult = (mask[i] != 0) ? trueVal[i] : {GetIterResult}; @@ -170,6 +180,16 @@ const string SimpleTernVecOpTest_ValidationLogicForCndSel = @"for (var i = 0; i < RetElementCount; i++) { {Op1BaseType} iterResult = (mask[i] != 0) ? {GetIterResult} : falseVal[i]; + if ({ConvertFunc}(iterResult) != {ConvertFunc}(result[i])) + { + succeeded = false; + break; + } + }"; + +const string SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue = @"for (var i = 0; i < RetElementCount; i++) + { + {Op1BaseType} iterResult = (mask[i] != 0) ? trueVal[i] : {GetIterResult}; if (mask[i] != 0) { // Pick the trueValue @@ -183,18 +203,8 @@ { // For false, the values are merged with destination, and we do not know // those contents would be, so skip verification for them. - } - }"; - -const string SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue = @"for (var i = 0; i < RetElementCount; i++) - { - {Op1BaseType} iterResult = (mask[i] != 0) ? trueVal[i] : {GetIterResult}; - if ({ConvertFunc}(iterResult) != {ConvertFunc}(result[i])) - { - succeeded = false; - break; } - }"; + }"; const string VecPairBinOpTest_ValidationLogic = @" int index = 0; @@ -263,6 +273,7 @@ ("_SveBinaryOpTestTemplate.template", "SveVecBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleVecOpTest_ValidationLogicForCndSel_FalseValue }), ("_SveBinaryOpTestTemplate.template", "SveVecBinOpVecTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_VectorValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_VectorValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleVecOpTest_VectorValidationLogicForCndSel_FalseValue }), ("_SveBinaryOpTestTemplate.template", "SveVecBinOpConvertTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue }), + ("_SveBinaryRetMaskOpTestTemplate.template", "SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue, ["TemplateValidationLogicForCndSelMask"] = SimpleVecOpTest_ValidationLogicForCndSelMask }), ("_SveBinaryOpDifferentTypesTestTemplate.template", "SveVecBinOpDifferentTypesTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleVecOpTest_ValidationLogicForCndSel_FalseValue }), ("_SveBinaryMaskOpTestTemplate.template", "SveMaskVecBinOpConvertTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleVecOpTest_ValidationLogicForCndSel_FalseValue }), ("_SveImmBinaryOpTestTemplate.template", "SveVecImmBinOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleVecOpTest_ValidationLogicForCndSel_FalseValue }), @@ -3056,6 +3067,15 @@ ("SveSimpleVecOpTest.template", new Dictionary { ["TestName"] = "Sve_Abs_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Abs", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "-TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.Abs(firstOp[i]) != result[i]", ["GetIterResult"] = "(int)Helpers.Abs(leftOp[i])"}), ("SveSimpleVecOpTest.template", new Dictionary { ["TestName"] = "Sve_Abs_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Abs", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "-TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(long)Helpers.Abs(firstOp[i]) != (long)result[i]", ["GetIterResult"] = "(long)Helpers.Abs(leftOp[i])"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareGreaterThan_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareGreaterThan_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareGreaterThanOrEqual_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareGreaterThanOrEqual_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareLessThan_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareLessThan_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareLessThanOrEqual_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary {["TestName"] = "Sve_AbsoluteCompareLessThanOrEqual_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteCompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveAbsoluteCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveAbsoluteCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_AbsoluteDifference_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteDifference", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "BitConverter.SingleToInt32Bits(Helpers.AbsoluteDifference(left[i], right[i])) != BitConverter.SingleToInt32Bits(result[i])", ["GetIterResult"] = "Helpers.AbsoluteDifference(left[i], right[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), ("SveVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_AbsoluteDifference_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteDifference", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "BitConverter.DoubleToInt64Bits(Helpers.AbsoluteDifference(left[i], right[i])) != BitConverter.DoubleToInt64Bits(result[i])", ["GetIterResult"] = "Helpers.AbsoluteDifference(left[i], right[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), ("SveVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_AbsoluteDifference_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "AbsoluteDifference", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "(sbyte)TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.AbsoluteDifference(left[i], right[i]) != result[i]", ["GetIterResult"] = "(sbyte)Helpers.AbsoluteDifference(left[i], right[i])", ["ConvertFunc"] = ""}), @@ -3141,6 +3161,75 @@ ("SveVecBinOpVecTest.template", new Dictionary { ["TestName"] = "Sve_Compact_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), ("SveVecBinOpVecTest.template", new Dictionary { ["TestName"] = "Sve_Compact_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compact", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateVectorResult"] = "!result.SequenceEqual(Helpers.Compact(left, right))", ["GetVectorResult"] = "Helpers.Compact(left, right)",}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareEqual_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareGreaterThan_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary {["TestName"] = "Sve_CompareGreaterThanOrEqual_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareGreaterThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareGreaterThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareLessThan_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThan", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareLessThan(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThan(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template",new Dictionary { ["TestName"] = "Sve_CompareLessThanOrEqual_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareLessThanOrEqual", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareLessThanOrEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareLessThanOrEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareNotEqualTo_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareNotEqualTo", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.SveCompareNotEqual(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareNotEqual(leftOp[i], rightOp[i])", ["ConvertFunc"] = ""}), + + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareUnordered_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareUnordered", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.SveCompareUnordered(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareUnordered(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.SingleToInt32Bits"}), + ("SveVecBinRetMaskOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_CompareUnordered_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "CompareUnordered", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.SveCompareUnordered(left[i], right[i]) != result[i]", ["GetIterResult"] = "Helpers.SveCompareUnordered(leftOp[i], rightOp[i])", ["ConvertFunc"] = "BitConverter.DoubleToInt64Bits"}), + ("SveMaskVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_Compute16BitAddresses_uint_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(left[i] + ((uint)right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + ((uint)right[i] * 2))"}), ("SveMaskVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_Compute16BitAddresses_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(left[i] + (right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + (right[i] * 2))"}), ("SveMaskVecBinOpConvertTest.template", new Dictionary { ["TestName"] = "Sve_Compute16BitAddresses_ulong_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "Compute16BitAddresses", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(left[i] + ((ulong)right[i] * 2)) != result[i]", ["GetIterResult"] = "(left[i] + ((ulong)right[i] * 2))"}), diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index 3e987ec4612979..ebffa0504f7445 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -1667,6 +1667,196 @@ public static float AbsoluteCompareLessThanOrEqual(float left, float right) return BitConverter.Int32BitsToSingle(result); } + + public static double SveAbsoluteCompareGreaterThan(double left, double right) + { + long result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left > right) + { + result = 1; + } + + return BitConverter.Int64BitsToDouble(result); + } + + public static float SveAbsoluteCompareGreaterThan(float left, float right) + { + int result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left > right) + { + result = 1; + } + + return BitConverter.Int32BitsToSingle(result); + } + + public static double SveAbsoluteCompareGreaterThanOrEqual(double left, double right) + { + long result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left >= right) + { + result = 1; + } + + return BitConverter.Int64BitsToDouble(result); + } + + public static float SveAbsoluteCompareGreaterThanOrEqual(float left, float right) + { + int result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left >= right) + { + result = 1; + } + + return BitConverter.Int32BitsToSingle(result); + } + + public static double SveAbsoluteCompareLessThan(double left, double right) + { + long result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left < right) + { + result = 1; + } + + return BitConverter.Int64BitsToDouble(result); + } + + public static float SveAbsoluteCompareLessThan(float left, float right) + { + int result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left < right) + { + result = 1; + } + + return BitConverter.Int32BitsToSingle(result); + } + + public static double SveAbsoluteCompareLessThanOrEqual(double left, double right) + { + long result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left <= right) + { + result = 1; + } + + return BitConverter.Int64BitsToDouble(result); + } + + public static float SveAbsoluteCompareLessThanOrEqual(float left, float right) + { + int result = 0; + + left = Math.Abs(left); + right = Math.Abs(right); + + if (left <= right) + { + result = 1; + } + + return BitConverter.Int32BitsToSingle(result); + } + + public static double SveCompareEqual(double left, double right) => BitConverter.Int64BitsToDouble((left == right) ? 1 : 0); + public static float SveCompareEqual(float left, float right) => BitConverter.Int32BitsToSingle((left == right) ? 1 : 0); + public static sbyte SveCompareEqual(sbyte left, sbyte right) => (sbyte)((left == right) ? 1 : 0); + public static byte SveCompareEqual(byte left, byte right) => (byte)((left == right) ? 1 : 0); + public static short SveCompareEqual(short left, short right) => (short)((left == right) ? 1 : 0); + public static ushort SveCompareEqual(ushort left, ushort right) => (ushort)((left == right) ? 1 : 0); + public static int SveCompareEqual(int left, int right) => (int)((left == right) ? 1 : 0); + public static uint SveCompareEqual(uint left, uint right) => (uint)((left == right) ? 1 : 0); + public static long SveCompareEqual(long left, long right) => (long)((left == right) ? 1 : 0); + public static ulong SveCompareEqual(ulong left, ulong right) => (ulong)((left == right) ? 1 : 0); + + public static double SveCompareNotEqual(double left, double right) => BitConverter.Int64BitsToDouble((left != right) ? 1 : 0); + public static float SveCompareNotEqual(float left, float right) => BitConverter.Int32BitsToSingle((left != right) ? 1 : 0); + public static sbyte SveCompareNotEqual(sbyte left, sbyte right) => (sbyte)((left != right) ? 1 : 0); + public static byte SveCompareNotEqual(byte left, byte right) => (byte)((left != right) ? 1 : 0); + public static short SveCompareNotEqual(short left, short right) => (short)((left != right) ? 1 : 0); + public static ushort SveCompareNotEqual(ushort left, ushort right) => (ushort)((left != right) ? 1 : 0); + public static int SveCompareNotEqual(int left, int right) => (int)((left != right) ? 1 : 0); + public static uint SveCompareNotEqual(uint left, uint right) => (uint)((left != right) ? 1 : 0); + public static long SveCompareNotEqual(long left, long right) => (long)((left != right) ? 1 : 0); + public static ulong SveCompareNotEqual(ulong left, ulong right) => (ulong)((left != right) ? 1 : 0); + + public static double SveCompareGreaterThan(double left, double right) => BitConverter.Int64BitsToDouble((left > right) ? 1 : 0); + public static float SveCompareGreaterThan(float left, float right) => BitConverter.Int32BitsToSingle((left > right) ? 1 : 0); + public static sbyte SveCompareGreaterThan(sbyte left, sbyte right) => (sbyte)((left > right) ? 1 : 0); + public static byte SveCompareGreaterThan(byte left, byte right) => (byte)((left > right) ? 1 : 0); + public static short SveCompareGreaterThan(short left, short right) => (short)((left > right) ? 1 : 0); + public static ushort SveCompareGreaterThan(ushort left, ushort right) => (ushort)((left > right) ? 1 : 0); + public static int SveCompareGreaterThan(int left, int right) => (int)((left > right) ? 1 : 0); + public static uint SveCompareGreaterThan(uint left, uint right) => (uint)((left > right) ? 1 : 0); + public static long SveCompareGreaterThan(long left, long right) => (long)((left > right) ? 1 : 0); + public static ulong SveCompareGreaterThan(ulong left, ulong right) => (ulong)((left > right) ? 1 : 0); + + public static double SveCompareGreaterThanOrEqual(double left, double right) => BitConverter.Int64BitsToDouble((left >= right) ? 1 : 0); + public static float SveCompareGreaterThanOrEqual(float left, float right) => BitConverter.Int32BitsToSingle((left >= right) ? 1 : 0); + public static sbyte SveCompareGreaterThanOrEqual(sbyte left, sbyte right) => (sbyte)((left >= right) ? 1 : 0); + public static byte SveCompareGreaterThanOrEqual(byte left, byte right) => (byte)((left >= right) ? 1 : 0); + public static short SveCompareGreaterThanOrEqual(short left, short right) => (short)((left >= right) ? 1 : 0); + public static ushort SveCompareGreaterThanOrEqual(ushort left, ushort right) => (ushort)((left >= right) ? 1 : 0); + public static int SveCompareGreaterThanOrEqual(int left, int right) => (int)((left >= right) ? 1 : 0); + public static uint SveCompareGreaterThanOrEqual(uint left, uint right) => (uint)((left >= right) ? 1 : 0); + public static long SveCompareGreaterThanOrEqual(long left, long right) => (long)((left >= right) ? 1 : 0); + public static ulong SveCompareGreaterThanOrEqual(ulong left, ulong right) => (ulong)((left >= right) ? 1 : 0); + + public static double SveCompareLessThan(double left, double right) => BitConverter.Int64BitsToDouble((left < right) ? 1 : 0); + public static float SveCompareLessThan(float left, float right) => BitConverter.Int32BitsToSingle((left < right) ? 1 : 0); + public static sbyte SveCompareLessThan(sbyte left, sbyte right) => (sbyte)((left < right) ? 1 : 0); + public static byte SveCompareLessThan(byte left, byte right) => (byte)((left < right) ? 1 : 0); + public static short SveCompareLessThan(short left, short right) => (short)((left < right) ? 1 : 0); + public static ushort SveCompareLessThan(ushort left, ushort right) => (ushort)((left < right) ? 1 : 0); + public static int SveCompareLessThan(int left, int right) => (int)((left < right) ? 1 : 0); + public static uint SveCompareLessThan(uint left, uint right) => (uint)((left < right) ? 1 : 0); + public static long SveCompareLessThan(long left, long right) => (long)((left < right) ? 1 : 0); + public static ulong SveCompareLessThan(ulong left, ulong right) => (ulong)((left < right) ? 1 : 0); + + public static double SveCompareLessThanOrEqual(double left, double right) => BitConverter.Int64BitsToDouble((left <= right) ? 1 : 0); + public static float SveCompareLessThanOrEqual(float left, float right) => BitConverter.Int32BitsToSingle((left <= right) ? 1 : 0); + public static sbyte SveCompareLessThanOrEqual(sbyte left, sbyte right) => (sbyte)((left <= right) ? 1 : 0); + public static byte SveCompareLessThanOrEqual(byte left, byte right) => (byte)((left <= right) ? 1 : 0); + public static short SveCompareLessThanOrEqual(short left, short right) => (short)((left <= right) ? 1 : 0); + public static ushort SveCompareLessThanOrEqual(ushort left, ushort right) => (ushort)((left <= right) ? 1 : 0); + public static int SveCompareLessThanOrEqual(int left, int right) => (int)((left <= right) ? 1 : 0); + public static uint SveCompareLessThanOrEqual(uint left, uint right) => (uint)((left <= right) ? 1 : 0); + public static long SveCompareLessThanOrEqual(long left, long right) => (long)((left <= right) ? 1 : 0); + public static ulong SveCompareLessThanOrEqual(ulong left, ulong right) => (ulong)((left <= right) ? 1 : 0); + + public static double SveCompareUnordered(double left, double right) => BitConverter.Int64BitsToDouble((double.IsNaN(left) || double.IsNaN(right)) ? 1 : 0); + public static float SveCompareUnordered(float left, float right) => BitConverter.Int32BitsToSingle((float.IsNaN(left) || float.IsNaN(right)) ? 1 : 0); + public static double CompareEqual(double left, double right) { long result = 0; diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryRetMaskOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryRetMaskOpTestTemplate.template new file mode 100644 index 00000000000000..cc9fe1d9e4ea25 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveBinaryRetMaskOpTestTemplate.template @@ -0,0 +1,571 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TemplateName}BinaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + + // Validates executing the test inside conditional, with op1 as falseValue + test.ConditionalSelect_Op1(); + + // Validates executing the test inside conditional, with op2 as falseValue + test.ConditionalSelect_Op2(); + + // Validates executing the test inside conditional, with op3 as falseValue + test.ConditionalSelect_FalseOp(); + + // Validates executing the test inside conditional, with op3 as zero + test.ConditionalSelect_ZeroOp(); + + // Validates the API can be used to load the mask + test.RunLoadMask(); + + // Validates the API can be used inside ConditionalSelect + test.ConditionalSelect_MethodMask(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class {TemplateName}BinaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + private byte[] inArray2; + private byte[] outArray; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {Op2BaseType}[] inArray2, {RetBaseType}[] outArray, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf<{Op2BaseType}>(); + int sizeOfoutArray = outArray.Length * Unsafe.SizeOf<{RetBaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfoutArray) + { + throw new ArgumentException($"Invalid value of alignment: {alignment}, sizeOfinArray1: {sizeOfinArray1}, sizeOfinArray2: {sizeOfinArray2}, sizeOfoutArray: {sizeOfoutArray}"); + } + + this.inArray1 = new byte[alignment * 2]; + this.inArray2 = new byte[alignment * 2]; + this.outArray = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned); + this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), (uint)sizeOfinArray2); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment); + public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + inHandle2.Free(); + outHandle.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op2VectorType}<{Op2BaseType}> _fld2; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario({TemplateName}BinaryOpTest__{TestName} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int Op2ElementCount = Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>() / sizeof({Op2BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _maskData = new {Op1BaseType}[Op1ElementCount]; + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _mask; + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + private {Op2VectorType}<{Op2BaseType}> _falseFld; + + private DataTable _dataTable; + + public {TemplateName}BinaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _mask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _falseFld), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = {NextValueOp2}; } + _dataTable = new DataTable(_data1, _data2, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask = Sve.CreateTrueMask{RetBaseType}(SveMaskPattern.All); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(loadMask, ({Op2BaseType}*)(_dataTable.inArray2Ptr)) + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunLoadMask() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLoadMask)); + + {Op1VectorType}<{Op1BaseType}> loadMask = {Isa}.{Method}(_fld1, _fld2); + + var result = {LoadIsa}.Load{Op1VectorType}(loadMask, ({Op1BaseType}*)(_dataTable.inArray1Ptr)); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectMaskResult(_fld1, _fld2, Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), {Op1VectorType}<{Op1BaseType}>.Zero, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr) + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var op2 = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var result = {Isa}.{Method}(op1, op2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, op2, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, test._fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void ConditionalSelect_Op1() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_mask - operation in TrueValue"); + ConditionalSelectScenario_TrueValue(_mask, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_zero - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_all - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_mask - operation in FalseValue"); + ConditionalSelectScenario_FalseValue(_mask, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_zero - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _fld1); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op1_all - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _fld1); + } + + public void ConditionalSelect_Op2() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_mask - operation in TrueValue"); + ConditionalSelectScenario_TrueValue(_mask, _fld1, _fld2, _fld2); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_zero - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _fld2); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_all - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _fld2); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_mask - operation in FalseValue"); + ConditionalSelectScenario_FalseValue(_mask, _fld1, _fld2, _fld2); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_zero - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _fld2); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Op2_all - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _fld2); + } + + public void ConditionalSelect_FalseOp() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_mask - operation in TrueValue"); + ConditionalSelectScenario_TrueValue(_mask, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_zero - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_all - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_mask - operation in FalseValue"); + ConditionalSelectScenario_FalseValue(_mask, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_zero - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, _falseFld); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_FalseOp_all - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, _falseFld); + } + + public void ConditionalSelect_ZeroOp() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_mask - operation in TrueValue"); + ConditionalSelectScenario_TrueValue(_mask, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_zero - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_all - operation in TrueValue"); + ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_mask - operation in FalseValue"); + ConditionalSelectScenario_FalseValue(_mask, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_zero - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.Zero, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_ZeroOp_all - operation in FalseValue"); + ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}>.AllBitsSet, _fld1, _fld2, {Op1VectorType}<{Op1BaseType}>.Zero); + } + + [method: MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op1BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp) + { + var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2), falseOp); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr); + } + + [method: MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op1BaseType}> op2, {Op1VectorType}<{Op1BaseType}> trueOp) + { + var result = Sve.ConditionalSelect(mask, trueOp, {Isa}.{Method}(op1, op2)); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectResult_FalseValue(mask, op1, op2, trueOp, _dataTable.outArrayPtr); + } + + public void ConditionalSelect_MethodMask() + { + TestLibrary.TestFramework.BeginScenario("ConditionalSelect_Methodmask - operation in TrueValue"); + var result = Sve.ConditionalSelect({Isa}.{Method}(_fld1, _fld2), _fld1, _fld2); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateConditionalSelectMaskResult(_fld1, _fld2, _fld1, _fld2, _dataTable.outArrayPtr); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateConditionalSelectResult_TrueValue({Op1VectorType}<{Op1BaseType}> maskOp, {Op1VectorType}<{Op1BaseType}> leftOp, {Op1VectorType}<{Op1BaseType}> rightOp, {Op1VectorType}<{Op1BaseType}> falseOp, void* output, [CallerMemberName] string method = "") + { + {Op1BaseType}[] mask = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] right = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] falseVal = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref mask[0]), maskOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref right[0]), rightOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref falseVal[0]), falseOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref result[0]), ref Unsafe.AsRef(output), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + bool succeeded = true; + + {TemplateValidationLogicForCndSel} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", mask)})"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" falseOp: ({string.Join(", ", falseVal)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateConditionalSelectResult_FalseValue({Op1VectorType}<{Op1BaseType}> maskOp, {Op1VectorType}<{Op1BaseType}> leftOp, {Op1VectorType}<{Op1BaseType}> rightOp, {Op1VectorType}<{Op1BaseType}> trueOp, void* output, [CallerMemberName] string method = "") + { + {Op1BaseType}[] mask = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] right = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] trueVal = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref mask[0]), maskOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref right[0]), rightOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref trueVal[0]), trueOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref result[0]), ref Unsafe.AsRef(output), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + bool succeeded = true; + + {TemplateValidationLogicForCndSel_FalseValue} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", mask)})"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" trueOp: ({string.Join(", ", trueVal)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateConditionalSelectMaskResult({Op1VectorType}<{Op1BaseType}> leftOp, {Op1VectorType}<{Op1BaseType}> rightOp, {Op1VectorType}<{Op1BaseType}> trueOp, {Op1VectorType}<{Op1BaseType}> falseOp, void* output, [CallerMemberName] string method = "") + { + {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] right = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] trueVal = new {Op1BaseType}[Op1ElementCount]; + {Op1BaseType}[] falseVal = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref right[0]), rightOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref trueVal[0]), trueOp); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref falseVal[0]), falseOp); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref result[0]), ref Unsafe.AsRef(output), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + bool succeeded = true; + + {TemplateValidationLogicForCndSelMask} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" trueOp: ({string.Join(", ", trueVal)})"); + TestLibrary.TestFramework.LogInformation($" falseOp: ({string.Join(", ", falseVal)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), op2); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult(void* op1, void* op2, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, inArray2, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] left, {Op2BaseType}[] right, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {TemplateValidationLogic} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); + TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} From e125e93c3f42a1b90bcb831756b78fab561ab775 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Fri, 5 Jul 2024 17:08:20 -0700 Subject: [PATCH 38/72] Fix an issue with complex number parsing (#104388) --- .../src/System/Numerics/Complex.cs | 4 +- .../tests/ComplexTests.cs | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index 2eb4458ce38fc8..51d0580c285edd 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -2113,7 +2113,7 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro return false; } - if (!double.TryParse(s.Slice(openBracket + 1, semicolon), style, provider, out double real)) + if (!double.TryParse(s.Slice(openBracket + 1, semicolon - openBracket - 1), style, provider, out double real)) { result = default; return false; @@ -2126,7 +2126,7 @@ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatPro semicolon += 1; } - if (!double.TryParse(s.Slice(semicolon + 1, closeBracket - semicolon), style, provider, out double imaginary)) + if (!double.TryParse(s.Slice(semicolon + 1, closeBracket - semicolon - 1), style, provider, out double imaginary)) { result = default; return false; diff --git a/src/libraries/System.Runtime.Numerics/tests/ComplexTests.cs b/src/libraries/System.Runtime.Numerics/tests/ComplexTests.cs index fc843295b2e51f..953a81490813c7 100644 --- a/src/libraries/System.Runtime.Numerics/tests/ComplexTests.cs +++ b/src/libraries/System.Runtime.Numerics/tests/ComplexTests.cs @@ -70,6 +70,120 @@ public static void SqrtMinusOne() Assert.Equal(Complex.Sqrt(-1.0), Complex.ImaginaryOne); } + public static IEnumerable Parse_Valid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; + + var dollarSignCommaSeparatorFormat = new NumberFormatInfo() + { + CurrencySymbol = "$", + CurrencyGroupSeparator = "," + }; + + var decimalSeparatorFormat = new NumberFormatInfo() + { + NumberDecimalSeparator = "." + }; + + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + + yield return new object[] { "-123", defaultStyle, null, -123.0 }; + yield return new object[] { "0", defaultStyle, null, 0.0 }; + yield return new object[] { "123", defaultStyle, null, 123.0 }; + yield return new object[] { " 123 ", defaultStyle, null, 123.0 }; + yield return new object[] { (567.89).ToString(), defaultStyle, null, 567.89 }; + yield return new object[] { (-567.89).ToString(), defaultStyle, null, -567.89 }; + yield return new object[] { "1E23", defaultStyle, null, 1E23 }; + yield return new object[] { "9007199254740997.0", defaultStyle, invariantFormat, 9007199254740996.0 }; + yield return new object[] { "9007199254740997.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 9007199254740996.0 }; + yield return new object[] { "9007199254740997.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 9007199254740996.0 }; + yield return new object[] { "9007199254740997.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", defaultStyle, invariantFormat, 9007199254740998.0 }; + yield return new object[] { "9007199254740997.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", defaultStyle, invariantFormat, 9007199254740998.0 }; + yield return new object[] { "9007199254740997.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", defaultStyle, invariantFormat, 9007199254740998.0 }; + yield return new object[] { "5.005", defaultStyle, invariantFormat, 5.005 }; + yield return new object[] { "5.050", defaultStyle, invariantFormat, 5.05 }; + yield return new object[] { "5.005000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 5.005 }; + yield return new object[] { "5.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005", defaultStyle, invariantFormat, 5.0 }; + yield return new object[] { "5.0050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 5.005 }; + + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, 0.234 }; + yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 234.0 }; + yield return new object[] { new string('0', 458) + "1" + new string('0', 308) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 1E308 }; + yield return new object[] { new string('0', 459) + "1" + new string('0', 308) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, 1E308 }; + + yield return new object[] { "5005.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 5005.0 }; + yield return new object[] { "50050.0", defaultStyle, invariantFormat, 50050.0 }; + yield return new object[] { "5005", defaultStyle, invariantFormat, 5005.0 }; + yield return new object[] { "050050", defaultStyle, invariantFormat, 50050.0 }; + yield return new object[] { "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 0.0 }; + yield return new object[] { "0.005", defaultStyle, invariantFormat, 0.005 }; + yield return new object[] { "0.0500", defaultStyle, invariantFormat, 0.05 }; + yield return new object[] { "6250000000000000000000000000000000e-12", defaultStyle, invariantFormat, 6.25e21 }; + yield return new object[] { "6250000e0", defaultStyle, invariantFormat, 6.25e6 }; + yield return new object[] { "6250100e-5", defaultStyle, invariantFormat, 62.501 }; + yield return new object[] { "625010.00e-4", defaultStyle, invariantFormat, 62.501 }; + yield return new object[] { "62500e-4", defaultStyle, invariantFormat, 6.25 }; + yield return new object[] { "62500", defaultStyle, invariantFormat, 62500.0 }; + yield return new object[] { "10e-3", defaultStyle, invariantFormat, 0.01 }; + + yield return new object[] { (123.1).ToString(), NumberStyles.AllowDecimalPoint, null, 123.1 }; + yield return new object[] { (1000.0).ToString("N0"), NumberStyles.AllowThousands, null, 1000.0 }; + + yield return new object[] { "123", NumberStyles.Any, emptyFormat, 123.0 }; + yield return new object[] { (123.567).ToString(), NumberStyles.Any, emptyFormat, 123.567 }; + yield return new object[] { "123", NumberStyles.Float, emptyFormat, 123.0 }; + yield return new object[] { "$1,000", NumberStyles.Currency, dollarSignCommaSeparatorFormat, 1000.0 }; + yield return new object[] { "$1000", NumberStyles.Currency, dollarSignCommaSeparatorFormat, 1000.0 }; + yield return new object[] { "123.123", NumberStyles.Float, decimalSeparatorFormat, 123.123 }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, decimalSeparatorFormat, -123.0 }; + + yield return new object[] { "NaN", NumberStyles.Any, invariantFormat, double.NaN }; + yield return new object[] { "Infinity", NumberStyles.Any, invariantFormat, double.PositiveInfinity }; + yield return new object[] { "-Infinity", NumberStyles.Any, invariantFormat, double.NegativeInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse(string valueScalar, NumberStyles style, IFormatProvider provider, double expectedScalar) + { + string value = $"<{valueScalar}; {valueScalar}>"; + Complex expected = new Complex(expectedScalar, expectedScalar); + + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Complex result; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Complex.TryParse(value, null, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Complex.Parse(value, null)); + } + + Assert.Equal(expected, Complex.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.True(Complex.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Complex.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.True(Complex.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Complex.Parse(value, style, null)); + Assert.Equal(expected, Complex.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + public static IEnumerable Valid_2_TestData() { foreach (double real in s_validDoubleValues) From e224ffb9b7f39b5a3218a9aa0df774530eb2f397 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 5 Jul 2024 18:27:59 -0700 Subject: [PATCH 39/72] Delete some unused HRESULT macro definitions (#104484) --- src/coreclr/inc/corjit.h | 82 ++----------------- src/coreclr/pal/inc/pal.h | 28 +------ .../tools/Common/JitInterface/CorInfoTypes.cs | 16 ++-- 3 files changed, 17 insertions(+), 109 deletions(-) diff --git a/src/coreclr/inc/corjit.h b/src/coreclr/inc/corjit.h index 18a5be23fdde2d..d8e6a774784f52 100644 --- a/src/coreclr/inc/corjit.h +++ b/src/coreclr/inc/corjit.h @@ -5,11 +5,6 @@ * * * CorJit.h - EE / JIT interface * * * -* Version 1.0 * -******************************************************************************* -* * -* * -* * \*****************************************************************************/ ////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,78 +28,17 @@ #include "corjitflags.h" - -#ifndef MAKE_HRESULT -// If this header is included without including the windows or PAL headers, then define -// MAKE_HRESULT, and associated macros - -/******************* HRESULT types ****************************************/ - -#define FACILITY_WINDOWS 8 -#define FACILITY_URT 19 -#define FACILITY_UMI 22 -#define FACILITY_SXS 23 -#define FACILITY_STORAGE 3 -#define FACILITY_SSPI 9 -#define FACILITY_SCARD 16 -#define FACILITY_SETUPAPI 15 -#define FACILITY_SECURITY 9 -#define FACILITY_RPC 1 -#define FACILITY_WIN32 7 -#define FACILITY_CONTROL 10 -#define FACILITY_NULL 0 -#define FACILITY_MSMQ 14 -#define FACILITY_MEDIASERVER 13 -#define FACILITY_INTERNET 12 -#define FACILITY_ITF 4 -#define FACILITY_DPLAY 21 -#define FACILITY_DISPATCH 2 -#define FACILITY_COMPLUS 17 -#define FACILITY_CERT 11 -#define FACILITY_ACS 20 -#define FACILITY_AAF 18 - -#define NO_ERROR 0L - -#define SEVERITY_SUCCESS 0 -#define SEVERITY_ERROR 1 - -#define SUCCEEDED(Status) ((JITINTERFACE_HRESULT)(Status) >= 0) -#define FAILED(Status) ((JITINTERFACE_HRESULT)(Status)<0) -#define IS_ERROR(Status) ((uint32_t)(Status) >> 31 == SEVERITY_ERROR) // diff from win32 -#define HRESULT_CODE(hr) ((hr) & 0xFFFF) -#define SCODE_CODE(sc) ((sc) & 0xFFFF) -#define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) -#define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff) -#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) -#define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1) - -// both macros diff from Win32 -#define MAKE_HRESULT(sev,fac,code) \ - ((JITINTERFACE_HRESULT) (((uint32_t)(sev)<<31) | ((uint32_t)(fac)<<16) | ((uint32_t)(code))) ) -#define MAKE_SCODE(sev,fac,code) \ - ((SCODE) (((uint32_t)(sev)<<31) | ((uint32_t)(fac)<<16) | ((LONG)(code))) ) - -#define FACILITY_NT_BIT 0x10000000 -#define HRESULT_FROM_WIN32(x) ((JITINTERFACE_HRESULT)(x) <= 0 ? ((JITINTERFACE_HRESULT)(x)) : ((JITINTERFACE_HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000))) -#define __HRESULT_FROM_WIN32(x) HRESULT_FROM_WIN32(x) - -#define HRESULT_FROM_NT(x) ((JITINTERFACE_HRESULT) ((x) | FACILITY_NT_BIT)) -#endif // MAKE_HRESULT - /*****************************************************************************/ - // These are error codes returned by CompileMethod +// These are error codes returned by CompileMethod enum CorJitResult { - // Note that I dont use FACILITY_NULL for the facility number, - // we may want to get a 'real' facility number - CORJIT_OK = NO_ERROR, - CORJIT_BADCODE = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 1), - CORJIT_OUTOFMEM = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 2), - CORJIT_INTERNALERROR = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 3), - CORJIT_SKIPPED = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 4), - CORJIT_RECOVERABLEERROR = MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 5), - CORJIT_IMPLLIMITATION= MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 6), + CORJIT_OK = 0, + CORJIT_BADCODE = (JITINTERFACE_HRESULT)0x80000001, + CORJIT_OUTOFMEM = (JITINTERFACE_HRESULT)0x80000002, + CORJIT_INTERNALERROR = (JITINTERFACE_HRESULT)0x80000003, + CORJIT_SKIPPED = (JITINTERFACE_HRESULT)0x80000004, + CORJIT_RECOVERABLEERROR = (JITINTERFACE_HRESULT)0x80000005, + CORJIT_IMPLLIMITATION= (JITINTERFACE_HRESULT)0x80000006, }; /*****************************************************************************/ diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 05c27cf185bb61..741a019ed3afe7 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -4525,29 +4525,10 @@ class NativeExceptionHolderFactory /******************* HRESULT types ****************************************/ -#define FACILITY_WINDOWS 8 -#define FACILITY_URT 19 -#define FACILITY_UMI 22 -#define FACILITY_SXS 23 -#define FACILITY_STORAGE 3 -#define FACILITY_SSPI 9 -#define FACILITY_SCARD 16 -#define FACILITY_SETUPAPI 15 -#define FACILITY_SECURITY 9 -#define FACILITY_RPC 1 +#define FACILITY_ITF 4 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 -#define FACILITY_NULL 0 -#define FACILITY_MSMQ 14 -#define FACILITY_MEDIASERVER 13 -#define FACILITY_INTERNET 12 -#define FACILITY_ITF 4 -#define FACILITY_DPLAY 21 -#define FACILITY_DISPATCH 2 -#define FACILITY_COMPLUS 17 -#define FACILITY_CERT 11 -#define FACILITY_ACS 20 -#define FACILITY_AAF 18 +#define FACILITY_URT 19 #define NO_ERROR 0L @@ -4556,13 +4537,8 @@ class NativeExceptionHolderFactory #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) -#define IS_ERROR(Status) ((ULONG)(Status) >> 31 == SEVERITY_ERROR) // diff from win32 #define HRESULT_CODE(hr) ((hr) & 0xFFFF) -#define SCODE_CODE(sc) ((sc) & 0xFFFF) #define HRESULT_FACILITY(hr) (((hr) >> 16) & 0x1fff) -#define SCODE_FACILITY(sc) (((sc) >> 16) & 0x1fff) -#define HRESULT_SEVERITY(hr) (((hr) >> 31) & 0x1) -#define SCODE_SEVERITY(sc) (((sc) >> 31) & 0x1) // both macros diff from Win32 #define MAKE_HRESULT(sev,fac,code) \ diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index b7062fc7660354..12e2af5bd3a36a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1391,15 +1391,13 @@ public enum CorInfoTokenKind // These are error codes returned by CompileMethod public enum CorJitResult { - // Note that I dont use FACILITY_NULL for the facility number, - // we may want to get a 'real' facility number - CORJIT_OK = 0 /*NO_ERROR*/, - CORJIT_BADCODE = unchecked((int)0x80000001)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 1)*/, - CORJIT_OUTOFMEM = unchecked((int)0x80000002)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 2)*/, - CORJIT_INTERNALERROR = unchecked((int)0x80000003)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 3)*/, - CORJIT_SKIPPED = unchecked((int)0x80000004)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 4)*/, - CORJIT_RECOVERABLEERROR = unchecked((int)0x80000005)/*MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, 5)*/, - CORJIT_IMPLLIMITATION = unchecked((int)0x80000006)/*MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 6)*/, + CORJIT_OK = 0, + CORJIT_BADCODE = unchecked((int)0x80000001), + CORJIT_OUTOFMEM = unchecked((int)0x80000002), + CORJIT_INTERNALERROR = unchecked((int)0x80000003), + CORJIT_SKIPPED = unchecked((int)0x80000004), + CORJIT_RECOVERABLEERROR = unchecked((int)0x80000005), + CORJIT_IMPLLIMITATION = unchecked((int)0x80000006), }; public enum TypeCompareState From 67e198341debe77290651ce430bea09b418596d0 Mon Sep 17 00:00:00 2001 From: Mikhail Ablakatov <164922675+mikabl-arm@users.noreply.github.com> Date: Sat, 6 Jul 2024 02:49:23 +0100 Subject: [PATCH 40/72] JIT ARM64-SVE: Add Sve.ConditionalExtract* APIs (#104150) * JIT ARM64-SVE: Add Sve.ConditionalExtract* APIs * cleanup: fix formatting * Update Helpers.cs * cleanup: group and place APIs in alphabedical order and align the field * Remove redundant HasScalarInputVariant flags from *Replicate intrinsics * cleanup: place special intrinsics in alphabetical order * cleanup: correctly specify emitted instructions in the comments * fixup! cleanup: place special intrinsics in alphabetical order * fixup! cleanup: group and place APIs in alphabedical order and align the field * fixup! ARM64-SVE: GatherPrefetch (#103826) * Revert "fixup! ARM64-SVE: GatherPrefetch (#103826)" This reverts commit b0212caf190db21c54a59a48f226ca7c4eceea25. * cleanup: align the lines for the newly added tests. --------- Co-authored-by: Kunal Pathak --- src/coreclr/jit/hwintrinsic.h | 6 + src/coreclr/jit/hwintrinsicarm64.cpp | 29 + src/coreclr/jit/hwintrinsiccodegenarm64.cpp | 45 + src/coreclr/jit/hwintrinsiclistarm64sve.h | 15 +- .../Arm/Sve.PlatformNotSupported.cs | 372 ++++++++ .../src/System/Runtime/Intrinsics/Arm/Sve.cs | 372 ++++++++ .../ref/System.Runtime.Intrinsics.cs | 61 ++ .../GenerateHWIntrinsicTests_Arm.cs | 64 ++ .../HardwareIntrinsics/Arm/Shared/Helpers.cs | 882 ++++++++++++++++++ .../_SveScalarTernOpTestTemplate.template | 338 +++++++ 10 files changed, 2179 insertions(+), 5 deletions(-) create mode 100644 src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveScalarTernOpTestTemplate.template diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 610499f05eb76c..915657e0146310 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -957,6 +957,12 @@ struct HWIntrinsicInfo switch (id) { + case NI_Sve_ConditionalExtractAfterLastActiveElement: + return NI_Sve_ConditionalExtractAfterLastActiveElementScalar; + + case NI_Sve_ConditionalExtractLastActiveElement: + return NI_Sve_ConditionalExtractLastActiveElementScalar; + case NI_Sve_SaturatingDecrementBy16BitElementCount: return NI_Sve_SaturatingDecrementBy16BitElementCountScalar; diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index d69b0a9016575a..3a13fcfab7358c 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -2801,6 +2801,35 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_Sve_ConditionalExtractAfterLastActiveElementScalar: + case NI_Sve_ConditionalExtractLastActiveElementScalar: + { + assert(sig->numArgs == 3); + +#ifdef DEBUG + isValidScalarIntrinsic = true; +#endif + + CORINFO_ARG_LIST_HANDLE arg1 = sig->args; + CORINFO_ARG_LIST_HANDLE arg2 = info.compCompHnd->getArgNext(arg1); + CORINFO_ARG_LIST_HANDLE arg3 = info.compCompHnd->getArgNext(arg2); + var_types argType = TYP_UNKNOWN; + CORINFO_CLASS_HANDLE argClass = NO_CLASS_HANDLE; + + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg3, &argClass))); + op3 = getArgForHWIntrinsic(argType, argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg2, &argClass))); + op2 = getArgForHWIntrinsic(argType, argClass); + CorInfoType op2BaseJitType = getBaseJitTypeOfSIMDType(argClass); + argType = JITtype2varType(strip(info.compCompHnd->getArgType(sig, arg1, &argClass))); + op1 = getArgForHWIntrinsic(argType, argClass); + + retNode = gtNewScalarHWIntrinsicNode(retType, op1, op2, op3, intrinsic); + + retNode->AsHWIntrinsic()->SetSimdBaseJitType(simdBaseJitType); + retNode->AsHWIntrinsic()->SetAuxiliaryJitType(op2BaseJitType); + break; + } default: { diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index 4b47ad7412967c..5c7b97e3ebc073 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -2252,6 +2252,51 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case NI_Sve_ConditionalExtractAfterLastActiveElementScalar: + case NI_Sve_ConditionalExtractLastActiveElementScalar: + { + opt = emitter::optGetSveInsOpt(emitTypeSize(node->GetSimdBaseType())); + + if (emitter::isGeneralRegisterOrZR(targetReg)) + { + assert(varTypeIsIntegralOrI(intrin.baseType)); + + emitSize = emitTypeSize(node); + + if (targetReg != op2Reg) + { + assert(targetReg != op1Reg); + assert(targetReg != op3Reg); + GetEmitter()->emitIns_Mov(INS_mov, emitSize, targetReg, op2Reg, + /* canSkip */ true); + } + + GetEmitter()->emitInsSve_R_R_R(ins, emitSize, targetReg, op1Reg, op3Reg, opt, + INS_SCALABLE_OPTS_NONE); + break; + } + + // FP scalars are processed by the INS_SCALABLE_OPTS_WITH_SIMD_SCALAR variant of the instructions + FALLTHROUGH; + } + case NI_Sve_ConditionalExtractAfterLastActiveElement: + case NI_Sve_ConditionalExtractLastActiveElement: + { + assert(emitter::isFloatReg(targetReg)); + assert(varTypeIsFloating(node->gtType) || varTypeIsSIMD(node->gtType)); + + if (targetReg != op2Reg) + { + assert(targetReg != op1Reg); + assert(targetReg != op3Reg); + GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op2Reg, + /* canSkip */ true); + } + GetEmitter()->emitInsSve_R_R_R(ins, EA_SCALABLE, targetReg, op1Reg, op3Reg, opt, + INS_SCALABLE_OPTS_WITH_SIMD_SCALAR); + break; + } + default: unreached(); } diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index 9288662fd003a8..b976202ef822d6 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -41,6 +41,10 @@ HARDWARE_INTRINSIC(Sve, Compute16BitAddresses, HARDWARE_INTRINSIC(Sve, Compute32BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, Compute64BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, Compute8BitAddresses, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_adr, INS_invalid, INS_sve_adr, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, ConditionalExtractAfterLastActiveElement, -1, 3, true, {INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, ConditionalExtractAfterLastActiveElementAndReplicate, -1, 3, true, {INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, ConditionalExtractLastActiveElement, -1, 3, true, {INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, ConditionalExtractLastActiveElementAndReplicate, -1, 3, true, {INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, ConditionalSelect, -1, 3, true, {INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_SupportsContainment) HARDWARE_INTRINSIC(Sve, ConvertToInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzs, INS_sve_fcvtzs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertToInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzs, INS_sve_fcvtzs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) @@ -259,14 +263,12 @@ HARDWARE_INTRINSIC(Sve, ZipLow, // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // Special intrinsics that are generated during importing or lowering -#define SPECIAL_NI_Sve NI_Sve_ConvertMaskToVector +#define SPECIAL_NI_Sve NI_Sve_ConditionalExtractAfterLastActiveElementScalar +HARDWARE_INTRINSIC(Sve, ConditionalExtractAfterLastActiveElementScalar, 0, 3, false, {INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta, INS_sve_clasta}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, ConditionalExtractLastActiveElementScalar, 0, 3, false, {INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertMaskToVector, -1, 1, true, {INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov, INS_sve_mov}, HW_Category_Helper, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertVectorToMask, -1, 2, true, {INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne, INS_sve_cmpne}, HW_Category_Helper, HW_Flag_Scalable|HW_Flag_ReturnsPerElementMask|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, CreateTrueMaskAll, -1, -1, false, {INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue, INS_sve_ptrue}, HW_Category_Helper, HW_Flag_Scalable|HW_Flag_ReturnsPerElementMask) -HARDWARE_INTRINSIC(Sve, StoreAndZipx2, -1, 3, true, {INS_sve_st2b, INS_sve_st2b, INS_sve_st2h, INS_sve_st2h, INS_sve_st2w, INS_sve_st2w, INS_sve_st2d, INS_sve_st2d, INS_sve_st2w, INS_sve_st2d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) -HARDWARE_INTRINSIC(Sve, StoreAndZipx3, -1, 3, true, {INS_sve_st3b, INS_sve_st3b, INS_sve_st3h, INS_sve_st3h, INS_sve_st3w, INS_sve_st3w, INS_sve_st3d, INS_sve_st3d, INS_sve_st3w, INS_sve_st3d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) -HARDWARE_INTRINSIC(Sve, StoreAndZipx4, -1, 3, true, {INS_sve_st4b, INS_sve_st4b, INS_sve_st4h, INS_sve_st4h, INS_sve_st4w, INS_sve_st4w, INS_sve_st4d, INS_sve_st4d, INS_sve_st4w, INS_sve_st4d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) - // Scalar variants of Saturating*By*BitElementCount. There is 8bit versions as the generic version is scalar only. HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy16BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdech, INS_sve_uqdech, INS_sve_sqdech, INS_sve_uqdech, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy32BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqdecw, INS_sve_uqdecw, INS_sve_sqdecw, INS_sve_uqdecw, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) @@ -274,6 +276,9 @@ HARDWARE_INTRINSIC(Sve, SaturatingDecrementBy64BitElementCountScalar, HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy16BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqinch, INS_sve_uqinch, INS_sve_sqinch, INS_sve_uqinch, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy32BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincw, INS_sve_uqincw, INS_sve_sqincw, INS_sve_uqincw, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy64BitElementCountScalar, 0, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_sqincd, INS_sve_uqincd, INS_sve_sqincd, INS_sve_uqincd, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_HasRMWSemantics) +HARDWARE_INTRINSIC(Sve, StoreAndZipx2, -1, 3, true, {INS_sve_st2b, INS_sve_st2b, INS_sve_st2h, INS_sve_st2h, INS_sve_st2w, INS_sve_st2w, INS_sve_st2d, INS_sve_st2d, INS_sve_st2w, INS_sve_st2d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) +HARDWARE_INTRINSIC(Sve, StoreAndZipx3, -1, 3, true, {INS_sve_st3b, INS_sve_st3b, INS_sve_st3h, INS_sve_st3h, INS_sve_st3w, INS_sve_st3w, INS_sve_st3d, INS_sve_st3d, INS_sve_st3w, INS_sve_st3d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) +HARDWARE_INTRINSIC(Sve, StoreAndZipx4, -1, 3, true, {INS_sve_st4b, INS_sve_st4b, INS_sve_st4h, INS_sve_st4h, INS_sve_st4w, INS_sve_st4w, INS_sve_st4d, INS_sve_st4d, INS_sve_st4w, INS_sve_st4d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_NeedsConsecutiveRegisters) #endif // FEATURE_HW_INTRINSIC diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index 10318cb7240409..079541962cc9f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -1411,6 +1411,378 @@ internal Arm64() { } /// public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) { throw new PlatformNotSupportedException(); } + /// Conditionally extract element after last + + /// + /// svuint8_t svclasta[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTA Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint8_t svclasta[_n_u8](svbool_t pg, uint8_t fallback, svuint8_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe byte ConditionalExtractAfterLastActiveElement(Vector mask, byte defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svclasta[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// float64_t svclasta[_n_f64](svbool_t pg, float64_t fallback, svfloat64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe double ConditionalExtractAfterLastActiveElement(Vector mask, double defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svclasta[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTA Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int16_t svclasta[_n_s16](svbool_t pg, int16_t fallback, svint16_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe short ConditionalExtractAfterLastActiveElement(Vector mask, short defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svclasta[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int32_t svclasta[_n_s32](svbool_t pg, int32_t fallback, svint32_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe int ConditionalExtractAfterLastActiveElement(Vector mask, int defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svclasta[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svclasta[_n_s64](svbool_t pg, int64_t fallback, svint64_t data) + /// CLASTA Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe long ConditionalExtractAfterLastActiveElement(Vector mask, long defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svclasta[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTA Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int8_t svclasta[_n_s8](svbool_t pg, int8_t fallback, svint8_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe sbyte ConditionalExtractAfterLastActiveElement(Vector mask, sbyte defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svclasta[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svclasta[_n_f32](svbool_t pg, float32_t fallback, svfloat32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe float ConditionalExtractAfterLastActiveElement(Vector mask, float defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svclasta[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTA Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint16_t svclasta[_n_u16](svbool_t pg, uint16_t fallback, svuint16_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe ushort ConditionalExtractAfterLastActiveElement(Vector mask, ushort defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svclasta[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svclasta[_n_u32](svbool_t pg, uint32_t fallback, svuint32_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe uint ConditionalExtractAfterLastActiveElement(Vector mask, uint defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svclasta[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svclasta[_n_u64](svbool_t pg, uint64_t fallback, svuint64_t data) + /// CLASTA Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe ulong ConditionalExtractAfterLastActiveElement(Vector mask, ulong defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + + /// Conditionally extract element after last + + /// + /// svuint8_t svclasta[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTA Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svclasta[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svclasta[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTA Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svclasta[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svclasta[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svclasta[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTA Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svclasta[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svclasta[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTA Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svclasta[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svclasta[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) { throw new PlatformNotSupportedException(); } + + + /// Conditionally extract last element + + /// + /// svuint8_t svclastb[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTB Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint8_t svclastb[_n_u8](svbool_t pg, uint8_t fallback, svuint8_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe byte ConditionalExtractLastActiveElement(Vector mask, byte defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svclastb[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// float64_t svclastb[_n_f64](svbool_t pg, float64_t fallback, svfloat64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe double ConditionalExtractLastActiveElement(Vector mask, double defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svclastb[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTB Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int16_t svclastb[_n_s16](svbool_t pg, int16_t fallback, svint16_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe short ConditionalExtractLastActiveElement(Vector mask, short defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svclastb[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int32_t svclastb[_n_s32](svbool_t pg, int32_t fallback, svint32_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe int ConditionalExtractLastActiveElement(Vector mask, int defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svclastb[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int64_t svclastb[_n_s64](svbool_t pg, int64_t fallback, svint64_t data) + /// CLASTB Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe long ConditionalExtractLastActiveElement(Vector mask, long defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svclastb[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTB Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// int8_t svclastb[_n_s8](svbool_t pg, int8_t fallback, svint8_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe sbyte ConditionalExtractLastActiveElement(Vector mask, sbyte defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svclastb[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// float32_t svclastb[_n_f32](svbool_t pg, float32_t fallback, svfloat32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe float ConditionalExtractLastActiveElement(Vector mask, float defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svclastb[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTB Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint16_t svclastb[_n_u16](svbool_t pg, uint16_t fallback, svuint16_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe ushort ConditionalExtractLastActiveElement(Vector mask, ushort defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svclastb[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint32_t svclastb[_n_u32](svbool_t pg, uint32_t fallback, svuint32_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe uint ConditionalExtractLastActiveElement(Vector mask, uint defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svclastb[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// uint64_t svclastb[_n_u64](svbool_t pg, uint64_t fallback, svuint64_t data) + /// CLASTB Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe ulong ConditionalExtractLastActiveElement(Vector mask, ulong defaultValues, Vector data) { throw new PlatformNotSupportedException(); } + + + /// Conditionally extract last element + + /// + /// svuint8_t svclastb[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTB Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svclastb[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint16_t svclastb[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTB Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint32_t svclastb[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint64_t svclastb[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svint8_t svclastb[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTB Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svclastb[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint16_t svclastb[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTB Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint32_t svclastb[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// + /// svuint64_t svclastb[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) { throw new PlatformNotSupportedException(); } + + /// Compare equal to /// ConditionalSelect : Conditionally select elements diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index f12e1f4d48ba29..ae978c4362f24d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -1441,6 +1441,378 @@ internal Arm64() { } /// public static unsafe Vector Compute8BitAddresses(Vector bases, Vector indices) => Compute8BitAddresses(bases, indices); + /// Conditionally extract element after last + + /// + /// svuint8_t svclasta[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTA Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// uint8_t svclasta[_n_u8](svbool_t pg, uint8_t fallback, svuint8_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe byte ConditionalExtractAfterLastActiveElement(Vector mask, byte defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svfloat64_t svclasta[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// float64_t svclasta[_n_f64](svbool_t pg, float64_t fallback, svfloat64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe double ConditionalExtractAfterLastActiveElement(Vector mask, double defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svint16_t svclasta[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTA Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// int16_t svclasta[_n_s16](svbool_t pg, int16_t fallback, svint16_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe short ConditionalExtractAfterLastActiveElement(Vector mask, short defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svint32_t svclasta[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// int32_t svclasta[_n_s32](svbool_t pg, int32_t fallback, svint32_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe int ConditionalExtractAfterLastActiveElement(Vector mask, int defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svint64_t svclasta[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// int64_t svclasta[_n_s64](svbool_t pg, int64_t fallback, svint64_t data) + /// CLASTA Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe long ConditionalExtractAfterLastActiveElement(Vector mask, long defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svint8_t svclasta[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTA Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// int8_t svclasta[_n_s8](svbool_t pg, int8_t fallback, svint8_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe sbyte ConditionalExtractAfterLastActiveElement(Vector mask, sbyte defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svfloat32_t svclasta[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// float32_t svclasta[_n_f32](svbool_t pg, float32_t fallback, svfloat32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe float ConditionalExtractAfterLastActiveElement(Vector mask, float defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svuint16_t svclasta[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTA Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// uint16_t svclasta[_n_u16](svbool_t pg, uint16_t fallback, svuint16_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe ushort ConditionalExtractAfterLastActiveElement(Vector mask, ushort defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svuint32_t svclasta[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTA Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// uint32_t svclasta[_n_u32](svbool_t pg, uint32_t fallback, svuint32_t data) + /// CLASTA Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe uint ConditionalExtractAfterLastActiveElement(Vector mask, uint defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + /// + /// svuint64_t svclasta[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTA Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValue, data); + + /// + /// uint64_t svclasta[_n_u64](svbool_t pg, uint64_t fallback, svuint64_t data) + /// CLASTA Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe ulong ConditionalExtractAfterLastActiveElement(Vector mask, ulong defaultValues, Vector data) => ConditionalExtractAfterLastActiveElement(mask, defaultValues, data); + + + /// Conditionally extract element after last + + /// + /// svuint8_t svclasta[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTA Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svfloat64_t svclasta[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svint16_t svclasta[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTA Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svint32_t svclasta[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svint64_t svclasta[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svint8_t svclasta[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTA Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svfloat32_t svclasta[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svuint16_t svclasta[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTA Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svuint32_t svclasta[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTA Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + /// + /// svuint64_t svclasta[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTA Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractAfterLastActiveElementAndReplicate(Vector mask, Vector defaultScalar, Vector data) => ConditionalExtractAfterLastActiveElementAndReplicate(mask, defaultScalar, data); + + + /// Conditionally extract last element + + /// + /// svuint8_t svclastb[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTB Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// uint8_t svclastb[_n_u8](svbool_t pg, uint8_t fallback, svuint8_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe byte ConditionalExtractLastActiveElement(Vector mask, byte defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svfloat64_t svclastb[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// float64_t svclastb[_n_f64](svbool_t pg, float64_t fallback, svfloat64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe double ConditionalExtractLastActiveElement(Vector mask, double defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svint16_t svclastb[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTB Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// int16_t svclastb[_n_s16](svbool_t pg, int16_t fallback, svint16_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe short ConditionalExtractLastActiveElement(Vector mask, short defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svint32_t svclastb[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// int32_t svclastb[_n_s32](svbool_t pg, int32_t fallback, svint32_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe int ConditionalExtractLastActiveElement(Vector mask, int defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svint64_t svclastb[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// int64_t svclastb[_n_s64](svbool_t pg, int64_t fallback, svint64_t data) + /// CLASTB Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe long ConditionalExtractLastActiveElement(Vector mask, long defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svint8_t svclastb[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTB Btied, Pg, Btied, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// int8_t svclastb[_n_s8](svbool_t pg, int8_t fallback, svint8_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.B + /// + public static unsafe sbyte ConditionalExtractLastActiveElement(Vector mask, sbyte defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svfloat32_t svclastb[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// float32_t svclastb[_n_f32](svbool_t pg, float32_t fallback, svfloat32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe float ConditionalExtractLastActiveElement(Vector mask, float defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svuint16_t svclastb[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTB Htied, Pg, Htied, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// uint16_t svclastb[_n_u16](svbool_t pg, uint16_t fallback, svuint16_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.H + /// + public static unsafe ushort ConditionalExtractLastActiveElement(Vector mask, ushort defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svuint32_t svclastb[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTB Stied, Pg, Stied, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// uint32_t svclastb[_n_u32](svbool_t pg, uint32_t fallback, svuint32_t data) + /// CLASTB Wtied, Pg, Wtied, Zdata.S + /// + public static unsafe uint ConditionalExtractLastActiveElement(Vector mask, uint defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + /// + /// svuint64_t svclastb[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTB Dtied, Pg, Dtied, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElement(Vector mask, Vector defaultValue, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValue, data); + + /// + /// uint64_t svclastb[_n_u64](svbool_t pg, uint64_t fallback, svuint64_t data) + /// CLASTB Xtied, Pg, Xtied, Zdata.D + /// + public static unsafe ulong ConditionalExtractLastActiveElement(Vector mask, ulong defaultValues, Vector data) => ConditionalExtractLastActiveElement(mask, defaultValues, data); + + + /// Conditionally extract last element + + /// + /// svuint8_t svclastb[_u8](svbool_t pg, svuint8_t fallback, svuint8_t data) + /// CLASTB Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svfloat64_t svclastb[_f64](svbool_t pg, svfloat64_t fallback, svfloat64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svint16_t svclastb[_s16](svbool_t pg, svint16_t fallback, svint16_t data) + /// CLASTB Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svint32_t svclastb[_s32](svbool_t pg, svint32_t fallback, svint32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svint64_t svclastb[_s64](svbool_t pg, svint64_t fallback, svint64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svint8_t svclastb[_s8](svbool_t pg, svint8_t fallback, svint8_t data) + /// CLASTB Ztied.B, Pg, Ztied.B, Zdata.B + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svfloat32_t svclastb[_f32](svbool_t pg, svfloat32_t fallback, svfloat32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svuint16_t svclastb[_u16](svbool_t pg, svuint16_t fallback, svuint16_t data) + /// CLASTB Ztied.H, Pg, Ztied.H, Zdata.H + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svuint32_t svclastb[_u32](svbool_t pg, svuint32_t fallback, svuint32_t data) + /// CLASTB Ztied.S, Pg, Ztied.S, Zdata.S + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// + /// svuint64_t svclastb[_u64](svbool_t pg, svuint64_t fallback, svuint64_t data) + /// CLASTB Ztied.D, Pg, Ztied.D, Zdata.D + /// + public static unsafe Vector ConditionalExtractLastActiveElementAndReplicate(Vector mask, Vector fallback, Vector data) => ConditionalExtractLastActiveElementAndReplicate(mask, fallback, data); + + /// Compare equal to /// ConditionalSelect : Conditionally select elements diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index f4885977916aeb..3df9a05b405ba2 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -4412,6 +4412,67 @@ internal Arm64() { } public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } public static System.Numerics.Vector Compute8BitAddresses(System.Numerics.Vector bases, System.Numerics.Vector indices) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static byte ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, byte defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static double ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, double defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static short ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, short defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static int ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, int defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static long ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, long defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static sbyte ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, sbyte defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static float ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, float defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static ushort ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, ushort defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static uint ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, uint defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static ulong ConditionalExtractAfterLastActiveElement(System.Numerics.Vector mask, ulong defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractAfterLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector defaultScalar, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static byte ConditionalExtractLastActiveElement(System.Numerics.Vector mask, byte defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static double ConditionalExtractLastActiveElement(System.Numerics.Vector mask, double defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static short ConditionalExtractLastActiveElement(System.Numerics.Vector mask, short defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static int ConditionalExtractLastActiveElement(System.Numerics.Vector mask, int defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static long ConditionalExtractLastActiveElement(System.Numerics.Vector mask, long defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static sbyte ConditionalExtractLastActiveElement(System.Numerics.Vector mask, sbyte defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static float ConditionalExtractLastActiveElement(System.Numerics.Vector mask, float defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static ushort ConditionalExtractLastActiveElement(System.Numerics.Vector mask, ushort defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static uint ConditionalExtractLastActiveElement(System.Numerics.Vector mask, uint defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElement(System.Numerics.Vector mask, System.Numerics.Vector defaultValue, System.Numerics.Vector data) { throw null; } + public static ulong ConditionalExtractLastActiveElement(System.Numerics.Vector mask, ulong defaultValues, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalExtractLastActiveElementAndReplicate(System.Numerics.Vector mask, System.Numerics.Vector fallback, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 1caaf29a9d226d..7455aebc0ad324 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -58,6 +58,8 @@ const string SimpleVecOpTest_VectorValidationLogic = @"succeeded = !({ValidateVectorResult});"; +const string SimpleScalarOpTest_ValidationLogic = @"succeeded = !({ValidateScalarResult});"; + const string SimpleTernVecOpTest_ValidationLogic = @"for (var i = 0; i < RetElementCount; i++) { if ({ValidateIterResult}) @@ -284,6 +286,7 @@ ("_SveImmTernOpTestTemplate.template", "SveVecImmTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue }), ("_SveTernOpMaskedOpTestTemplate.template", "SveVecTernOpMaskedTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue }), ("_SveImmTernOpFirstArgTestTemplate.template", "SveVecImmTernOpFirstArgTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic, ["TemplateValidationLogicForCndSel"] = SimpleTernVecOpTest_ValidationLogicForCndSel, ["TemplateValidationLogicForCndSel_FalseValue"] = SimpleTernVecOpTest_ValidationLogicForCndSel_FalseValue }), + ("_SveScalarTernOpTestTemplate.template", "SveScalarTernOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleScalarOpTest_ValidationLogic }), ("_SveImm2UnaryOpTestTemplate.template", "SveVecImm2UnOpTest.template", new Dictionary { ["TemplateName"] = "Imm", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), ("_SveMinimalUnaryOpTestTemplate.template", "SveVecReduceUnOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = VecReduceOpTest_ValidationLogic }), ("_SveMasklessUnaryOpTestTemplate.template", "SveMasklessSimpleVecOpTest.template", new Dictionary { ["TemplateName"] = "Simple", ["TemplateValidationLogic"] = SimpleVecOpTest_ValidationLogic }), @@ -4176,6 +4179,67 @@ ("SveVecReduceUnOpTest.template",new Dictionary {["TestName"] = "Sve_ZeroExtendWideningUpper_uint_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZeroExtendWideningUpper", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ValidateReduceOpResult"] = "Helpers.ZeroExtendWideningUpper(firstOp, 0) != result[0]", ["ValidateRemainingResults"] = "Helpers.ZeroExtendWideningUpper(firstOp, i) != result[i]"}), ("SveVecReduceUnOpTest.template",new Dictionary {["TestName"] = "Sve_ZeroExtendWideningUpper_ulong_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZeroExtendWideningUpper", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ValidateReduceOpResult"] = "Helpers.ZeroExtendWideningUpper(firstOp, 0) != result[0]", ["ValidateRemainingResults"] = "Helpers.ZeroExtendWideningUpper(firstOp, i) != result[i]"}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_float_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_double_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_sbyte_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_short_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_int_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_long_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_byte_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_ushort_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_uint_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElement_ulong_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElement", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractAfterLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractAfterLastActiveElementAndReplicate_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractAfterLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractAfterLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_float_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_double_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_sbyte_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_short_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_int_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_long_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_byte_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_ushort_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_uint_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElement(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveScalarTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElement_ulong_scalar", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElement", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateScalarResult"] = "Helpers.ConditionalExtractLastActiveElement(firstOp, secondOp, thirdOp) != result",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_short", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_int", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_long", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Int64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_byte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Byte", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "Byte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_ushort", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt16", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt16", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_uint", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt32", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecTernOpTest.template", new Dictionary { ["TestName"] = "Sve_ConditionalExtractLastActiveElementAndReplicate_ulong", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ConditionalExtractLastActiveElementAndReplicate", ["RetVectorType"] = "Vector", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "UInt64", ["Op3VectorType"] = "Vector", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(firstOp, secondOp, thirdOp)[i] != result[i]", ["GetIterResult"] = "Helpers.ConditionalExtractLastActiveElementAndReplicate(first, second, third)[i]", ["ConvertFunc"] = " ",}), + ("SveVecPairBinOpTest.template", new Dictionary { ["TestName"] = "SveZipHigh_float", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZipHigh", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateEntry"] = "result[i] != left[index + half] || result[i + 1] != right[index + half]"}), ("SveVecPairBinOpTest.template", new Dictionary { ["TestName"] = "SveZipHigh_double", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZipHigh", ["RetVectorType"] = "Vector", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateEntry"] = "result[i] != left[index + half] || result[i + 1] != right[index + half]"}), ("SveVecPairBinOpTest.template", new Dictionary { ["TestName"] = "SveZipHigh_sbyte", ["Isa"] = "Sve", ["LoadIsa"] = "Sve", ["Method"] = "ZipHigh", ["RetVectorType"] = "Vector", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateEntry"] = "result[i] != left[index + half] || result[i + 1] != right[index + half]"}), diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index ebffa0504f7445..1c9268fa84e3ef 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -168,6 +168,30 @@ public static int LastActiveElement(byte[] v) return -1; } + public static int LastActiveElement(sbyte[] v) + { + for (var i = v.Length - 1; i >= 0; i--) + { + if (v[i] != 0) + { + return i; + } + } + return -1; + } + + public static int LastActiveElement(short[] v) + { + for (var i = v.Length - 1; i >= 0; i--) + { + if (v[i] != 0) + { + return i; + } + } + return -1; + } + public static int LastActiveElement(ushort[] v) { for (var i = v.Length - 1; i >= 0; i--) @@ -180,6 +204,18 @@ public static int LastActiveElement(ushort[] v) return -1; } + public static int LastActiveElement(int[] v) + { + for (var i = v.Length - 1; i >= 0; i--) + { + if (v[i] != 0) + { + return i; + } + } + return -1; + } + public static int LastActiveElement(uint[] v) { for (var i = v.Length - 1; i >= 0; i--) @@ -192,6 +228,18 @@ public static int LastActiveElement(uint[] v) return -1; } + public static int LastActiveElement(long[] v) + { + for (var i = v.Length - 1; i >= 0; i--) + { + if (v[i] != 0) + { + return i; + } + } + return -1; + } + public static int LastActiveElement(ulong[] v) { for (var i = v.Length - 1; i >= 0; i--) @@ -204,6 +252,30 @@ public static int LastActiveElement(ulong[] v) return -1; } + private static int LastActiveElement(float[] v) + { + for (int i = v.Length - 1; i >= 0; i--) + { + if (Unsafe.BitCast(v[i]) != 0) + { + return i; + } + } + return -1; + } + + private static int LastActiveElement(double[] v) + { + for (int i = v.Length - 1; i >= 0; i--) + { + if (Unsafe.BitCast(v[i]) != 0) + { + return i; + } + } + return -1; + } + public static byte[] CreateMaskForNextActiveElement(byte[] mask, byte[] srcMask) { var count = srcMask.Length; @@ -7889,5 +7961,815 @@ public static T[] CreateBreakBeforePropagateMask(T[] mask, T[] op1, T[] op2) } return result; } + + private static byte ConditionalExtract(byte[] op1, byte op2, byte[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static byte[] ConditionalExtract(byte[] op1, byte[] op2, byte[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + byte[] result = new byte[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static byte ConditionalExtractAfterLastActiveElement(byte[] op1, byte op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static byte ConditionalExtractLastActiveElement(byte[] op1, byte op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static byte[] ConditionalExtractAfterLastActiveElement(byte[] op1, byte[] op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static byte[] ConditionalExtractAfterLastActiveElementAndReplicate(byte[] op1, byte[] op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static byte[] ConditionalExtractLastActiveElement(byte[] op1, byte[] op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static byte[] ConditionalExtractLastActiveElementAndReplicate(byte[] op1, byte[] op2, byte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static sbyte ConditionalExtract(sbyte[] op1, sbyte op2, sbyte[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static sbyte[] ConditionalExtract(sbyte[] op1, sbyte[] op2, sbyte[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + sbyte[] result = new sbyte[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static sbyte ConditionalExtractAfterLastActiveElement(sbyte[] op1, sbyte op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static sbyte ConditionalExtractLastActiveElement(sbyte[] op1, sbyte op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static sbyte[] ConditionalExtractAfterLastActiveElement(sbyte[] op1, sbyte[] op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static sbyte[] ConditionalExtractAfterLastActiveElementAndReplicate(sbyte[] op1, sbyte[] op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static sbyte[] ConditionalExtractLastActiveElement(sbyte[] op1, sbyte[] op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static sbyte[] ConditionalExtractLastActiveElementAndReplicate(sbyte[] op1, sbyte[] op2, sbyte[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static short ConditionalExtract(short[] op1, short op2, short[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static short[] ConditionalExtract(short[] op1, short[] op2, short[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + short[] result = new short[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static short ConditionalExtractAfterLastActiveElement(short[] op1, short op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static short ConditionalExtractLastActiveElement(short[] op1, short op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static short[] ConditionalExtractAfterLastActiveElement(short[] op1, short[] op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static short[] ConditionalExtractAfterLastActiveElementAndReplicate(short[] op1, short[] op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static short[] ConditionalExtractLastActiveElement(short[] op1, short[] op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static short[] ConditionalExtractLastActiveElementAndReplicate(short[] op1, short[] op2, short[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static ushort ConditionalExtract(ushort[] op1, ushort op2, ushort[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static ushort[] ConditionalExtract(ushort[] op1, ushort[] op2, ushort[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + ushort[] result = new ushort[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static ushort ConditionalExtractAfterLastActiveElement(ushort[] op1, ushort op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static ushort ConditionalExtractLastActiveElement(ushort[] op1, ushort op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static ushort[] ConditionalExtractAfterLastActiveElement(ushort[] op1, ushort[] op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static ushort[] ConditionalExtractAfterLastActiveElementAndReplicate(ushort[] op1, ushort[] op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static ushort[] ConditionalExtractLastActiveElement(ushort[] op1, ushort[] op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static ushort[] ConditionalExtractLastActiveElementAndReplicate(ushort[] op1, ushort[] op2, ushort[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static int ConditionalExtract(int[] op1, int op2, int[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static int[] ConditionalExtract(int[] op1, int[] op2, int[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int[] result = new int[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static int ConditionalExtractAfterLastActiveElement(int[] op1, int op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static int ConditionalExtractLastActiveElement(int[] op1, int op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static int[] ConditionalExtractAfterLastActiveElement(int[] op1, int[] op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static int[] ConditionalExtractAfterLastActiveElementAndReplicate(int[] op1, int[] op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static int[] ConditionalExtractLastActiveElement(int[] op1, int[] op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static int[] ConditionalExtractLastActiveElementAndReplicate(int[] op1, int[] op2, int[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static uint ConditionalExtract(uint[] op1, uint op2, uint[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static uint[] ConditionalExtract(uint[] op1, uint[] op2, uint[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + uint[] result = new uint[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static uint ConditionalExtractAfterLastActiveElement(uint[] op1, uint op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static uint ConditionalExtractLastActiveElement(uint[] op1, uint op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static uint[] ConditionalExtractAfterLastActiveElement(uint[] op1, uint[] op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static uint[] ConditionalExtractAfterLastActiveElementAndReplicate(uint[] op1, uint[] op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static uint[] ConditionalExtractLastActiveElement(uint[] op1, uint[] op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static uint[] ConditionalExtractLastActiveElementAndReplicate(uint[] op1, uint[] op2, uint[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static long ConditionalExtract(long[] op1, long op2, long[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static long[] ConditionalExtract(long[] op1, long[] op2, long[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + long[] result = new long[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static long ConditionalExtractAfterLastActiveElement(long[] op1, long op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static long ConditionalExtractLastActiveElement(long[] op1, long op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static long[] ConditionalExtractAfterLastActiveElement(long[] op1, long[] op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static long[] ConditionalExtractAfterLastActiveElementAndReplicate(long[] op1, long[] op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static long[] ConditionalExtractLastActiveElement(long[] op1, long[] op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static long[] ConditionalExtractLastActiveElementAndReplicate(long[] op1, long[] op2, long[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + public static ulong ConditionalExtractAfterLastActiveElement(ulong[] op1, ulong op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + private static ulong ConditionalExtract(ulong[] op1, ulong op2, ulong[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static ulong[] ConditionalExtract(ulong[] op1, ulong[] op2, ulong[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + ulong[] result = new ulong[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static ulong ConditionalExtractLastActiveElement(ulong[] op1, ulong op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static ulong[] ConditionalExtractAfterLastActiveElement(ulong[] op1, ulong[] op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static ulong[] ConditionalExtractAfterLastActiveElementAndReplicate(ulong[] op1, ulong[] op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static ulong[] ConditionalExtractLastActiveElement(ulong[] op1, ulong[] op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static ulong[] ConditionalExtractLastActiveElementAndReplicate(ulong[] op1, ulong[] op2, ulong[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static float ConditionalExtract(float[] op1, float op2, float[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static float[] ConditionalExtract(float[] op1, float[] op2, float[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + float[] result = new float[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static float ConditionalExtractAfterLastActiveElement(float[] op1, float op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static float ConditionalExtractLastActiveElement(float[] op1, float op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static float[] ConditionalExtractAfterLastActiveElement(float[] op1, float[] op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static float[] ConditionalExtractAfterLastActiveElementAndReplicate(float[] op1, float[] op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static float[] ConditionalExtractLastActiveElement(float[] op1, float[] op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static float[] ConditionalExtractLastActiveElementAndReplicate(float[] op1, float[] op2, float[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } + + private static double ConditionalExtract(double[] op1, double op2, double[] op3, bool after) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + return op3[pos]; + } + + private static double[] ConditionalExtract(double[] op1, double[] op2, double[] op3, bool after, bool replicate) + { + int last = LastActiveElement(op1); + if (last < 0) + { + return op2; + } + + double[] result = new double[op1.Length]; + int pos = last; + if (after) + { + pos++; + if (pos == op1.Length) { + pos = 0; + } + } + + if (replicate) + { + Array.Fill(result, op3[pos]); + } + else + { + Array.Fill(result, 0, 0, op1.Length); + result[0] = op3[pos]; + } + + return result; + } + + public static double ConditionalExtractAfterLastActiveElement(double[] op1, double op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true); + } + + public static double ConditionalExtractLastActiveElement(double[] op1, double op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false); + } + + public static double[] ConditionalExtractAfterLastActiveElement(double[] op1, double[] op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ false); + } + + public static double[] ConditionalExtractAfterLastActiveElementAndReplicate(double[] op1, double[] op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ true, /* replicate = */ true); + } + + public static double[] ConditionalExtractLastActiveElement(double[] op1, double[] op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ false); + } + + public static double[] ConditionalExtractLastActiveElementAndReplicate(double[] op1, double[] op2, double[] op3) + { + return ConditionalExtract(op1, op2, op3, /* after = */ false, /* replicate = */ true); + } } } diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveScalarTernOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveScalarTernOpTestTemplate.template new file mode 100644 index 00000000000000..5b21288c8dc749 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveScalarTernOpTestTemplate.template @@ -0,0 +1,338 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; +using Xunit; + +namespace JIT.HardwareIntrinsics.Arm +{ + public static partial class Program + { + [Fact] + public static void {TestName}() + { + var test = new {TemplateName}TernaryOpTest__{TestName}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class {TemplateName}TernaryOpTest__{TestName} + { + private struct DataTable + { + private byte[] inArray1; + public {Op2BaseType} inScalar2; + private byte[] inArray3; + + private GCHandle inHandle1; + private GCHandle inHandle2; + private GCHandle inHandle3; + private GCHandle outHandle; + + private ulong alignment; + + public DataTable({Op1BaseType}[] inArray1, {Op2BaseType} inScalar2, {Op3BaseType}[] inArray3, int alignment) + { + int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf<{Op1BaseType}>(); + int sizeOfinArray3 = inArray3.Length * Unsafe.SizeOf<{Op3BaseType}>(); + if ((alignment != 64 && alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray3) + { + throw new ArgumentException("Invalid value of alignment"); + } + + this.inArray1 = new byte[alignment * 2]; + this.inArray3 = new byte[alignment * 2]; + + this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned); + this.inHandle3 = GCHandle.Alloc(this.inArray3, GCHandleType.Pinned); + + this.alignment = (ulong)alignment; + + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), (uint)sizeOfinArray1); + Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray3Ptr), ref Unsafe.As<{Op3BaseType}, byte>(ref inArray3[0]), (uint)sizeOfinArray3); + } + + public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment); + public void* inArray3Ptr => Align((byte*)(inHandle3.AddrOfPinnedObject().ToPointer()), alignment); + + public void Dispose() + { + inHandle1.Free(); + inHandle3.Free(); + } + + private static unsafe void* Align(byte* buffer, ulong expectedAlignment) + { + return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1)); + } + } + + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op2BaseType} _fld2; + public {Op1VectorType}<{Op3BaseType}> _fld3; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + _data2 = {NextValueOp2}; + testStruct._fld2 = _data2; + for (var i = 0; i < Op1ElementCount; i++) { _data3[i] = {NextValueOp3}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op3BaseType}>, byte>(ref testStruct._fld3), ref Unsafe.As<{Op3BaseType}, byte>(ref _data3[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op3BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario({TemplateName}TernaryOpTest__{TestName} testClass) + { + {RetBaseType} result = {Isa}.{Method}(_fld1, _fld2, _fld3); + + testClass.ValidateResult(_fld1, _fld2, _fld3, result); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int Op3ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op3BaseType}>>() / sizeof({Op3BaseType}); + + private static {Op1BaseType}[] _maskData = new {Op1BaseType}[Op1ElementCount]; + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType} _data2; + private static {Op3BaseType}[] _data3 = new {Op3BaseType}[Op3ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _mask; + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2BaseType} _fld2; + private {Op1VectorType}<{Op3BaseType}> _fld3; + private {Op1VectorType}<{Op1BaseType}> _falseFld; + + private DataTable _dataTable; + + public {TemplateName}TernaryOpTest__{TestName}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _mask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + _data2 = {NextValueOp2}; + _fld2 = _data2; + for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = {NextValueOp3}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op3BaseType}>, byte>(ref _fld3), ref Unsafe.As<{Op3BaseType}, byte>(ref _data3[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op3BaseType}>>()); + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _falseFld), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } + _data2 = {NextValueOp2}; + for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = {NextValueOp3}; } + _dataTable = new DataTable(_data1, _data2, _data3, LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + {RetBaseType} result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + _dataTable.inScalar2, + Unsafe.Read<{Op1VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr) + ); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inScalar2, _dataTable.inArray3Ptr, result); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + {Op1VectorType}<{Op1BaseType}> loadMask1 = Sve.CreateTrueMask{Op1BaseType}(SveMaskPattern.All); + {Op1VectorType}<{Op3BaseType}> loadMask3 = Sve.CreateTrueMask{Op3BaseType}(SveMaskPattern.All); + + {RetBaseType} result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(loadMask1, ({Op1BaseType}*)(_dataTable.inArray1Ptr)), + _dataTable.inScalar2, + {LoadIsa}.Load{Op1VectorType}(loadMask3, ({Op3BaseType}*)(_dataTable.inArray3Ptr)) + ); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inScalar2, _dataTable.inArray3Ptr, result); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + {RetBaseType} result = ({RetBaseType}) typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2BaseType}), typeof({Op1VectorType}<{Op3BaseType}>) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + _dataTable.inScalar2, + Unsafe.Read<{Op1VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr) + }); + + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inScalar2, _dataTable.inArray3Ptr, result); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var op2 = _dataTable.inScalar2; + var op3 = Unsafe.Read<{Op1VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr); + {RetBaseType} result = {Isa}.{Method}(op1, op2, op3); + + ValidateResult(op1, op2, op3, result); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + {RetBaseType} result = {Isa}.{Method}(_fld1, _fld2, _fld3); + + ValidateResult(_fld1, _fld2, _fld3, result); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + {RetBaseType} result = {Isa}.{Method}(test._fld1, test._fld2, test._fld3); + + ValidateResult(test._fld1, test._fld2, test._fld3, result); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, {Op2BaseType} op2, {Op1VectorType}<{Op3BaseType}> op3, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType} inScalar2; + {Op3BaseType}[] inArray3 = new {Op3BaseType}[Op1ElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + inScalar2 = op2; + Unsafe.WriteUnaligned(ref Unsafe.As<{Op3BaseType}, byte>(ref inArray3[0]), op3); + + ValidateResult(inArray1, inScalar2, inArray3, result, method); + } + + private void ValidateResult(void* op1, {Op2BaseType} op2, void* op3, {RetBaseType} result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType} inScalar2; + {Op3BaseType}[] inArray3 = new {Op3BaseType}[Op1ElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + inScalar2 = op2; + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op3BaseType}, byte>(ref inArray3[0]), ref Unsafe.AsRef(op3), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op3BaseType}>>()); + + ValidateResult(inArray1, inScalar2, inArray3, result, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {Op2BaseType} secondOp, {Op3BaseType}[] thirdOp, {RetBaseType} result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + {TemplateValidationLogic} + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}({RetBaseType})({Op1VectorType}<{Op1BaseType}>, {Op2BaseType}, {Op1VectorType}<{Op3BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($"secondOp: ({string.Join(", ", secondOp)})"); + TestLibrary.TestFramework.LogInformation($" thirdOp: ({string.Join(", ", thirdOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + } +} From aee78d306a530d9ff68a587daa9f146733621847 Mon Sep 17 00:00:00 2001 From: Sun Lijun Date: Sat, 6 Jul 2024 09:54:38 +0800 Subject: [PATCH 41/72] [LoongArch64] Add nativeaot support on LoongArch64. (#103889) * [LoongArch64] Add nativeaot support on LoongArch64. --------- Co-authored-by: Filip Navara Co-authored-by: Jan Kotas --- eng/Subsets.props | 2 +- src/coreclr/CMakeLists.txt | 2 +- .../src/Internal/Runtime/TransitionBlock.cs | 74 +++++ src/coreclr/nativeaot/Directory.Build.props | 3 + src/coreclr/nativeaot/Runtime/CommonMacros.h | 5 + src/coreclr/nativeaot/Runtime/EHHelpers.cpp | 71 ++--- src/coreclr/nativeaot/Runtime/ICodeManager.h | 21 ++ src/coreclr/nativeaot/Runtime/MiscHelpers.cpp | 35 +++ src/coreclr/nativeaot/Runtime/PalRedhawk.h | 82 +++++ .../nativeaot/Runtime/PalRedhawkCommon.h | 31 ++ .../nativeaot/Runtime/StackFrameIterator.cpp | 212 ++++++++++++- .../nativeaot/Runtime/StackFrameIterator.h | 11 + .../nativeaot/Runtime/ThunksMapping.cpp | 24 ++ src/coreclr/nativeaot/Runtime/gcenv.ee.cpp | 4 +- .../nativeaot/Runtime/inc/TargetPtrs.h | 2 + src/coreclr/nativeaot/Runtime/inc/rhbinder.h | 65 ++++ src/coreclr/nativeaot/Runtime/regdisplay.h | 58 +++- .../nativeaot/Runtime/unix/UnixContext.cpp | 171 +++++++++- .../nativeaot/Runtime/unix/UnixContext.h | 55 ++++ .../Runtime/unix/UnixNativeCodeManager.cpp | 150 ++++++++- .../nativeaot/Runtime/unix/UnwindHelpers.cpp | 296 ++++++++++++++++++ .../TypeLoader/TypeSystemContextFactory.cs | 2 + .../LoongArch64ReadyToRunHelperNode.cs | 1 + .../ILCompiler.Compiler/Compiler/JitHelper.cs | 11 +- .../include/__libunwind_config.h | 4 +- .../external/llvm-libunwind/src/Registers.hpp | 43 ++- 26 files changed, 1359 insertions(+), 76 deletions(-) diff --git a/eng/Subsets.props b/eng/Subsets.props index 70d16e724cae4d..5aac9db362e7c4 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -120,7 +120,7 @@ <_NativeAotSupportedOS Condition="'$(TargetOS)' == 'windows' or '$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'freebsd'">true - <_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true + <_NativeAotSupportedArch Condition="'$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm' or '$(TargetArchitecture)' == 'loongarch64' or ('$(TargetOS)' == 'windows' and '$(TargetArchitecture)' == 'x86')">true true true diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 808aac13e22cd1..2e9a6c76743e5f 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -150,7 +150,7 @@ add_subdirectory(tools/aot/jitinterface) if(NOT CLR_CROSS_COMPONENTS_BUILD) # NativeAOT only buildable for a subset of CoreCLR-supported configurations - if(CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CMAKE_HOST_ARCH_ARM OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_HOST_WIN32)) + if(CLR_CMAKE_HOST_ARCH_ARM64 OR CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CMAKE_HOST_ARCH_ARM OR CLR_CMAKE_HOST_ARCH_LOONGARCH64 OR (CLR_CMAKE_HOST_ARCH_I386 AND CLR_CMAKE_HOST_WIN32)) add_subdirectory(nativeaot) endif() endif(NOT CLR_CROSS_COMPONENTS_BUILD) diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs index 98126202f1164a..ccff78114d4e9a 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/TransitionBlock.cs @@ -35,6 +35,12 @@ #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE #define ENREGISTERED_PARAMTYPE_MAXSIZE #elif TARGET_WASM +#elif TARGET_LOONGARCH64 +#define CALLDESCR_ARGREGS // CallDescrWorker has ArgumentRegister parameter +#define CALLDESCR_FPARGREGS // CallDescrWorker has FloatArgumentRegisters parameter +#define ENREGISTERED_RETURNTYPE_MAXSIZE +#define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE +#define ENREGISTERED_PARAMTYPE_MAXSIZE #else #error Unknown architecture! #endif @@ -300,6 +306,60 @@ internal struct ArchitectureConstants public const int STACK_ELEM_SIZE = 4; public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } } +#elif TARGET_LOONGARCH64 + [StructLayout(LayoutKind.Sequential)] + internal struct ReturnBlock + { + private IntPtr returnValue; + private IntPtr returnValue2; + private IntPtr returnValue3; + private IntPtr returnValue4; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ArgumentRegisters + { + private IntPtr r4; + private IntPtr r5; + private IntPtr r6; + private IntPtr r7; + private IntPtr r8; + private IntPtr r9; + private IntPtr r10; + private IntPtr r11; + public static unsafe int GetOffsetOfr11() + { + return sizeof(IntPtr) * 7; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FloatArgumentRegisters + { + private double f0; + private double f1; + private double f2; + private double f3; + private double f4; + private double f5; + private double f6; + private double f7; + } + + internal struct ArchitectureConstants + { + // To avoid corner case bugs, limit maximum size of the arguments with sufficient margin + public const int MAX_ARG_SIZE = 0xFFFFFF; + + public const int NUM_ARGUMENT_REGISTERS = 8; + public const int ARGUMENTREGISTERS_SIZE = NUM_ARGUMENT_REGISTERS * 8; + public const int ENREGISTERED_RETURNTYPE_MAXSIZE = 32; // bytes (four FP registers: d0,d1,d2 and d3) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE = 16; // bytes (two int registers: x0 and x1) + public const int ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE_PRIMITIVE = 8; + public const int ENREGISTERED_PARAMTYPE_MAXSIZE = 16; // bytes (max value type size that can be passed by value) + public const int STACK_ELEM_SIZE = 8; + public static int StackElemSize(int size) { return (((size) + STACK_ELEM_SIZE - 1) & ~(STACK_ELEM_SIZE - 1)); } + } #endif // @@ -392,6 +452,20 @@ public static unsafe int GetOffsetOfArgumentRegisters() { return sizeof(ReturnBlock); } +#elif TARGET_LOONGARCH64 + public ReturnBlock m_returnBlock; + public static unsafe int GetOffsetOfReturnValuesBlock() + { + return 0; + } + + public ArgumentRegisters m_argumentRegisters; + public static unsafe int GetOffsetOfArgumentRegisters() + { + return sizeof(ReturnBlock); + } + + public IntPtr m_alignmentPad; #else #error Portability problem #endif diff --git a/src/coreclr/nativeaot/Directory.Build.props b/src/coreclr/nativeaot/Directory.Build.props index b06c29baeb43e6..c01756cfc8aba2 100644 --- a/src/coreclr/nativeaot/Directory.Build.props +++ b/src/coreclr/nativeaot/Directory.Build.props @@ -89,6 +89,9 @@ TARGET_64BIT;TARGET_ARM64;$(DefineConstants) + + TARGET_64BIT;TARGET_LOONGARCH64;$(DefineConstants) + TARGET_WINDOWS;$(DefineConstants) diff --git a/src/coreclr/nativeaot/Runtime/CommonMacros.h b/src/coreclr/nativeaot/Runtime/CommonMacros.h index fe6a081bbecb9f..be6556ba153511 100644 --- a/src/coreclr/nativeaot/Runtime/CommonMacros.h +++ b/src/coreclr/nativeaot/Runtime/CommonMacros.h @@ -119,6 +119,11 @@ inline bool IS_ALIGNED(T* val, uintptr_t alignment); #define LOG2_PTRSIZE 2 #define POINTER_SIZE 4 +#elif defined(HOST_LOONGARCH64) + +#define LOG2_PTRSIZE 3 +#define POINTER_SIZE 8 + #else #error Unsupported target architecture #endif diff --git a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp index 2cb6c6bad1ddf8..060e19f3c525e2 100644 --- a/src/coreclr/nativeaot/Runtime/EHHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/EHHelpers.cpp @@ -187,6 +187,22 @@ FCIMPL3(void, RhpCopyContextFromExInfo, void * pOSContext, int32_t cbOSContext, pContext->Sp = pPalContext->SP; pContext->Lr = pPalContext->LR; pContext->Pc = pPalContext->IP; +#elif defined(HOST_LOONGARCH64) + pContext->R4 = pPalContext->R4; + pContext->R5 = pPalContext->R5; + pContext->R23 = pPalContext->R23; + pContext->R24 = pPalContext->R24; + pContext->R25 = pPalContext->R25; + pContext->R26 = pPalContext->R26; + pContext->R27 = pPalContext->R27; + pContext->R28 = pPalContext->R28; + pContext->R29 = pPalContext->R29; + pContext->R30 = pPalContext->R30; + pContext->R31 = pPalContext->R31; + pContext->Fp = pPalContext->FP; + pContext->Sp = pPalContext->SP; + pContext->Ra = pPalContext->RA; + pContext->Pc = pPalContext->IP; #elif defined(HOST_WASM) // No registers, no work to do yet #else @@ -195,7 +211,6 @@ FCIMPL3(void, RhpCopyContextFromExInfo, void * pOSContext, int32_t cbOSContext, } FCIMPLEND -#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) struct DISPATCHER_CONTEXT { uintptr_t ControlPc; @@ -257,56 +272,8 @@ EXTERN_C int32_t __stdcall RhpPInvokeExceptionGuard(PEXCEPTION_RECORD pExc return 0; } -#else -EXTERN_C int32_t RhpPInvokeExceptionGuard() -{ - ASSERT_UNCONDITIONALLY("RhpPInvokeExceptionGuard NYI for this architecture!"); - RhFailFast(); - return 0; -} -#endif -#if defined(HOST_AMD64) || defined(HOST_ARM) || defined(HOST_X86) || defined(HOST_ARM64) || defined(HOST_WASM) FCDECL2(void, RhpThrowHwEx, int exceptionCode, TADDR faultingIP); -#else -FCIMPL0(void, RhpThrowHwEx) -{ - ASSERT_UNCONDITIONALLY("RhpThrowHwEx NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpThrowEx) -{ - ASSERT_UNCONDITIONALLY("RhpThrowEx NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallCatchFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallCatchFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallFinallyFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallFinallyFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpCallFilterFunclet) -{ - ASSERT_UNCONDITIONALLY("RhpCallFilterFunclet NYI for this architecture!"); -} -FCIMPLEND -FCIMPL0(void, RhpRethrow) -{ - ASSERT_UNCONDITIONALLY("RhpRethrow NYI for this architecture!"); -} -FCIMPLEND - -EXTERN_C void* RhpCallCatchFunclet2 = NULL; -EXTERN_C void* RhpCallFinallyFunclet2 = NULL; -EXTERN_C void* RhpCallFilterFunclet2 = NULL; -EXTERN_C void* RhpThrowEx2 = NULL; -EXTERN_C void* RhpThrowHwEx2 = NULL; -EXTERN_C void* RhpRethrow2 = NULL; -#endif EXTERN_C CODE_LOCATION RhpAssignRefAVLocation; #if defined(HOST_X86) @@ -328,7 +295,7 @@ EXTERN_C CODE_LOCATION RhpCheckedAssignRefEBPAVLocation; #endif EXTERN_C CODE_LOCATION RhpByRefAssignRefAVLocation1; -#if !defined(HOST_ARM64) +#if !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) EXTERN_C CODE_LOCATION RhpByRefAssignRefAVLocation2; #endif @@ -361,7 +328,7 @@ static bool InWriteBarrierHelper(uintptr_t faultingIP) (uintptr_t)&RhpCheckedAssignRefEBPAVLocation, #endif (uintptr_t)&RhpByRefAssignRefAVLocation1, -#if !defined(HOST_ARM64) +#if !defined(HOST_ARM64) && !defined(HOST_LOONGARCH64) (uintptr_t)&RhpByRefAssignRefAVLocation2, #endif }; @@ -443,6 +410,8 @@ static uintptr_t UnwindSimpleHelperToCaller( pContext->SetSp(sp+sizeof(uintptr_t)); // pop the stack #elif defined(HOST_ARM) || defined(HOST_ARM64) uintptr_t adjustedFaultingIP = pContext->GetLr(); +#elif defined(HOST_LOONGARCH64) + uintptr_t adjustedFaultingIP = pContext->GetRa(); #else uintptr_t adjustedFaultingIP = 0; // initializing to make the compiler happy PORTABILITY_ASSERT("UnwindSimpleHelperToCaller"); diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index dfc6e9efa915a8..d1dbd47e51985e 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -65,6 +65,27 @@ inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) return returnKind; } +#elif defined(TARGET_LOONGARCH64) +// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back +C_ASSERT(PTFF_R4_IS_GCREF == ((uint64_t)GCRK_Object << 32)); +C_ASSERT(PTFF_R4_IS_BYREF == ((uint64_t)GCRK_Byref << 32)); +C_ASSERT(PTFF_R5_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 32)); +C_ASSERT(PTFF_R5_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 32)); + +inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) +{ + // just need to report gc ref bits here. + // appropriate PTFF_SAVE_ bits will be added by the frame building routine. + return ((uint64_t)returnKind << 32); +} + +inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) +{ + GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R4_IS_GCREF | PTFF_R4_IS_BYREF | PTFF_R5_IS_GCREF | PTFF_R5_IS_BYREF)) >> 32); + ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_R4) && (transFrameFlags & PTFF_SAVE_R5))); + return returnKind; +} + #elif defined(TARGET_AMD64) // Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index c5bbcc22842776..3351326ad3071e 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -334,6 +334,41 @@ FCIMPL1(uint8_t *, RhGetCodeTarget, uint8_t * pCodeOrg) int64_t distToTarget = ((int64_t)pCode[0] << 38) >> 36; return (uint8_t *)pCode + distToTarget; } +#elif TARGET_LOONGARCH64 + uint32_t * pCode = (uint32_t *)pCodeOrg; + // is this "addi.d $a0, $a0, 8"? + if (pCode[0] == 0x02c02084) + { + // unboxing sequence + unboxingStub = true; + pCode++; + } + // is this an indirect jump? + // pcalau12i $t7, imm20; ld.d $t7, $t7, imm12; jirl $r0, $t7, 0 + if ((pCode[0] & 0xfe000000) == 0x1a000000 && + (pCode[1] & 0xffc00000) == 0x28c00000 && + (pCode[2] & 0xfc000000) == 0x4c000000) + { + // normal import stub - dist to IAT cell is relative to (PC & ~0xfff) + // pcalau12i: imm = SignExtend(imm20:Zeros(12), 64); + int64_t distToIatCell = ((((int64_t)pCode[0] & ~0x1f) << 39) >> 32); + // ld.d: offset = SignExtend(imm12, 64); + distToIatCell += (((int64_t)pCode[1] << 42) >> 52); + uint8_t ** pIatCell = (uint8_t **)(((int64_t)pCode & ~0xfff) + distToIatCell); + return *pIatCell; + } + // is this an unboxing stub followed by a relative jump? + // pcaddu18i $r21, imm20; jirl $r0, $r21, imm16 + else if (unboxingStub && + (pCode[0] & 0xfe00001f) == 0x1e000015 && + (pCode[1] & 0xfc0003ff) == 0x4c0002a0) + { + // relative jump - dist is relative to the instruction + // offset = SignExtend(immhi20:immlo16:'00', 64); + int64_t distToTarget = ((((int64_t)pCode[0] & ~0x1f) << 39) >> 26); + distToTarget += ((((int64_t)pCode[1] & ~0x3ff) << 38) >> 46); + return (uint8_t *)((int64_t)pCode + distToTarget); + } #else UNREFERENCED_PARAMETER(unboxingStub); PORTABILITY_ASSERT("RhGetCodeTarget"); diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index f2a73b7f06f446..22f12552530f84 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -449,6 +449,88 @@ typedef struct DECLSPEC_ALIGN(16) _CONTEXT { } } CONTEXT, *PCONTEXT; +#elif defined(HOST_LOONGARCH64) + +#define CONTEXT_LOONGARCH64 0x00800000L + +#define CONTEXT_CONTROL (CONTEXT_LOONGARCH64 | 0x1L) +#define CONTEXT_INTEGER (CONTEXT_LOONGARCH64 | 0x2L) + +// Specify the number of breakpoints and watchpoints that the OS +// will track. Architecturally, LOONGARCH64 supports up to 16. In practice, +// however, almost no one implements more than 4 of each. + +#define LOONGARCH64_MAX_BREAKPOINTS 8 +#define LOONGARCH64_MAX_WATCHPOINTS 2 + +typedef struct DECLSPEC_ALIGN(16) _CONTEXT { + // + // Control flags. + // + uint32_t ContextFlags; + + // + // Integer registers + // + uint64_t R0; + uint64_t Ra; + uint64_t R2; + uint64_t Sp; + uint64_t R4; + uint64_t R5; + uint64_t R6; + uint64_t R7; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + uint64_t R16; + uint64_t R17; + uint64_t R18; + uint64_t R19; + uint64_t R20; + uint64_t R21; + uint64_t Fp; + uint64_t R23; + uint64_t R24; + uint64_t R25; + uint64_t R26; + uint64_t R27; + uint64_t R28; + uint64_t R29; + uint64_t R30; + uint64_t R31; + uint64_t Pc; + + // + // Floating Point Registers: FPR64/LSX/LASX. + // + uint64_t F[4*32]; + uint64_t Fcc; + uint32_t Fcsr; + + void SetIp(uintptr_t ip) { Pc = ip; } + void SetArg0Reg(uintptr_t val) { R4 = val; } + void SetArg1Reg(uintptr_t val) { R5 = val; } + uintptr_t GetIp() { return Pc; } + uintptr_t GetRa() { return Ra; } + uintptr_t GetSp() { return Sp; } + + template + void ForEachPossibleObjectRef(F lambda) + { + for (uint64_t* pReg = &R0; pReg <= &R31; pReg++) + lambda((size_t*)pReg); + + // Ra can be used as a scratch register + lambda((size_t*)&Ra); + } +} CONTEXT, *PCONTEXT; + #elif defined(HOST_WASM) typedef struct DECLSPEC_ALIGN(8) _CONTEXT { diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h index 29896b7f53ba8e..d72b9d9f8e3d73 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawkCommon.h @@ -91,6 +91,37 @@ struct PAL_LIMITED_CONTEXT uintptr_t GetLr() const { return LR; } void SetIp(uintptr_t ip) { IP = ip; } void SetSp(uintptr_t sp) { SP = sp; } +#elif defined(TARGET_LOONGARCH64) + uintptr_t FP; + uintptr_t RA; + + uintptr_t R4; + uintptr_t R5; + uintptr_t R23; + uintptr_t R24; + uintptr_t R25; + uintptr_t R26; + uintptr_t R27; + uintptr_t R28; + uintptr_t R29; + uintptr_t R30; + uintptr_t R31; + uintptr_t R2; + + uintptr_t SP; + uintptr_t IP; + + uint64_t F[32 - 24]; // Only the F registers F24..F31 need to be preserved + // (F0-F23 are not preserved according to the ABI spec). + + + uintptr_t GetIp() const { return IP; } + uintptr_t GetSp() const { return SP; } + uintptr_t GetFp() const { return FP; } + uintptr_t GetRa() const { return RA; } + void SetIp(uintptr_t ip) { IP = ip; } + void SetSp(uintptr_t sp) { SP = sp; } + #elif defined(UNIX_AMD64_ABI) // Param regs: rdi, rsi, rdx, rcx, r8, r9, scratch: rax, rdx (both return val), preserved: rbp, rbx, r12-r15 uintptr_t IP; diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 2574ea39a16633..4af21fc436be8d 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -239,6 +239,54 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF m_HijackedReturnValueKind = retValueKind; } +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); + + ASSERT(!(pFrame->m_Flags & PTFF_SAVE_FP)); // FP should never contain a GC ref + + if (pFrame->m_Flags & PTFF_SAVE_R23) { m_RegDisplay.pR23 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R24) { m_RegDisplay.pR24 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R25) { m_RegDisplay.pR25 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R26) { m_RegDisplay.pR26 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R27) { m_RegDisplay.pR27 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R28) { m_RegDisplay.pR28 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R29) { m_RegDisplay.pR29 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R30) { m_RegDisplay.pR30 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R31) { m_RegDisplay.pR31 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_SP) { m_RegDisplay.SP = *pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_R0) { m_RegDisplay.pR0 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_RA) { m_RegDisplay.pRA = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R2) { m_RegDisplay.pR2 = pPreservedRegsCursor++; } + + if (pFrame->m_Flags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R6) { m_RegDisplay.pR6 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R7) { m_RegDisplay.pR7 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R8) { m_RegDisplay.pR8 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R9) { m_RegDisplay.pR9 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R10) { m_RegDisplay.pR10 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R12) { m_RegDisplay.pR12 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R13) { m_RegDisplay.pR13 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R14) { m_RegDisplay.pR14 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R15) { m_RegDisplay.pR15 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R16) { m_RegDisplay.pR16 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R17) { m_RegDisplay.pR17 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R18) { m_RegDisplay.pR18 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R19) { m_RegDisplay.pR19 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R20) { m_RegDisplay.pR20 = pPreservedRegsCursor++; } + if (pFrame->m_Flags & PTFF_SAVE_R21) { m_RegDisplay.pR21 = pPreservedRegsCursor++; } + + GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); + if (retValueKind != GCRK_Scalar) + { + m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pR4; + m_HijackedReturnValueKind = retValueKind; + } + #else // TARGET_ARM if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } @@ -403,6 +451,35 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PTR_PAL_LIMITED_CO m_RegDisplay.pX1 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, X1); // TODO: Copy X2-X7 when we start supporting HVA's +#elif defined(TARGET_LOONGARCH64) + // + // preserved regs + // + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, FP); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, RA); + + // + // preserved vfp regs + // + for (int32_t i = 0; i < 16 - 8; i++) + { + m_RegDisplay.F[i] = pCtx->F[i]; + } + // + // scratch regs + // + m_RegDisplay.pR4 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R4); + m_RegDisplay.pR5 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pCtx, R5); + #elif defined(UNIX_AMD64_ABI) // // preserved regs @@ -598,6 +675,48 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC m_RegDisplay.pR11 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R11); m_RegDisplay.pR12 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R12); m_RegDisplay.pLR = (PTR_uintptr_t)PTR_TO_REG(pCtx, Lr); + +#elif defined(TARGET_LOONGARCH64) + + // + // preserved regs + // + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_REG(pCtx, Fp); + m_RegDisplay.pRA = (PTR_uintptr_t)PTR_TO_REG(pCtx, Ra); + + // + // scratch regs + // + m_RegDisplay.pR0 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R0); + m_RegDisplay.pR2 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R2); + m_RegDisplay.pR4 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R4); + m_RegDisplay.pR5 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R5); + m_RegDisplay.pR6 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R6); + m_RegDisplay.pR7 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R7); + m_RegDisplay.pR8 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R8); + m_RegDisplay.pR9 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R9); + m_RegDisplay.pR10 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R10); + m_RegDisplay.pR11 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R11); + m_RegDisplay.pR12 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R12); + m_RegDisplay.pR13 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R13); + m_RegDisplay.pR14 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R14); + m_RegDisplay.pR15 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R15); + m_RegDisplay.pR16 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R16); + m_RegDisplay.pR17 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R17); + m_RegDisplay.pR18 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R18); + m_RegDisplay.pR19 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R19); + m_RegDisplay.pR20 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R20); + m_RegDisplay.pR21 = (PTR_uintptr_t)PTR_TO_REG(pCtx, R21); + #else PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); #endif // TARGET_ARM @@ -747,6 +866,18 @@ void StackFrameIterator::UpdateFromExceptionDispatch(PTR_StackFrameIterator pSou m_RegDisplay.pX28 = thisFuncletPtrs.pX28; m_RegDisplay.pFP = thisFuncletPtrs.pFP; +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pR23 = thisFuncletPtrs.pR23; + m_RegDisplay.pR24 = thisFuncletPtrs.pR24; + m_RegDisplay.pR25 = thisFuncletPtrs.pR25; + m_RegDisplay.pR26 = thisFuncletPtrs.pR26; + m_RegDisplay.pR27 = thisFuncletPtrs.pR27; + m_RegDisplay.pR28 = thisFuncletPtrs.pR28; + m_RegDisplay.pR29 = thisFuncletPtrs.pR29; + m_RegDisplay.pR30 = thisFuncletPtrs.pR30; + m_RegDisplay.pR31 = thisFuncletPtrs.pR31; + m_RegDisplay.pFP = thisFuncletPtrs.pFP; + #elif defined(UNIX_AMD64_ABI) // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. m_RegDisplay.pRbp = thisFuncletPtrs.pRbp; @@ -980,12 +1111,54 @@ void StackFrameIterator::UnwindFuncletInvokeThunk() m_RegDisplay.pX27 = SP++; m_RegDisplay.pX28 = SP++; +#elif defined(TARGET_LOONGARCH64) + PTR_uint64_t f = (PTR_uint64_t)(m_RegDisplay.SP); + + for (int i = 0; i < 8; i++) + { + m_RegDisplay.F[i] = *f++; + } + + SP = (PTR_uintptr_t)f; + + if (!isFilterInvoke) + { + // RhpCallCatchFunclet puts a couple of extra things on the stack that aren't put there by the other two + // thunks, but we don't need to know what they are here, so we just skip them. + SP += EQUALS_RETURN_ADDRESS(m_ControlPC, RhpCallCatchFunclet2) ? 6 : 4; + // Save the preserved regs portion of the REGDISPLAY across the unwind through the C# EH dispatch code. + m_funcletPtrs.pR23 = m_RegDisplay.pR23; + m_funcletPtrs.pR24 = m_RegDisplay.pR24; + m_funcletPtrs.pR25 = m_RegDisplay.pR25; + m_funcletPtrs.pR26 = m_RegDisplay.pR26; + m_funcletPtrs.pR27 = m_RegDisplay.pR27; + m_funcletPtrs.pR28 = m_RegDisplay.pR28; + m_funcletPtrs.pR29 = m_RegDisplay.pR29; + m_funcletPtrs.pR30 = m_RegDisplay.pR30; + m_funcletPtrs.pR31 = m_RegDisplay.pR31; + m_funcletPtrs.pFP = m_RegDisplay.pFP; + } + + m_RegDisplay.pFP = SP++; + + m_RegDisplay.SetIP(*SP++); + + m_RegDisplay.pR23 = SP++; + m_RegDisplay.pR24 = SP++; + m_RegDisplay.pR25 = SP++; + m_RegDisplay.pR26 = SP++; + m_RegDisplay.pR27 = SP++; + m_RegDisplay.pR28 = SP++; + m_RegDisplay.pR29 = SP++; + m_RegDisplay.pR30 = SP++; + m_RegDisplay.pR31 = SP++; + #else SP = (PTR_uintptr_t)(m_RegDisplay.SP); ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif -#if !defined(TARGET_ARM64) +#if !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) m_RegDisplay.SetIP(PCODEToPINSTR(*SP++)); #endif @@ -1120,6 +1293,30 @@ struct UniversalTransitionStackFrame { pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); } + +#elif defined(TARGET_LOONGARCH64) + + // Conservative GC reporting must be applied to everything between the base of the + // ReturnBlock and the top of the StackPassedArgs. +private: + uintptr_t m_pushedFP; // ChildSP+000 CallerSP-100 (0x08 bytes) (fp) + uintptr_t m_pushedRA; // ChildSP+008 CallerSP-0F8 (0x08 bytes) (ra) + Fp128 m_fpArgRegs[8]; // ChildSP+010 CallerSP-0F0 (0x80 bytes) (q0-q7) + uintptr_t m_returnBlock[4]; // ChildSP+090 CallerSP-070 (0x40 bytes) + uintptr_t m_intArgRegs[9]; // ChildSP+0B0 CallerSP-050 (0x48 bytes) (x0-x8) + uintptr_t m_alignmentPad; // ChildSP+0F8 CallerSP-008 (0x08 bytes) + uintptr_t m_stackPassedArgs[1]; // ChildSP+100 CallerSP+000 (unknown size) + +public: + PTR_uintptr_t get_CallerSP() { return GET_POINTER_TO_FIELD(m_stackPassedArgs[0]); } + PTR_uintptr_t get_AddressOfPushedCallerIP() { return GET_POINTER_TO_FIELD(m_pushedRA); } + PTR_uintptr_t get_LowerBoundForConservativeReporting() { return GET_POINTER_TO_FIELD(m_returnBlock[0]); } + + void UnwindNonVolatileRegisters(REGDISPLAY * pRegisterSet) + { + pRegisterSet->pFP = GET_POINTER_TO_FIELD(m_pushedFP); + } + #elif defined(TARGET_WASM) private: // WASMTODO: #error NYI for this arch @@ -1191,6 +1388,8 @@ void StackFrameIterator::UnwindUniversalTransitionThunk() #define STACK_ALIGN_SIZE 16 #elif defined(TARGET_X86) #define STACK_ALIGN_SIZE 4 +#elif defined(TARGET_LOONGARCH64) +#define STACK_ALIGN_SIZE 16 #elif defined(TARGET_WASM) #define STACK_ALIGN_SIZE 4 #endif @@ -1256,6 +1455,17 @@ void StackFrameIterator::UnwindThrowSiteThunk() m_RegDisplay.pRdi = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rdi); m_RegDisplay.pRsi = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rsi); m_RegDisplay.pRbx = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, Rbx); +#elif defined(TARGET_LOONGARCH64) + m_RegDisplay.pR23 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R23); + m_RegDisplay.pR24 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R24); + m_RegDisplay.pR25 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R25); + m_RegDisplay.pR26 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R26); + m_RegDisplay.pR27 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R27); + m_RegDisplay.pR28 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R28); + m_RegDisplay.pR29 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R29); + m_RegDisplay.pR30 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R30); + m_RegDisplay.pR31 = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, R31); + m_RegDisplay.pFP = (PTR_uintptr_t)PTR_TO_MEMBER_TADDR(PAL_LIMITED_CONTEXT, pContext, FP); #else ASSERT_UNCONDITIONALLY("NYI for this arch"); #endif diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index 77cef2133b5c16..f174edd4c473b2 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -180,6 +180,17 @@ class StackFrameIterator PTR_uintptr_t pX27; PTR_uintptr_t pX28; PTR_uintptr_t pFP; +#elif defined(TARGET_LOONGARCH64) + PTR_uintptr_t pR23; + PTR_uintptr_t pR24; + PTR_uintptr_t pR25; + PTR_uintptr_t pR26; + PTR_uintptr_t pR27; + PTR_uintptr_t pR28; + PTR_uintptr_t pR29; + PTR_uintptr_t pR30; + PTR_uintptr_t pR31; + PTR_uintptr_t pFP; #elif defined(UNIX_AMD64_ABI) PTR_uintptr_t pRbp; PTR_uintptr_t pRbx; diff --git a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp index d22f30e19d9e0c..c8f91a07a2819f 100644 --- a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp +++ b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp @@ -22,6 +22,8 @@ #define THUNK_SIZE 20 #elif TARGET_ARM64 #define THUNK_SIZE 16 +#elif TARGET_LOONGARCH64 +#define THUNK_SIZE 16 #else #define THUNK_SIZE (2 * OS_PAGE_SIZE) // This will cause RhpGetNumThunksPerBlock to return 0 #endif @@ -231,6 +233,28 @@ EXTERN_C void* QCALLTYPE RhAllocateThunksMapping() *((uint32_t*)pCurrentThunkAddress) = 0xD43E0000; pCurrentThunkAddress += 4; + +#elif TARGET_LOONGARCH64 + + //pcaddi $t7, + //pcaddi $t8, - + //ld.d $t8, $t8, + //jirl $r0, $t8, 0 + + int delta = (int)(pCurrentDataAddress - pCurrentThunkAddress); + *((uint32_t*)pCurrentThunkAddress) = 0x18000013 | (((delta & 0x3FFFFC) >> 2) << 5); + pCurrentThunkAddress += 4; + + delta += OS_PAGE_SIZE - POINTER_SIZE - (i * POINTER_SIZE * 2) - 4; + *((uint32_t*)pCurrentThunkAddress) = 0x18000014 | (((delta & 0x3FFFFC) >> 2) << 5); + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0x28C00294; + pCurrentThunkAddress += 4; + + *((uint32_t*)pCurrentThunkAddress) = 0x4C000280; + pCurrentThunkAddress += 4; + #else UNREFERENCED_PARAMETER(pCurrentDataAddress); UNREFERENCED_PARAMETER(pCurrentThunkAddress); diff --git a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp index 2cb9445144c220..f041e499c11d4b 100644 --- a/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp +++ b/src/coreclr/nativeaot/Runtime/gcenv.ee.cpp @@ -56,7 +56,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) { FireEtwGCRestartEEBegin_V1(GetClrInstanceId()); -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if !defined(TARGET_X86) && !defined(TARGET_AMD64) // Flush the store buffers on all CPUs, to ensure that they all see changes made // by the GC threads. This only matters on weak memory ordered processors as // the strong memory ordered processors wouldn't have reordered the relevant reads. @@ -64,7 +64,7 @@ void GCToEEInterface::RestartEE(bool /*bFinishedGC*/) // the runtime was suspended and that will return to cooperative mode after the runtime // is restarted. ::FlushProcessWriteBuffers(); -#endif //TARGET_ARM || TARGET_ARM64 +#endif // !defined(TARGET_X86) && !defined(TARGET_AMD64) SyncClean::CleanUp(); diff --git a/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h index 0ef7f5e8a84f72..ece8ae50b379e6 100644 --- a/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h +++ b/src/coreclr/nativeaot/Runtime/inc/TargetPtrs.h @@ -13,6 +13,8 @@ typedef uint32_t UIntTarget; typedef uint64_t UIntTarget; #elif defined(TARGET_WASM) typedef uint32_t UIntTarget; +#elif defined(TARGET_LOONGARCH64) +typedef uint64_t UIntTarget; #else #error unexpected target architecture #endif diff --git a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h index f0ebc5b7a7e508..f72ff28caf001f 100644 --- a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h +++ b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h @@ -338,6 +338,67 @@ enum PInvokeTransitionFrameFlags : uint64_t PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition }; +#elif defined(TARGET_LOONGARCH64) +enum PInvokeTransitionFrameFlags : uint64_t +{ + // NOTE: Keep in sync with src\coreclr\nativeaot\Runtime\loongarch64\AsmMacros.h + + // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has + // to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp + + // standard preserved registers + PTFF_SAVE_R23 = 0x0000000000000001, + PTFF_SAVE_R24 = 0x0000000000000002, + PTFF_SAVE_R25 = 0x0000000000000004, + PTFF_SAVE_R26 = 0x0000000000000008, + PTFF_SAVE_R27 = 0x0000000000000010, + PTFF_SAVE_R28 = 0x0000000000000020, + PTFF_SAVE_R29 = 0x0000000000000040, + PTFF_SAVE_R30 = 0x0000000000000080, + PTFF_SAVE_R31 = 0x0000000000000100, + + PTFF_SAVE_SP = 0x0000000000000200, // Used for 'coop pinvokes' in runtime helper routines. Methods with + // PInvokes are required to have a frame pointers, but methods which + // call runtime helpers are not. Therefore, methods that call runtime + // helpers may need SP to seed the stackwalk. + + // Scratch registers + PTFF_SAVE_R0 = 0x0000000000000400, + PTFF_SAVE_R2 = 0x0000000000000800, + PTFF_SAVE_R4 = 0x0000000000001000, + PTFF_SAVE_R5 = 0x0000000000002000, + PTFF_SAVE_R6 = 0x0000000000004000, + PTFF_SAVE_R7 = 0x0000000000008000, + PTFF_SAVE_R8 = 0x0000000000010000, + PTFF_SAVE_R9 = 0x0000000000020000, + PTFF_SAVE_R10 = 0x0000000000040000, + PTFF_SAVE_R11 = 0x0000000000080000, + PTFF_SAVE_R12 = 0x0000000000100000, + PTFF_SAVE_R13 = 0x0000000000200000, + PTFF_SAVE_R14 = 0x0000000000400000, + PTFF_SAVE_R15 = 0x0000000000800000, + PTFF_SAVE_R16 = 0x0000000001000000, + PTFF_SAVE_R17 = 0x0000000002000000, + PTFF_SAVE_R18 = 0x0000000004000000, + PTFF_SAVE_R19 = 0x0000000008000000, + PTFF_SAVE_R20 = 0x0000000010000000, + PTFF_SAVE_R21 = 0x0000000020000000, + + PTFF_SAVE_FP = 0x0000000040000000, // should never be used, we require FP frames for methods with + // pinvoke and it is saved into the frame pointer field instead + + PTFF_SAVE_RA = 0x0000000080000000, // this is useful for the case of loop hijacking where we need both + // a return address pointing into the hijacked method and that method's + // ra register, which may hold a gc pointer + + // used by hijack handler to report return value of hijacked method + PTFF_R4_IS_GCREF = 0x0000000100000000, + PTFF_R4_IS_BYREF = 0x0000000200000000, + PTFF_R5_IS_GCREF = 0x0000000400000000, + PTFF_R5_IS_BYREF = 0x0000000800000000, + + PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition +}; #else // TARGET_ARM enum PInvokeTransitionFrameFlags @@ -412,6 +473,8 @@ struct PInvokeTransitionFrame // can be an invalid pointer in universal transition cases (which never need to call GetThread) #ifdef TARGET_ARM64 uint64_t m_Flags; // PInvokeTransitionFrameFlags +#elif TARGET_LOONGARCH64 + uint64_t m_Flags; // PInvokeTransitionFrameFlags #else uint32_t m_Flags; // PInvokeTransitionFrameFlags #endif @@ -436,6 +499,8 @@ struct PInvokeTransitionFrame #define OFFSETOF__Thread__m_pTransitionFrame 0x40 #elif defined(TARGET_ARM64) #define OFFSETOF__Thread__m_pTransitionFrame 0x40 +#elif defined(TARGET_LOONGARCH64) +#define OFFSETOF__Thread__m_pTransitionFrame 0x40 #elif defined(TARGET_X86) #define OFFSETOF__Thread__m_pTransitionFrame 0x2c #elif defined(TARGET_ARM) diff --git a/src/coreclr/nativeaot/Runtime/regdisplay.h b/src/coreclr/nativeaot/Runtime/regdisplay.h index 739a4eec230908..e95c9d5fd71d6f 100644 --- a/src/coreclr/nativeaot/Runtime/regdisplay.h +++ b/src/coreclr/nativeaot/Runtime/regdisplay.h @@ -169,6 +169,62 @@ struct REGDISPLAY inline void SetIP(PCODE IP) { this->IP = IP; } inline void SetSP(uintptr_t SP) { this->SP = SP; } }; + +#elif defined(TARGET_LOONGARCH64) + +struct REGDISPLAY +{ + PTR_uintptr_t pR0; + PTR_uintptr_t pRA; + PTR_uintptr_t pR2; + + uintptr_t SP; + + PTR_uintptr_t pR4; + PTR_uintptr_t pR5; + PTR_uintptr_t pR6; + PTR_uintptr_t pR7; + PTR_uintptr_t pR8; + PTR_uintptr_t pR9; + PTR_uintptr_t pR10; + PTR_uintptr_t pR11; + PTR_uintptr_t pR12; + PTR_uintptr_t pR13; + PTR_uintptr_t pR14; + PTR_uintptr_t pR15; + PTR_uintptr_t pR16; + PTR_uintptr_t pR17; + PTR_uintptr_t pR18; + PTR_uintptr_t pR19; + PTR_uintptr_t pR20; + PTR_uintptr_t pR21; + PTR_uintptr_t pFP; + PTR_uintptr_t pR23; + PTR_uintptr_t pR24; + PTR_uintptr_t pR25; + PTR_uintptr_t pR26; + PTR_uintptr_t pR27; + PTR_uintptr_t pR28; + PTR_uintptr_t pR29; + PTR_uintptr_t pR30; + PTR_uintptr_t pR31; + + PCODE IP; + + uint64_t F[32-24]; // Only the F registers F24..F31 needs to be preserved + // (F0-F23 are not preserved according to the ABI spec). + // These need to be unwound during a stack walk + // for EH, but not adjusted, so we only need + // their values, not their addresses + + inline PCODE GetIP() { return IP; } + inline uintptr_t GetSP() { return SP; } + inline uintptr_t GetFP() { return *pFP; } + + inline void SetIP(PCODE IP) { this->IP = IP; } + inline void SetSP(uintptr_t SP) { this->SP = SP; } +}; + #elif defined(TARGET_WASM) struct REGDISPLAY @@ -185,7 +241,7 @@ struct REGDISPLAY inline void SetIP(PCODE IP) { } inline void SetSP(uintptr_t SP) { } }; -#endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM +#endif // HOST_X86 || HOST_AMD64 || HOST_ARM || HOST_ARM64 || HOST_WASM || HOST_LOONGARCH64 typedef REGDISPLAY * PREGDISPLAY; diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp index 8e587901f60a3f..e084fb35e391f3 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp @@ -80,7 +80,43 @@ #if HAVE___GREGSET_T -#ifdef HOST_64BIT +#if defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).__gregs[0]) +#define MCREG_Ra(mc) ((mc).__gregs[1]) +#define MCREG_Tp(mc) ((mc).__gregs[2]) +#define MCREG_Sp(mc) ((mc).__gregs[3]) +#define MCREG_A0(mc) ((mc).__gregs[4]) +#define MCREG_A1(mc) ((mc).__gregs[5]) +#define MCREG_A2(mc) ((mc).__gregs[6]) +#define MCREG_A3(mc) ((mc).__gregs[7]) +#define MCREG_A4(mc) ((mc).__gregs[8]) +#define MCREG_A5(mc) ((mc).__gregs[9]) +#define MCREG_A6(mc) ((mc).__gregs[10]) +#define MCREG_A7(mc) ((mc).__gregs[11]) +#define MCREG_T0(mc) ((mc).__gregs[12]) +#define MCREG_T1(mc) ((mc).__gregs[13]) +#define MCREG_T2(mc) ((mc).__gregs[14]) +#define MCREG_T3(mc) ((mc).__gregs[15]) +#define MCREG_T4(mc) ((mc).__gregs[16]) +#define MCREG_T5(mc) ((mc).__gregs[17]) +#define MCREG_T6(mc) ((mc).__gregs[18]) +#define MCREG_T7(mc) ((mc).__gregs[19]) +#define MCREG_T8(mc) ((mc).__gregs[20]) +#define MCREG_X0(mc) ((mc).__gregs[21]) +#define MCREG_Fp(mc) ((mc).__gregs[22]) +#define MCREG_S0(mc) ((mc).__gregs[23]) +#define MCREG_S1(mc) ((mc).__gregs[24]) +#define MCREG_S2(mc) ((mc).__gregs[25]) +#define MCREG_S3(mc) ((mc).__gregs[26]) +#define MCREG_S4(mc) ((mc).__gregs[27]) +#define MCREG_S5(mc) ((mc).__gregs[28]) +#define MCREG_S6(mc) ((mc).__gregs[29]) +#define MCREG_S7(mc) ((mc).__gregs[30]) +#define MCREG_S8(mc) ((mc).__gregs[31]) +#define MCREG_Pc(mc) ((mc).__pc) + +#elif HOST_64BIT #define MCREG_Rip(mc) ((mc).__gregs[_REG_RIP]) #define MCREG_Rsp(mc) ((mc).__gregs[_REG_RSP]) #define MCREG_Rax(mc) ((mc).__gregs[_REG_RAX]) @@ -115,7 +151,43 @@ #elif HAVE_GREGSET_T -#ifdef HOST_64BIT +#if defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).__gregs[0]) +#define MCREG_Ra(mc) ((mc).__gregs[1]) +#define MCREG_Tp(mc) ((mc).__gregs[2]) +#define MCREG_Sp(mc) ((mc).__gregs[3]) +#define MCREG_A0(mc) ((mc).__gregs[4]) +#define MCREG_A1(mc) ((mc).__gregs[5]) +#define MCREG_A2(mc) ((mc).__gregs[6]) +#define MCREG_A3(mc) ((mc).__gregs[7]) +#define MCREG_A4(mc) ((mc).__gregs[8]) +#define MCREG_A5(mc) ((mc).__gregs[9]) +#define MCREG_A6(mc) ((mc).__gregs[10]) +#define MCREG_A7(mc) ((mc).__gregs[11]) +#define MCREG_T0(mc) ((mc).__gregs[12]) +#define MCREG_T1(mc) ((mc).__gregs[13]) +#define MCREG_T2(mc) ((mc).__gregs[14]) +#define MCREG_T3(mc) ((mc).__gregs[15]) +#define MCREG_T4(mc) ((mc).__gregs[16]) +#define MCREG_T5(mc) ((mc).__gregs[17]) +#define MCREG_T6(mc) ((mc).__gregs[18]) +#define MCREG_T7(mc) ((mc).__gregs[19]) +#define MCREG_T8(mc) ((mc).__gregs[20]) +#define MCREG_X0(mc) ((mc).__gregs[21]) +#define MCREG_Fp(mc) ((mc).__gregs[22]) +#define MCREG_S0(mc) ((mc).__gregs[23]) +#define MCREG_S1(mc) ((mc).__gregs[24]) +#define MCREG_S2(mc) ((mc).__gregs[25]) +#define MCREG_S3(mc) ((mc).__gregs[26]) +#define MCREG_S4(mc) ((mc).__gregs[27]) +#define MCREG_S5(mc) ((mc).__gregs[28]) +#define MCREG_S6(mc) ((mc).__gregs[29]) +#define MCREG_S7(mc) ((mc).__gregs[30]) +#define MCREG_S8(mc) ((mc).__gregs[31]) +#define MCREG_Pc(mc) ((mc).__pc) + +#elif HOST_64BIT #define MCREG_Rip(mc) ((mc).gregs[REG_RIP]) #define MCREG_Rsp(mc) ((mc).gregs[REG_RSP]) #define MCREG_Rax(mc) ((mc).gregs[REG_RAX]) @@ -224,6 +296,42 @@ #define MCREG_Sp(mc) ((mc).sp) #define MCREG_Pc(mc) ((mc).pc) +#elif defined(HOST_LOONGARCH64) + +#define MCREG_R0(mc) ((mc).regs[0]) +#define MCREG_Ra(mc) ((mc).regs[1]) +#define MCREG_Tp(mc) ((mc).regs[2]) +#define MCREG_Sp(mc) ((mc).regs[3]) +#define MCREG_A0(mc) ((mc).regs[4]) +#define MCREG_A1(mc) ((mc).regs[5]) +#define MCREG_A2(mc) ((mc).regs[6]) +#define MCREG_A3(mc) ((mc).regs[7]) +#define MCREG_A4(mc) ((mc).regs[8]) +#define MCREG_A5(mc) ((mc).regs[9]) +#define MCREG_A6(mc) ((mc).regs[10]) +#define MCREG_A7(mc) ((mc).regs[11]) +#define MCREG_T0(mc) ((mc).regs[12]) +#define MCREG_T1(mc) ((mc).regs[13]) +#define MCREG_T2(mc) ((mc).regs[14]) +#define MCREG_T3(mc) ((mc).regs[15]) +#define MCREG_T4(mc) ((mc).regs[16]) +#define MCREG_T5(mc) ((mc).regs[17]) +#define MCREG_T6(mc) ((mc).regs[18]) +#define MCREG_T7(mc) ((mc).regs[19]) +#define MCREG_T8(mc) ((mc).regs[20]) +#define MCREG_X0(mc) ((mc).regs[21]) +#define MCREG_Fp(mc) ((mc).regs[22]) +#define MCREG_S0(mc) ((mc).regs[23]) +#define MCREG_S1(mc) ((mc).regs[24]) +#define MCREG_S2(mc) ((mc).regs[25]) +#define MCREG_S3(mc) ((mc).regs[26]) +#define MCREG_S4(mc) ((mc).regs[27]) +#define MCREG_S5(mc) ((mc).regs[28]) +#define MCREG_S6(mc) ((mc).regs[29]) +#define MCREG_S7(mc) ((mc).regs[30]) +#define MCREG_S8(mc) ((mc).regs[31]) +#define MCREG_Pc(mc) ((mc).pc) + #else // For FreeBSD, as found in x86/ucontext.h @@ -365,6 +473,29 @@ MCREG_X0(nativeContext->uc_mcontext) = arg0Reg; \ MCREG_X1(nativeContext->uc_mcontext) = arg1Reg; +#elif defined(HOST_LOONGARCH64) + +#define ASSIGN_CONTROL_REGS \ + ASSIGN_REG(Pc, IP) \ + ASSIGN_REG(Sp, SP) \ + ASSIGN_REG(Fp, FP) \ + ASSIGN_REG(Ra, RA) + +#define ASSIGN_INTEGER_REGS \ + ASSIGN_REG(S0, R23) \ + ASSIGN_REG(S1, R24) \ + ASSIGN_REG(S2, R25) \ + ASSIGN_REG(S3, R26) \ + ASSIGN_REG(S4, R27) \ + ASSIGN_REG(S5, R28) \ + ASSIGN_REG(S6, R29) \ + ASSIGN_REG(S7, R30) \ + ASSIGN_REG(S8, R31) + +#define ASSIGN_TWO_ARGUMENT_REGS \ + MCREG_A0(nativeContext->uc_mcontext) = arg0Reg; \ + MCREG_A1(nativeContext->uc_mcontext) = arg1Reg; + #elif defined(HOST_WASM) // TODO: determine how unwinding will work on WebAssembly #define ASSIGN_CONTROL_REGS @@ -529,6 +660,42 @@ uint64_t GetPC(void* context) uint64_t& UNIX_CONTEXT::R11(){ return (uint64_t&)MCREG_R11(ctx.uc_mcontext); } uint64_t& UNIX_CONTEXT::R12(){ return (uint64_t&)MCREG_R12(ctx.uc_mcontext); } +#elif TARGET_LOONGARCH64 + + uint64_t& UNIX_CONTEXT::R0() { return (uint64_t&)MCREG_R0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R2() { return (uint64_t&)MCREG_Tp(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R4() { return (uint64_t&)MCREG_A0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R5() { return (uint64_t&)MCREG_A1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R6() { return (uint64_t&)MCREG_A2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R7() { return (uint64_t&)MCREG_A3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R8() { return (uint64_t&)MCREG_A4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R9() { return (uint64_t&)MCREG_A5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R10() { return (uint64_t&)MCREG_A6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R11() { return (uint64_t&)MCREG_A7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R12() { return (uint64_t&)MCREG_T0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R13() { return (uint64_t&)MCREG_T1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R14() { return (uint64_t&)MCREG_T2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R15() { return (uint64_t&)MCREG_T3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R16() { return (uint64_t&)MCREG_T4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R17() { return (uint64_t&)MCREG_T5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R18() { return (uint64_t&)MCREG_T6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R19() { return (uint64_t&)MCREG_T7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R20() { return (uint64_t&)MCREG_T8(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R21() { return (uint64_t&)MCREG_X0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R23() { return (uint64_t&)MCREG_S0(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R24() { return (uint64_t&)MCREG_S1(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R25() { return (uint64_t&)MCREG_S2(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R26() { return (uint64_t&)MCREG_S3(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R27() { return (uint64_t&)MCREG_S4(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R28() { return (uint64_t&)MCREG_S5(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R29() { return (uint64_t&)MCREG_S6(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R30() { return (uint64_t&)MCREG_S7(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R31() { return (uint64_t&)MCREG_S8(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::Fp() { return (uint64_t&)MCREG_Fp(ctx.uc_mcontext); } // R22 + uint64_t& UNIX_CONTEXT::Ra() { return (uint64_t&)MCREG_Ra(ctx.uc_mcontext); } // R1 + uint64_t& UNIX_CONTEXT::Sp() { return (uint64_t&)MCREG_Sp(ctx.uc_mcontext); } // R3 + uint64_t& UNIX_CONTEXT::Pc() { return (uint64_t&)MCREG_Pc(ctx.uc_mcontext); } + #else PORTABILITY_ASSERT("UNIX_CONTEXT"); #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h index caddff419d373c..662b697715da0a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h @@ -158,6 +158,61 @@ struct UNIX_CONTEXT lambda((size_t*)&R11()); lambda((size_t*)&R12()); } + +#elif defined(TARGET_LOONGARCH64) + + uint64_t& R0(); + uint64_t& R2(); + uint64_t& R4(); + uint64_t& R5(); + uint64_t& R6(); + uint64_t& R7(); + uint64_t& R8(); + uint64_t& R9(); + uint64_t& R10(); + uint64_t& R11(); + uint64_t& R12(); + uint64_t& R13(); + uint64_t& R14(); + uint64_t& R15(); + uint64_t& R16(); + uint64_t& R17(); + uint64_t& R18(); + uint64_t& R19(); + uint64_t& R20(); + uint64_t& R21(); + uint64_t& R23(); + uint64_t& R24(); + uint64_t& R25(); + uint64_t& R26(); + uint64_t& R27(); + uint64_t& R28(); + uint64_t& R29(); + uint64_t& R30(); + uint64_t& R31(); + uint64_t& Fp(); // R22 + uint64_t& Ra(); // R1 + uint64_t& Sp(); // R3 + uint64_t& Pc(); + + uintptr_t GetIp() { return (uintptr_t)Pc(); } + uintptr_t GetSp() { return (uintptr_t)Sp(); } + + template + void ForEachPossibleObjectRef(F lambda) + { + // it is doubtful anyone would implement R0,R2,R4-R21,R23-R31 not as a contiguous array + // just in case - here are some asserts. + ASSERT(&R4() + 1 == &R5()); + ASSERT(&R4() + 10 == &R14()); + + for (uint64_t* pReg = &R0(); pReg <= &R31(); pReg++) + lambda((size_t*)pReg); + + // Ra can be used as a scratch register + lambda((size_t*)&Ra()); + } + #else PORTABILITY_ASSERT("UNIX_CONTEXT"); #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 3ccf59f611ecba..b12d63bf726129 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -399,7 +399,7 @@ bool UnixNativeCodeManager::IsUnwindable(PTR_VOID pvAddress) ASSERT(((uintptr_t)pvAddress & 1) == 0); #endif -#if defined(TARGET_ARM64) || defined(TARGET_ARM) +#if defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) MethodInfo methodInfo; FindMethodInfo(pvAddress, &methodInfo); pMethodInfo = &methodInfo; @@ -667,6 +667,61 @@ int UnixNativeCodeManager::IsInProlog(MethodInfo * pMethodInfo, PTR_VOID pvAddre return 0; +#elif defined(TARGET_LOONGARCH64) + +// 0010 1001 11xx xxxx xxxx xxxx xxxx xxxx +#define ST_BITS 0x29C00000 +#define ST_MASK 0xFFC00000 + +// addi.d $fp, $sp, x +// ori $fp, $sp, 0 +// 0000 0010 11xx xxxx xxxx xx00 0111 0110 +#define ADDI_FP_SP_BITS 0x02C00076 +#define ADDI_FP_SP_MASK 0xFFC003FF + +#define ST_RJ_MASK 0x3E0 +#define ST_RJ_FP 0x2C0 +#define ST_RJ_RA 0x20 +#define ST_RD_MASK 0x1F +#define ST_RD_SP 0x3 +#define ST_RD_FP 0x16 + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + ASSERT(pNativeMethodInfo != NULL); + + uint32_t* start = (uint32_t*)pNativeMethodInfo->pMethodStartAddress; + bool savedFp = false; + bool savedRa = false; + bool establishedFp = false; + + for (uint32_t* pInstr = (uint32_t*)start; pInstr < pvAddress && !(savedFp && savedRa && establishedFp); pInstr++) + { + uint32_t instr = *pInstr; + + if (((instr & ST_MASK) == ST_BITS) && + ((instr & ST_RD_MASK) == ST_RD_SP || (instr & ST_RD_MASK) == ST_RD_FP)) + { + // SP/FP-relative store of pair of registers + savedFp |= (instr & ST_RJ_MASK) == ST_RJ_FP; + savedRa |= (instr & ST_RJ_MASK) == ST_RJ_RA; + } + else if ((instr & ADDI_FP_SP_MASK) == ADDI_FP_SP_BITS) + { + establishedFp = true; + } + else + { + // JIT generates other patterns into the prolog that we currently don't + // recognize (saving unpaired register, stack pointer adjustments). We + // don't need to recognize these patterns unless a compact unwinding code + // is generated for them in ILC. + // https://github.com/dotnet/runtime/issues/76371 + return -1; + } + } + + return savedFp && savedRa && establishedFp ? 0 : 1; + #else return -1; @@ -1043,6 +1098,60 @@ int UnixNativeCodeManager::TrailingEpilogueInstructionsCount(MethodInfo * pMetho return 0; } +#elif defined(TARGET_LOONGARCH64) + +// ld.d +// 0010 1000 11xx xxxx xxxx xxxx xxxx xxxx +#define LD_BITS 0xB9400000 +#define LD_MASK 0xBF400000 + +// ldx.d with register offset +// 0011 1000 0000 1100 0xxx xxxx xxxx xxxx +#define LDX_BITS 0x380C0000 +#define LDX_MASK 0xFFFF7000 + +// Branches, Exception Generating and System instruction group +// 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx +#define BEGS_BITS 0x40000000 +#define BEGS_MASK 0xC0000000 + + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + ASSERT(pNativeMethodInfo != NULL); + + uint32_t* start = (uint32_t*)pNativeMethodInfo->pMethodStartAddress; + + // Since we stop on branches, the search is roughly limited by the containing basic block. + // We typically examine just 1-5 instructions and in rare cases up to 30. + // + // TODO: we can also limit the search by the longest possible epilogue length, but + // we must be sure the longest length considers all possibilities, + // which is somewhat nontrivial to derive/prove. + // It does not seem urgent, but it could be nice to have a constant upper bound. + for (uint32_t* pInstr = (uint32_t*)pvAddress - 1; pInstr > start; pInstr--) + { + uint32_t instr = *pInstr; + + // check for Branches, Exception Generating and System instruction group. + // If we see such instruction before seeing FP or RA restored, we are not in an epilog. + // Note: this includes RET, BRK, branches, calls, tailcalls, fences, etc... + if ((instr & BEGS_MASK) == BEGS_BITS) + { + // not in an epilogue + break; + } + + // check for restoring FP or RA with ld.d or ldx.d + int operand = (instr >> 5) & 0x1f; + if (operand == 22 || operand == 1) + { + if ((instr & LD_MASK) == LD_BITS || + (instr & LDX_MASK) == LDX_BITS) + { + return -1; + } + } + } + #endif return 0; @@ -1085,9 +1194,9 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn // Decode the GC info for the current method to determine its return type GcInfoDecoderFlags flags = DECODE_RETURN_KIND; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) +#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); -#endif // TARGET_ARM || TARGET_ARM64 +#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 GcInfoDecoder decoder(GCInfoToken(p), flags); *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); @@ -1172,6 +1281,41 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pLR; return true; + +#elif defined(TARGET_LOONGARCH64) + + if (decoder.HasTailCalls()) + { + // Do not hijack functions that have tail calls, since there are two problems: + // 1. When a function that tail calls another one is hijacked, the RA may be + // stored at a different location in the stack frame of the tail call target. + // So just by performing tail call, the hijacked location becomes invalid and + // unhijacking would corrupt stack by writing to that location. + // 2. There is a small window after the caller pops RA from the stack in its + // epilog and before the tail called function pushes RA in its prolog when + // the hijacked return address would not be not on the stack and so we would + // not be able to unhijack. + return false; + } + + PTR_uintptr_t pRA = pRegisterSet->pRA; + if (!VirtualUnwind(pMethodInfo, pRegisterSet)) + { + return false; + } + + if (pRegisterSet->pRA == pRA) + { + // This is the case when we are either: + // + // 1) In a leaf method that does not push RA on stack, OR + // 2) In the prolog/epilog of a non-leaf method that has not yet pushed RA on stack + // or has RA already popped off. + return false; + } + + *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pRA; + return true; #else return false; #endif // defined(TARGET_AMD64) diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 39bf9f024be60e..67701b45dd9489 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -34,6 +34,8 @@ using libunwind::Registers_arm; #elif defined(TARGET_ARM64) using libunwind::Registers_arm64; using libunwind::CompactUnwinder_arm64; +#elif defined(TARGET_LOONGARCH64) +using libunwind::Registers_loongarch; #elif defined(TARGET_X86) using libunwind::Registers_x86; #else @@ -806,6 +808,286 @@ void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) #endif // TARGET_ARM64 +#if defined(TARGET_LOONGARCH64) + +// Shim that implements methods required by libunwind over REGDISPLAY +struct Registers_REGDISPLAY : REGDISPLAY +{ + inline static int getArch() { return libunwind::REGISTERS_LOONGARCH; } + inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_LOONGARCH; } + + bool validRegister(int num) const; + bool validFloatRegister(int num) { return false; }; + bool validVectorRegister(int num) const; + + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value, uint64_t location); + + double getFloatRegister(int num) const {abort();} + void setFloatRegister(int num, double value) {abort();} + + libunwind::v128 getVectorRegister(int num) const; + void setVectorRegister(int num, libunwind::v128 value); + + uint64_t getSP() const { return SP;} + void setSP(uint64_t value, uint64_t location) { SP = value;} + uint64_t getIP() const { return IP;} + void setIP(uint64_t value, uint64_t location) { IP = value; } + uint64_t getFP() const { return *pFP;} + void setFP(uint64_t value, uint64_t location) { pFP = (PTR_uintptr_t)location;} +}; + +inline bool Registers_REGDISPLAY::validRegister(int num) const { + if (num == UNW_REG_SP || num == UNW_LOONGARCH_R3) + return true; + + if (num == UNW_LOONGARCH_R22) + return true; + + if (num == UNW_REG_IP) + return true; + + if (num >= UNW_LOONGARCH_R0 && num <= UNW_LOONGARCH_R31) + return true; + + return false; +} + +bool Registers_REGDISPLAY::validVectorRegister(int num) const +{ + if (num >= UNW_LOONGARCH_F24 && num <= UNW_LOONGARCH_F31) + return true; + + return false; +} + +inline uint64_t Registers_REGDISPLAY::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_LOONGARCH_R3) + return SP; + + if (regNum == UNW_LOONGARCH_R22) + return *pFP; + + if (regNum == UNW_LOONGARCH_R1) + return *pRA; + + if (regNum == UNW_REG_IP) + return IP; + + switch (regNum) + { + case (UNW_LOONGARCH_R0): + return *pR0; + case (UNW_LOONGARCH_R2): + return *pR2; + case (UNW_LOONGARCH_R4): + return *pR4; + case (UNW_LOONGARCH_R5): + return *pR5; + case (UNW_LOONGARCH_R6): + return *pR6; + case (UNW_LOONGARCH_R7): + return *pR7; + case (UNW_LOONGARCH_R8): + return *pR8; + case (UNW_LOONGARCH_R9): + return *pR9; + case (UNW_LOONGARCH_R10): + return *pR10; + case (UNW_LOONGARCH_R11): + return *pR11; + case (UNW_LOONGARCH_R12): + return *pR12; + case (UNW_LOONGARCH_R13): + return *pR13; + case (UNW_LOONGARCH_R14): + return *pR14; + case (UNW_LOONGARCH_R15): + return *pR15; + case (UNW_LOONGARCH_R16): + return *pR16; + case (UNW_LOONGARCH_R17): + return *pR17; + case (UNW_LOONGARCH_R18): + return *pR18; + case (UNW_LOONGARCH_R19): + return *pR19; + case (UNW_LOONGARCH_R20): + return *pR20; + case (UNW_LOONGARCH_R21): + return *pR21; + case (UNW_LOONGARCH_R23): + return *pR23; + case (UNW_LOONGARCH_R24): + return *pR24; + case (UNW_LOONGARCH_R25): + return *pR25; + case (UNW_LOONGARCH_R26): + return *pR26; + case (UNW_LOONGARCH_R27): + return *pR27; + case (UNW_LOONGARCH_R28): + return *pR28; + case (UNW_LOONGARCH_R29): + return *pR29; + case (UNW_LOONGARCH_R30): + return *pR30; + case (UNW_LOONGARCH_R31): + return *pR31; + } + + PORTABILITY_ASSERT("unsupported loongarch64 register"); +} + +void Registers_REGDISPLAY::setRegister(int num, uint64_t value, uint64_t location) +{ + if (num == UNW_REG_SP || num == UNW_LOONGARCH_R3) { + SP = (uintptr_t )value; + return; + } + + if (num == UNW_LOONGARCH_R22) { + pFP = (PTR_uintptr_t)location; + return; + } + + if (num == UNW_LOONGARCH_R1) { + pRA = (PTR_uintptr_t)location; + return; + } + + if (num == UNW_REG_IP) { + IP = value; + return; + } + + switch (num) + { + case (UNW_LOONGARCH_R0): + pR0 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R2): + pR2 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R4): + pR4 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R5): + pR5 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R6): + pR6 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R7): + pR7 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R8): + pR8 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R9): + pR9 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R10): + pR10 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R11): + pR11 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R12): + pR12 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R13): + pR13 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R14): + pR14 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R15): + pR15 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R16): + pR16 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R17): + pR17 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R18): + pR18 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R19): + pR19 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R20): + pR20 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R21): + pR21 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R23): + pR23 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R24): + pR24 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R25): + pR25 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R26): + pR26 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R27): + pR27 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R28): + pR28 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R29): + pR29 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R30): + pR30 = (PTR_uintptr_t)location; + break; + case (UNW_LOONGARCH_R31): + pR31 = (PTR_uintptr_t)location; + break; + default: + PORTABILITY_ASSERT("unsupported loongarch64 register"); + } +} + +libunwind::v128 Registers_REGDISPLAY::getVectorRegister(int num) const +{ + num -= UNW_LOONGARCH_F24; + + if (num < 0 || num >= sizeof(F) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported loongarch64 vector register"); + } + + libunwind::v128 result; + + result.vec[0] = 0; + result.vec[1] = 0; + result.vec[2] = F[num] >> 32; + result.vec[3] = F[num] & 0xFFFFFFFF; + + return result; +} + +void Registers_REGDISPLAY::setVectorRegister(int num, libunwind::v128 value) +{ + num -= UNW_LOONGARCH_F24; + + if (num < 0 || num >= sizeof(F) / sizeof(uint64_t)) + { + PORTABILITY_ASSERT("unsupported loongarch64 vector register"); + } + + F[num] = (uint64_t)value.vec[2] << 32 | (uint64_t)value.vec[3]; +} + +#endif // TARGET_LOONGARCH64 + bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t format, unw_word_t unwind_info) { #if _LIBUNWIND_SUPPORT_DWARF_UNWIND @@ -818,6 +1100,12 @@ bool UnwindHelpers::StepFrame(REGDISPLAY *regs, unw_word_t start_ip, uint32_t fo int stepRet = compactInst.stepWithCompactEncoding(format, start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); return stepRet == UNW_STEP_SUCCESS; } +#elif defined(TARGET_LOONGARCH64) + if ((format & UNWIND_LOONGARCH64_MODE_MASK) != UNWIND_LOONGARCH64_MODE_DWARF) { + CompactUnwinder_loongarch64 compactInst; + int stepRet = compactInst.stepWithCompactEncoding(format, start_ip, _addressSpace, *(Registers_REGDISPLAY*)regs); + return stepRet == UNW_STEP_SUCCESS; + } #elif defined(TARGET_AMD64) if ((format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { CompactUnwinder_x86_64 compactInst; @@ -867,6 +1155,8 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio libunwind::UnwindCursor uc(_addressSpace); #elif defined(HOST_X86) libunwind::UnwindCursor uc(_addressSpace); +#elif defined(HOST_LOONGARCH64) + libunwind::UnwindCursor uc(_addressSpace); #else #error "Unwinding is not implemented for this architecture yet." #endif @@ -885,6 +1175,12 @@ bool UnwindHelpers::GetUnwindProcInfo(PCODE pc, UnwindInfoSections &uwInfoSectio } else { dwarfOffsetHint = procInfo->format & UNWIND_ARM64_DWARF_SECTION_OFFSET; } +#elif defined(TARGET_LOONGARCH64) + if ((procInfo->format & UNWIND_LOONGARCH64_MODE_MASK) != UNWIND_LOONGARCH64_MODE_DWARF) { + return true; + } else { + dwarfOffsetHint = procInfo->format & UNWIND_LOONGARCH64_DWARF_SECTION_OFFSET; + } #elif defined(TARGET_AMD64) if ((procInfo->format & UNWIND_X86_64_MODE_MASK) != UNWIND_X86_64_MODE_DWARF) { return true; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs index 06db5253c590c2..c7a96e12ddf177 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeSystemContextFactory.cs @@ -42,6 +42,8 @@ public static TypeSystemContext Create() TargetArchitecture.X64, #elif TARGET_WASM TargetArchitecture.Wasm32, +#elif TARGET_LOONGARCH64 + TargetArchitecture.LoongArch64, #else #error Unknown architecture #endif diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs index db4d1855e20fb3..8656626e854062 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_LoongArch64/LoongArch64ReadyToRunHelperNode.cs @@ -48,6 +48,7 @@ protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter enc case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; + encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index c04658df379f7a..54f80651288dad 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -62,14 +62,19 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, break; case ReadyToRunHelper.WriteBarrier: - mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpAssignRefArm64" : "RhpAssignRef"; + mangledName = context.Target.Architecture switch + { + TargetArchitecture.ARM64 => "RhpAssignRefArm64", + TargetArchitecture.LoongArch64 => "RhpAssignRefLoongArch64", + _ => "RhpAssignRef" + }; break; case ReadyToRunHelper.CheckedWriteBarrier: mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpCheckedAssignRefArm64" : "RhpCheckedAssignRef"; break; case ReadyToRunHelper.BulkWriteBarrier: - mangledName = "RhBuffer_BulkMoveWithWriteBarrier"; - break; + mangledName = "RhBuffer_BulkMoveWithWriteBarrier"; + break; case ReadyToRunHelper.ByRefWriteBarrier: mangledName = context.Target.Architecture == TargetArchitecture.ARM64 ? "RhpByRefAssignRefArm64" : "RhpByRefAssignRef"; break; diff --git a/src/native/external/llvm-libunwind/include/__libunwind_config.h b/src/native/external/llvm-libunwind/include/__libunwind_config.h index ecfe7be0d12f6c..d521890f17f869 100644 --- a/src/native/external/llvm-libunwind/include/__libunwind_config.h +++ b/src/native/external/llvm-libunwind/include/__libunwind_config.h @@ -173,8 +173,8 @@ #elif defined(__loongarch__) #define _LIBUNWIND_TARGET_LOONGARCH 1 #if __loongarch_grlen == 64 -#define _LIBUNWIND_CONTEXT_SIZE 65 -#define _LIBUNWIND_CURSOR_SIZE 77 +#define _LIBUNWIND_CONTEXT_SIZE 98 +#define _LIBUNWIND_CURSOR_SIZE 110 #elif defined(HOST_WASM) #define _LIBUNWIND_TARGET_WASM 1 // TODO: Determine the right values diff --git a/src/native/external/llvm-libunwind/src/Registers.hpp b/src/native/external/llvm-libunwind/src/Registers.hpp index 4e1d75519ef35d..b76f24ea67da55 100644 --- a/src/native/external/llvm-libunwind/src/Registers.hpp +++ b/src/native/external/llvm-libunwind/src/Registers.hpp @@ -5268,13 +5268,14 @@ class _LIBUNWIND_HIDDEN Registers_loongarch { bool validRegister(int num) const; uint64_t getRegister(int num) const; - void setRegister(int num, uint64_t value); + void setRegister(int num, uint64_t value, uint64_t location); bool validFloatRegister(int num) const; double getFloatRegister(int num) const; void setFloatRegister(int num, double value); bool validVectorRegister(int num) const; v128 getVectorRegister(int num) const; void setVectorRegister(int num, v128 value); + uint64_t getRegisterLocation(int num) const; static const char *getRegisterName(int num); void jumpto(); static constexpr int lastDwarfRegNum() { @@ -5283,17 +5284,23 @@ class _LIBUNWIND_HIDDEN Registers_loongarch { static int getArch() { return REGISTERS_LOONGARCH; } uint64_t getSP() const { return _registers.__r[3]; } - void setSP(uint64_t value) { _registers.__r[3] = value; } + void setSP(uint64_t value, uint64_t location) { _registers.__r[3] = value; } uint64_t getIP() const { return _registers.__pc; } - void setIP(uint64_t value) { _registers.__pc = value; } + void setIP(uint64_t value, uint64_t location) { _registers.__pc = value; } private: - struct loongarch_thread_state_t { + struct GPRs { + uint64_t __r[32]; + uint64_t __pc; + }; + + struct GPRLocations { uint64_t __r[32]; uint64_t __pc; }; - loongarch_thread_state_t _registers; + GPRs _registers; + GPRLocations _registerLocations; #if __loongarch_frlen == 64 double _floats[32]; #endif @@ -5303,6 +5310,7 @@ inline Registers_loongarch::Registers_loongarch(const void *registers) { static_assert((check_fit::does_fit), "loongarch registers do not fit into unw_context_t"); memcpy(&_registers, registers, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); static_assert(sizeof(_registers) == 0x108, "expected float registers to be at offset 264"); #if __loongarch_frlen == 64 @@ -5313,6 +5321,7 @@ inline Registers_loongarch::Registers_loongarch(const void *registers) { inline Registers_loongarch::Registers_loongarch() { memset(&_registers, 0, sizeof(_registers)); + memset(&_registerLocations, 0, sizeof(_registerLocations)); #if __loongarch_frlen == 64 memset(&_floats, 0, sizeof(_floats)); #endif @@ -5337,17 +5346,33 @@ inline uint64_t Registers_loongarch::getRegister(int regNum) const { _LIBUNWIND_ABORT("unsupported loongarch register"); } -inline void Registers_loongarch::setRegister(int regNum, uint64_t value) { - if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) +inline void Registers_loongarch::setRegister(int regNum, uint64_t value, uint64_t location) { + if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) { _registers.__r[regNum - UNW_LOONGARCH_R0] = value; - else if (regNum == UNW_REG_IP) + _registerLocations.__r[regNum - UNW_LOONGARCH_R0] = location; + } + else if (regNum == UNW_REG_IP) { _registers.__pc = value; - else if (regNum == UNW_REG_SP) + _registerLocations.__pc = location; + } + else if (regNum == UNW_REG_SP) { _registers.__r[3] = value; + _registerLocations.__r[3] = location; + } else _LIBUNWIND_ABORT("unsupported loongarch register"); } +inline uint64_t Registers_loongarch::getRegisterLocation(int regNum) const { + if (regNum == UNW_REG_IP) + return _registerLocations.__pc; + if (regNum == UNW_REG_SP) + return _registerLocations.__r[3]; + if ((regNum >= 0) && (regNum < 32)) + return _registerLocations.__r[regNum]; + _LIBUNWIND_ABORT("unsupported loongarch64 register"); +} + inline const char *Registers_loongarch::getRegisterName(int regNum) { switch (regNum) { case UNW_REG_IP: From a7efcd9ca9255dc9faa8b4a2761cdfdb62619610 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Sat, 6 Jul 2024 04:45:32 +0200 Subject: [PATCH 42/72] Update readAheadTask asserts (#104496) --- .../src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index a946074e41f4d2..c850389203d3d8 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -607,7 +607,7 @@ public async Task SendAsync(HttpRequestMessage request, boo // meaning that PrepareForReuse would have failed, and we wouldn't have called SendAsync. // The task therefore shouldn't be 'default', as it's representing an async operation that had to yield at some point. Debug.Assert(_readAheadTask != default); - Debug.Assert(_readAheadTaskStatus == ReadAheadTask_CompletionReserved); + Debug.Assert(_readAheadTaskStatus is ReadAheadTask_CompletionReserved or ReadAheadTask_Completed); // Handle the pre-emptive read. For the async==false case, hopefully the read has // already completed and this will be a nop, but if it hasn't, the caller will be forced to block @@ -852,7 +852,7 @@ public async Task SendAsync(HttpRequestMessage request, boo if (_readAheadTask != default) { - Debug.Assert(_readAheadTaskStatus == ReadAheadTask_CompletionReserved); + Debug.Assert(_readAheadTaskStatus is ReadAheadTask_CompletionReserved or ReadAheadTask_Completed); LogExceptions(_readAheadTask.AsTask()); } From 9efc798f850e2bb68c02382c393d2cb1e818eb45 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 6 Jul 2024 11:35:23 -0700 Subject: [PATCH 43/72] Fix stackoverflow message for repeated frames (#104508) Fixes #104495 --- src/coreclr/vm/eepolicy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index cefcdec06c1fd0..dbbc672ee36cd1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -265,7 +265,7 @@ class CallStackLogger if (m_largestCommonStartLength != 0) { SmallStackSString repeatStr; - repeatStr.AppendPrintf("Repeat %d times:\n", m_largestCommonStartRepeat); + repeatStr.AppendPrintf("Repeated %d times:\n", m_largestCommonStartRepeat); PrintToStdErrW(repeatStr.GetUnicode()); PrintToStdErrA("--------------------------------\n"); From 4addcaa7e36104390835355c5a98eb36ec0334d0 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Sat, 6 Jul 2024 17:35:46 -0700 Subject: [PATCH 44/72] Add some helper functions for getting the intrinsic ID to use for a given oper (#104498) * Add some helper functions for getting the intrinsic ID to use for a given oper * Make the Unix build happy * Make the Arm64 build happy * Respond to PR feedback * Ensure we don't use EVEX unnecessarily * Ensure zero diffs for x64 --- src/coreclr/jit/compiler.h | 16 + src/coreclr/jit/gentree.cpp | 3794 +++++++++++++++++--------------- src/coreclr/jit/gentree.h | 23 +- src/coreclr/jit/lowerxarch.cpp | 4 +- src/coreclr/jit/morph.cpp | 18 +- src/coreclr/jit/valuenum.cpp | 8 +- 6 files changed, 2036 insertions(+), 1827 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f2ea5ec78cab05..916f277a1275c7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2597,6 +2597,7 @@ class Compiler friend class FlowGraphNaturalLoop; #ifdef FEATURE_HW_INTRINSICS + friend struct GenTreeHWIntrinsic; friend struct HWIntrinsicInfo; friend struct SimdAsHWIntrinsicInfo; #endif // FEATURE_HW_INTRINSICS @@ -9569,6 +9570,21 @@ class Compiler // F+CD+DQ+BW+VL (such as VBMI) and should appear with a corresponding query around AVX512*_VL (i.e. AVX512_VBMI_VL) #ifdef DEBUG + //------------------------------------------------------------------------ + // IsBaselineVector256IsaSupportedDebugOnly - Does isa support exist for Vector256. + // + // Returns: + // `true` if AVX. + // + bool IsBaselineVector256IsaSupportedDebugOnly() const + { +#ifdef TARGET_XARCH + return compIsaSupportedDebugOnly(InstructionSet_AVX); +#else + return false; +#endif + } + //------------------------------------------------------------------------ // IsBaselineVector512IsaSupportedDebugOnly - Does isa support exist for Vector512. // diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index da54ff739d7558..4093f3b796bdfe 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -20730,196 +20730,17 @@ GenTree* Compiler::gtNewSimdBinOpNode( (op2->TypeIs(TYP_SIMD12) && (type == TYP_SIMD16))); } - NamedIntrinsic intrinsic = NI_Illegal; + bool needsReverseOps = false; + GenTree* op2ForLookup = nullptr; switch (op) { -#if defined(TARGET_XARCH) - case GT_ADD: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_Add; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_Add; - } - } - else if (simdSize == 64) - { - if (varTypeIsSmall(simdBaseType)) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512BW)); - intrinsic = NI_AVX512BW_Add; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Add; - } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_Add; - } - else - { - intrinsic = NI_SSE2_Add; - } - break; - } - - case GT_AND: - { - if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_And; - - if (varTypeIsIntegral(simdBaseType)) - { - intrinsic = NI_AVX512F_And; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX512DQ)) - { - intrinsic = NI_AVX512DQ_And; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - - intrinsic = NI_AVX512F_And; - simdBaseJitType = (simdBaseType == TYP_DOUBLE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_INT; - } - } - else if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_And; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX2)) - { - intrinsic = NI_AVX2_And; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - - intrinsic = NI_AVX_And; - simdBaseJitType = varTypeIsLong(simdBaseType) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; - } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_And; - } - else - { - intrinsic = NI_SSE2_And; - } - break; - } - - case GT_AND_NOT: - { - if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_AndNot; - - if (varTypeIsIntegral(simdBaseType)) - { - intrinsic = NI_AVX512F_AndNot; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX512DQ)) - { - intrinsic = NI_AVX512DQ_AndNot; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - - intrinsic = NI_AVX512F_AndNot; - simdBaseJitType = (simdBaseType == TYP_DOUBLE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_INT; - } - } - else if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_AndNot; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX2)) - { - intrinsic = NI_AVX2_AndNot; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - - intrinsic = NI_AVX_AndNot; - simdBaseJitType = varTypeIsLong(simdBaseType) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; - } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_AndNot; - } - else - { - intrinsic = NI_SSE2_AndNot; - } - - // GT_AND_NOT expects `op1 & ~op2`, but xarch does `~op1 & op2` - // We expect op1 to have already been spilled - - std::swap(op1, op2); - break; - } - case GT_DIV: { - // TODO-XARCH-CQ: We could support division by constant for integral types - assert(varTypeIsFloating(simdBaseType)); - if (varTypeIsArithmetic(op2)) { op2 = gtNewSimdCreateBroadcastNode(type, op2, simdBaseJitType, simdSize); } - - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - intrinsic = NI_AVX_Divide; - } - else if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Divide; - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_Divide; - } - else - { - intrinsic = NI_SSE2_Divide; - } break; } @@ -20941,8 +20762,6 @@ GenTree* Compiler::gtNewSimdBinOpNode( simdBaseType = TYP_LONG; } - assert(!varTypeIsByte(simdBaseType) || op == GT_RSZ); - // "over shifting" is platform specific behavior. We will match the C# behavior // this requires we mask with (sizeof(T) * 8) - 1 which ensures the shift cannot // exceed the number of bits available in `T`. This is roughly equivalent to @@ -20961,1014 +20780,494 @@ GenTree* Compiler::gtNewSimdBinOpNode( { op2 = gtNewOperNode(GT_AND, TYP_INT, op2, gtNewIconNode(shiftCountMask)); - if (varTypeIsByte(simdBaseType)) +#if defined(TARGET_XARCH) + op2ForLookup = op2; + op2 = gtNewSimdCreateScalarNode(TYP_SIMD16, op2, CORINFO_TYPE_INT, 16); +#elif defined(TARGET_ARM64) + if (op != GT_LSH) { - // Save the intermediate "shiftCount & shiftCountMask" value as we will need it to - // calculate which bits we should mask off after the 32-bit shift we use for 8-bit values. - nonConstantByteShiftCountOp = fgMakeMultiUse(&op2); + op2 = gtNewOperNode(GT_NEG, TYP_INT, op2); } - op2 = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op2, NI_SSE2_ConvertScalarToVector128Int32, CORINFO_TYPE_INT, - 16); + op2 = gtNewSimdCreateBroadcastNode(type, op2, simdBaseJitType, simdSize); +#endif // !TARGET_XARCH && !TARGET_ARM64 } + break; + } - if (simdSize == 32) + case GT_MUL: + { + GenTree** broadcastOp = nullptr; + +#if defined(TARGET_ARM64) + if (varTypeIsLong(simdBaseType)) { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); + break; + } +#endif // TARGET_ARM64 - if (op == GT_LSH) - { - intrinsic = NI_AVX2_ShiftLeftLogical; - } - else if (op == GT_RSH) - { - if (varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE)) - { - assert(varTypeIsSigned(simdBaseType)); - if (compOpportunisticallyDependsOn(InstructionSet_AVX10v1)) - { - intrinsic = NI_AVX10v1_ShiftRightArithmetic; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F_VL)); - intrinsic = NI_AVX512F_VL_ShiftRightArithmetic; - } - } - else - { - intrinsic = NI_AVX2_ShiftRightArithmetic; - } - } - else + if (varTypeIsArithmetic(op1)) + { + broadcastOp = &op1; + +#if defined(TARGET_ARM64) + if (!varTypeIsByte(simdBaseType)) { - assert(op == GT_RSZ); - intrinsic = NI_AVX2_ShiftRightLogical; + // MultiplyByScalar requires the scalar op to be op2fGetHWIntrinsicIdForBinOp + needsReverseOps = true; } +#endif // TARGET_ARM64 } - else if (simdSize == 64) + else if (varTypeIsArithmetic(op2)) { - assert(IsBaselineVector512IsaSupportedDebugOnly()); + broadcastOp = &op2; + } - if (op == GT_LSH) - { - if (varTypeIsShort(simdBaseType)) - { - intrinsic = NI_AVX512BW_ShiftLeftLogical; - } - else - { - intrinsic = NI_AVX512F_ShiftLeftLogical; - } - } - else if (op == GT_RSH) - { - if (varTypeIsShort(simdBaseType)) - { - intrinsic = NI_AVX512BW_ShiftRightArithmetic; - } - else - { - intrinsic = NI_AVX512F_ShiftRightArithmetic; - } - } - else + if (broadcastOp != nullptr) + { +#if defined(TARGET_ARM64) + if (!varTypeIsByte(simdBaseType)) { - assert(op == GT_RSZ); - if (varTypeIsShort(simdBaseType)) - { - intrinsic = NI_AVX512BW_ShiftRightLogical; - } - else - { - intrinsic = NI_AVX512F_ShiftRightLogical; - } + op2ForLookup = *broadcastOp; + *broadcastOp = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD8, *broadcastOp, simdBaseJitType, 8); + break; } +#endif // TARGET_ARM64 + + *broadcastOp = gtNewSimdCreateBroadcastNode(type, *broadcastOp, simdBaseJitType, simdSize); } - else if (op == GT_LSH) - { - intrinsic = NI_SSE2_ShiftLeftLogical; - } - else if (op == GT_RSH) + break; + } + +#if defined(TARGET_XARCH) + case GT_AND: + case GT_AND_NOT: + case GT_OR: + case GT_XOR: + { + if (simdSize == 32) { - if (varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE)) + if (varTypeIsIntegral(simdBaseType) && !compOpportunisticallyDependsOn(InstructionSet_AVX2)) { - assert(varTypeIsSigned(simdBaseType)); - if (compOpportunisticallyDependsOn(InstructionSet_AVX10v1)) + if (varTypeIsLong(simdBaseType)) { - intrinsic = NI_AVX10v1_ShiftRightArithmetic; + simdBaseJitType = CORINFO_TYPE_DOUBLE; + simdBaseType = TYP_DOUBLE; } else { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F_VL)); - intrinsic = NI_AVX512F_VL_ShiftRightArithmetic; + simdBaseJitType = CORINFO_TYPE_FLOAT; + simdBaseType = TYP_FLOAT; } } - else - { - intrinsic = NI_SSE2_ShiftRightArithmetic; - } } - else + + if (op == GT_AND_NOT) { - assert(op == GT_RSZ); - intrinsic = NI_SSE2_ShiftRightLogical; + // GT_AND_NOT expects `op1 & ~op2`, but xarch does `~op1 & op2` + needsReverseOps = true; } + break; + } +#endif // TARGET_XARCH - if (varTypeIsByte(simdBaseType)) - { - assert(op == GT_RSZ); + default: + { + break; + } + } - // We don't have actual instructions for shifting bytes, so we'll emulate them - // by shifting 32-bit values and masking off the bits that should be zeroed. - GenTree* maskAmountOp; + if (needsReverseOps) + { + // We expect op1 to have already been spilled if needed + std::swap(op1, op2); + } - if (op2->IsCnsIntOrI()) - { - ssize_t shiftCount = op2->AsIntCon()->gtIconVal; - ssize_t mask = 255 >> shiftCount; + if (op2ForLookup == nullptr) + { + op2ForLookup = op2; + } + else + { + assert(op2ForLookup != op1); + } - maskAmountOp = gtNewIconNode(mask, type); - } - else - { - assert(nonConstantByteShiftCountOp != NULL); + NamedIntrinsic intrinsic = + GenTreeHWIntrinsic::GetHWIntrinsicIdForBinOp(this, op, op1, op2ForLookup, simdBaseType, simdSize, false); - maskAmountOp = gtNewOperNode(GT_RSZ, TYP_INT, gtNewIconNode(255), nonConstantByteShiftCountOp); - } + if (intrinsic != NI_Illegal) + { + return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); + } - GenTree* shiftOp = gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, CORINFO_TYPE_INT, simdSize); - GenTree* maskOp = gtNewSimdCreateBroadcastNode(type, maskAmountOp, simdBaseJitType, simdSize); + switch (op) + { +#if defined(TARGET_XARCH) + case GT_RSZ: + { + // We don't have actual instructions for shifting bytes, so we'll emulate them + // by shifting 32-bit values and masking off the bits that should be zeroed. - return gtNewSimdBinOpNode(GT_AND, type, shiftOp, maskOp, simdBaseJitType, simdSize); - } - break; - } + assert(varTypeIsByte(simdBaseType)); - case GT_MUL: - { - GenTree** broadcastOp = nullptr; + intrinsic = + GenTreeHWIntrinsic::GetHWIntrinsicIdForBinOp(this, op, op1, op2ForLookup, TYP_INT, simdSize, false); + assert(intrinsic != NI_Illegal); - if (varTypeIsArithmetic(op1)) + GenTree* maskAmountOp; + + if (op2->IsCnsIntOrI()) { - broadcastOp = &op1; + ssize_t shiftCount = op2->AsIntCon()->gtIconVal; + ssize_t mask = 255 >> shiftCount; + + maskAmountOp = gtNewIconNode(mask, type); } - else if (varTypeIsArithmetic(op2)) + else { - broadcastOp = &op2; - } + assert(op2->OperIsHWIntrinsic(NI_Vector128_CreateScalar)); - if (broadcastOp != nullptr) - { - *broadcastOp = gtNewSimdCreateBroadcastNode(type, *broadcastOp, simdBaseJitType, simdSize); + GenTree* nonConstantByteShiftCountOp = fgMakeMultiUse(&op2->AsHWIntrinsic()->Op(1)); + maskAmountOp = gtNewOperNode(GT_RSZ, TYP_INT, gtNewIconNode(255), nonConstantByteShiftCountOp); } - switch (simdBaseType) + GenTree* shiftOp = gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, CORINFO_TYPE_INT, simdSize); + GenTree* maskOp = gtNewSimdCreateBroadcastNode(type, maskAmountOp, simdBaseJitType, simdSize); + + return gtNewSimdBinOpNode(GT_AND, type, shiftOp, maskOp, simdBaseJitType, simdSize); + } +#endif // TARGET_XARCH + + case GT_MUL: + { +#if defined(TARGET_XARCH) + if (varTypeIsByte(simdBaseType)) { - case TYP_BYTE: - case TYP_UBYTE: + assert((simdSize != 64) || IsBaselineVector512IsaSupportedDebugOnly()); + + CorInfoType widenedSimdBaseJitType; + NamedIntrinsic widenIntrinsic; + NamedIntrinsic narrowIntrinsic; + var_types widenedType; + unsigned widenedSimdSize; + bool isV512Supported = false; + + if (simdSize == 32 && IsBaselineVector512IsaSupportedOpportunistically()) { - assert((simdSize != 64) || IsBaselineVector512IsaSupportedDebugOnly()); + // Input is SIMD32 [U]Byte and AVX512BW is supported: + // - Widen inputs as SIMD64 [U]Short + // - Multiply widened inputs (SIMD64 [U]Short) as widened product (SIMD64 [U]Short) + // - Narrow widened product (SIMD64 [U]Short) as SIMD32 [U]Byte + if (simdBaseType == TYP_BYTE) + { + widenedSimdBaseJitType = CORINFO_TYPE_SHORT; + widenIntrinsic = NI_AVX512BW_ConvertToVector512Int16; + narrowIntrinsic = NI_AVX512BW_ConvertToVector256SByte; + } + else + { + widenedSimdBaseJitType = CORINFO_TYPE_USHORT; + widenIntrinsic = NI_AVX512BW_ConvertToVector512UInt16; + narrowIntrinsic = NI_AVX512BW_ConvertToVector256Byte; + } + + widenedType = TYP_SIMD64; + widenedSimdSize = 64; + + // Vector512 widenedOp1 = Avx512BW.ConvertToVector512UInt16(op1) + GenTree* widenedOp1 = + gtNewSimdHWIntrinsicNode(widenedType, op1, widenIntrinsic, simdBaseJitType, widenedSimdSize); - CorInfoType widenedSimdBaseJitType; - NamedIntrinsic widenIntrinsic; - NamedIntrinsic narrowIntrinsic; - var_types widenedType; - unsigned widenedSimdSize; - bool isV512Supported = false; + // Vector512 widenedOp2 = Avx512BW.ConvertToVector512UInt16(op2) + GenTree* widenedOp2 = + gtNewSimdHWIntrinsicNode(widenedType, op2, widenIntrinsic, simdBaseJitType, widenedSimdSize); - if (simdSize == 32 && IsBaselineVector512IsaSupportedOpportunistically()) + // Vector512 widenedProduct = widenedOp1 * widenedOp2; + GenTree* widenedProduct = gtNewSimdBinOpNode(GT_MUL, widenedType, widenedOp1, widenedOp2, + widenedSimdBaseJitType, widenedSimdSize); + + // Vector256 product = Avx512BW.ConvertToVector256Byte(widenedProduct) + return gtNewSimdHWIntrinsicNode(type, widenedProduct, narrowIntrinsic, widenedSimdBaseJitType, + widenedSimdSize); + } + else if (simdSize == 16 && compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + if (compIsEvexOpportunisticallySupported(isV512Supported)) { - // Input is SIMD32 [U]Byte and AVX512BW is supported: - // - Widen inputs as SIMD64 [U]Short - // - Multiply widened inputs (SIMD64 [U]Short) as widened product (SIMD64 [U]Short) - // - Narrow widened product (SIMD64 [U]Short) as SIMD32 [U]Byte + // Input is SIMD16 [U]Byte and AVX512BW_VL is supported: + // - Widen inputs as SIMD32 [U]Short + // - Multiply widened inputs (SIMD32 [U]Short) as widened product (SIMD32 [U]Short) + // - Narrow widened product (SIMD32 [U]Short) as SIMD16 [U]Byte + widenIntrinsic = NI_AVX2_ConvertToVector256Int16; + if (simdBaseType == TYP_BYTE) { widenedSimdBaseJitType = CORINFO_TYPE_SHORT; - widenIntrinsic = NI_AVX512BW_ConvertToVector512Int16; - narrowIntrinsic = NI_AVX512BW_ConvertToVector256SByte; + narrowIntrinsic = !isV512Supported ? NI_AVX10v1_ConvertToVector128SByte + : NI_AVX512BW_VL_ConvertToVector128SByte; } else { widenedSimdBaseJitType = CORINFO_TYPE_USHORT; - widenIntrinsic = NI_AVX512BW_ConvertToVector512UInt16; - narrowIntrinsic = NI_AVX512BW_ConvertToVector256Byte; + narrowIntrinsic = !isV512Supported ? NI_AVX10v1_ConvertToVector128Byte + : NI_AVX512BW_VL_ConvertToVector128Byte; } - widenedType = TYP_SIMD64; - widenedSimdSize = 64; + widenedType = TYP_SIMD32; + widenedSimdSize = 32; - // Vector512 widenedOp1 = Avx512BW.ConvertToVector512UInt16(op1) + // Vector256 widenedOp1 = Avx2.ConvertToVector256Int16(op1).AsUInt16() GenTree* widenedOp1 = gtNewSimdHWIntrinsicNode(widenedType, op1, widenIntrinsic, simdBaseJitType, widenedSimdSize); - // Vector512 widenedOp2 = Avx512BW.ConvertToVector512UInt16(op2) + // Vector256 widenedOp2 = Avx2.ConvertToVector256Int16(op2).AsUInt16() GenTree* widenedOp2 = gtNewSimdHWIntrinsicNode(widenedType, op2, widenIntrinsic, simdBaseJitType, widenedSimdSize); - // Vector512 widenedProduct = widenedOp1 * widenedOp2; + // Vector256 widenedProduct = widenedOp1 * widenedOp2 GenTree* widenedProduct = gtNewSimdBinOpNode(GT_MUL, widenedType, widenedOp1, widenedOp2, widenedSimdBaseJitType, widenedSimdSize); - // Vector256 product = Avx512BW.ConvertToVector256Byte(widenedProduct) + // Vector128 product = Avx512BW.VL.ConvertToVector128Byte(widenedProduct) return gtNewSimdHWIntrinsicNode(type, widenedProduct, narrowIntrinsic, widenedSimdBaseJitType, widenedSimdSize); } - else if (simdSize == 16 && compOpportunisticallyDependsOn(InstructionSet_AVX2)) + else { - if (compIsEvexOpportunisticallySupported(isV512Supported)) - { - // Input is SIMD16 [U]Byte and AVX512BW_VL is supported: - // - Widen inputs as SIMD32 [U]Short - // - Multiply widened inputs (SIMD32 [U]Short) as widened product (SIMD32 [U]Short) - // - Narrow widened product (SIMD32 [U]Short) as SIMD16 [U]Byte - widenIntrinsic = NI_AVX2_ConvertToVector256Int16; - - if (simdBaseType == TYP_BYTE) - { - widenedSimdBaseJitType = CORINFO_TYPE_SHORT; - narrowIntrinsic = !isV512Supported ? NI_AVX10v1_ConvertToVector128SByte - : NI_AVX512BW_VL_ConvertToVector128SByte; - } - else - { - widenedSimdBaseJitType = CORINFO_TYPE_USHORT; - narrowIntrinsic = !isV512Supported ? NI_AVX10v1_ConvertToVector128Byte - : NI_AVX512BW_VL_ConvertToVector128Byte; - } - - widenedType = TYP_SIMD32; - widenedSimdSize = 32; - - // Vector256 widenedOp1 = Avx2.ConvertToVector256Int16(op1).AsUInt16() - GenTree* widenedOp1 = gtNewSimdHWIntrinsicNode(widenedType, op1, widenIntrinsic, - simdBaseJitType, widenedSimdSize); - - // Vector256 widenedOp2 = Avx2.ConvertToVector256Int16(op2).AsUInt16() - GenTree* widenedOp2 = gtNewSimdHWIntrinsicNode(widenedType, op2, widenIntrinsic, - simdBaseJitType, widenedSimdSize); + // Input is SIMD16 [U]Byte and AVX512BW_VL is NOT supported (only AVX2 will be used): + // - Widen inputs as SIMD32 [U]Short + // - Multiply widened inputs (SIMD32 [U]Short) as widened product (SIMD32 [U]Short) + // - Mask widened product (SIMD32 [U]Short) to select relevant bits + // - Pack masked product so that relevant bits are packed together in upper and lower halves + // - Shuffle packed product so that relevant bits are placed together in the lower half + // - Select lower (SIMD16 [U]Byte) from shuffled product (SIMD32 [U]Short) + widenedSimdBaseJitType = simdBaseType == TYP_BYTE ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT; + widenIntrinsic = NI_AVX2_ConvertToVector256Int16; + widenedType = TYP_SIMD32; + widenedSimdSize = 32; + + // Vector256 widenedOp1 = Avx2.ConvertToVector256Int16(op1).AsUInt16() + GenTree* widenedOp1 = + gtNewSimdHWIntrinsicNode(widenedType, op1, widenIntrinsic, simdBaseJitType, simdSize); + + // Vector256 widenedOp2 = Avx2.ConvertToVector256Int16(op2).AsUInt16() + GenTree* widenedOp2 = + gtNewSimdHWIntrinsicNode(widenedType, op2, widenIntrinsic, simdBaseJitType, simdSize); + + // Vector256 widenedProduct = widenedOp1 * widenedOp2 + GenTree* widenedProduct = gtNewSimdBinOpNode(GT_MUL, widenedType, widenedOp1, widenedOp2, + widenedSimdBaseJitType, widenedSimdSize); - // Vector256 widenedProduct = widenedOp1 * widenedOp2 - GenTree* widenedProduct = gtNewSimdBinOpNode(GT_MUL, widenedType, widenedOp1, widenedOp2, - widenedSimdBaseJitType, widenedSimdSize); + // Vector256 vecCon1 = Vector256.Create(0x00FF00FF00FF00FF).AsUInt16() + GenTreeVecCon* vecCon1 = gtNewVconNode(widenedType); - // Vector128 product = Avx512BW.VL.ConvertToVector128Byte(widenedProduct) - return gtNewSimdHWIntrinsicNode(type, widenedProduct, narrowIntrinsic, - widenedSimdBaseJitType, widenedSimdSize); - } - else + for (unsigned i = 0; i < (widenedSimdSize / 8); i++) { - // Input is SIMD16 [U]Byte and AVX512BW_VL is NOT supported (only AVX2 will be used): - // - Widen inputs as SIMD32 [U]Short - // - Multiply widened inputs (SIMD32 [U]Short) as widened product (SIMD32 [U]Short) - // - Mask widened product (SIMD32 [U]Short) to select relevant bits - // - Pack masked product so that relevant bits are packed together in upper and lower halves - // - Shuffle packed product so that relevant bits are placed together in the lower half - // - Select lower (SIMD16 [U]Byte) from shuffled product (SIMD32 [U]Short) - widenedSimdBaseJitType = - simdBaseType == TYP_BYTE ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT; - widenIntrinsic = NI_AVX2_ConvertToVector256Int16; - widenedType = TYP_SIMD32; - widenedSimdSize = 32; - - // Vector256 widenedOp1 = Avx2.ConvertToVector256Int16(op1).AsUInt16() - GenTree* widenedOp1 = - gtNewSimdHWIntrinsicNode(widenedType, op1, widenIntrinsic, simdBaseJitType, simdSize); - - // Vector256 widenedOp2 = Avx2.ConvertToVector256Int16(op2).AsUInt16() - GenTree* widenedOp2 = - gtNewSimdHWIntrinsicNode(widenedType, op2, widenIntrinsic, simdBaseJitType, simdSize); - - // Vector256 widenedProduct = widenedOp1 * widenedOp2 - GenTree* widenedProduct = gtNewSimdBinOpNode(GT_MUL, widenedType, widenedOp1, widenedOp2, - widenedSimdBaseJitType, widenedSimdSize); - - // Vector256 vecCon1 = Vector256.Create(0x00FF00FF00FF00FF).AsUInt16() - GenTreeVecCon* vecCon1 = gtNewVconNode(widenedType); - - for (unsigned i = 0; i < (widenedSimdSize / 8); i++) - { - vecCon1->gtSimdVal.u64[i] = 0x00FF00FF00FF00FF; - } + vecCon1->gtSimdVal.u64[i] = 0x00FF00FF00FF00FF; + } - // Validate we can't use AVX512F_VL_TernaryLogic here - assert(!canUseEvexEncodingDebugOnly()); + // Validate we can't use AVX512F_VL_TernaryLogic here + assert(!canUseEvexEncodingDebugOnly()); - // Vector256 maskedProduct = Avx2.And(widenedProduct, vecCon1).AsInt16() - GenTree* maskedProduct = gtNewSimdBinOpNode(GT_AND, widenedType, widenedProduct, vecCon1, - widenedSimdBaseJitType, widenedSimdSize); - GenTree* maskedProductDup = fgMakeMultiUse(&maskedProduct); + // Vector256 maskedProduct = Avx2.And(widenedProduct, vecCon1).AsInt16() + GenTree* maskedProduct = gtNewSimdBinOpNode(GT_AND, widenedType, widenedProduct, vecCon1, + widenedSimdBaseJitType, widenedSimdSize); + GenTree* maskedProductDup = fgMakeMultiUse(&maskedProduct); - // Vector256 packedProduct = Avx2.PackUnsignedSaturate(maskedProduct, - // maskedProduct).AsUInt64() - GenTree* packedProduct = - gtNewSimdHWIntrinsicNode(widenedType, maskedProduct, maskedProductDup, - NI_AVX2_PackUnsignedSaturate, CORINFO_TYPE_UBYTE, - widenedSimdSize); + // Vector256 packedProduct = Avx2.PackUnsignedSaturate(maskedProduct, + // maskedProduct).AsUInt64() + GenTree* packedProduct = + gtNewSimdHWIntrinsicNode(widenedType, maskedProduct, maskedProductDup, + NI_AVX2_PackUnsignedSaturate, CORINFO_TYPE_UBYTE, widenedSimdSize); - CorInfoType permuteBaseJitType = - (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG; + CorInfoType permuteBaseJitType = + (simdBaseType == TYP_BYTE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_ULONG; - // Vector256 shuffledProduct = Avx2.Permute4x64(w1, 0xD8).AsByte() - GenTree* shuffledProduct = - gtNewSimdHWIntrinsicNode(widenedType, packedProduct, gtNewIconNode(SHUFFLE_WYZX), - NI_AVX2_Permute4x64, permuteBaseJitType, widenedSimdSize); + // Vector256 shuffledProduct = Avx2.Permute4x64(w1, 0xD8).AsByte() + GenTree* shuffledProduct = + gtNewSimdHWIntrinsicNode(widenedType, packedProduct, gtNewIconNode(SHUFFLE_WYZX), + NI_AVX2_Permute4x64, permuteBaseJitType, widenedSimdSize); - // Vector128 product = shuffledProduct.getLower() - return gtNewSimdGetLowerNode(type, shuffledProduct, simdBaseJitType, widenedSimdSize); - } + // Vector128 product = shuffledProduct.getLower() + return gtNewSimdGetLowerNode(type, shuffledProduct, simdBaseJitType, widenedSimdSize); } + } - // No special handling could be performed, apply fallback logic: - // - Widen both inputs lower and upper halves as [U]Short (using helper method) - // - Multiply corrsponding widened input halves together as widened product halves - // - Narrow widened product halves as [U]Byte (using helper method) - widenedSimdBaseJitType = simdBaseType == TYP_BYTE ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT; - - // op1Dup = op1 - GenTree* op1Dup = fgMakeMultiUse(&op1); - - // op2Dup = op2 - GenTree* op2Dup = fgMakeMultiUse(&op2); + // No special handling could be performed, apply fallback logic: + // - Widen both inputs lower and upper halves as [U]Short (using helper method) + // - Multiply corrsponding widened input halves together as widened product halves + // - Narrow widened product halves as [U]Byte (using helper method) + widenedSimdBaseJitType = simdBaseType == TYP_BYTE ? CORINFO_TYPE_SHORT : CORINFO_TYPE_USHORT; - // Vector256 lowerOp1 = Avx2.ConvertToVector256Int16(op1.GetLower()).AsUInt16() - GenTree* lowerOp1 = gtNewSimdWidenLowerNode(type, op1, simdBaseJitType, simdSize); + // op1Dup = op1 + GenTree* op1Dup = fgMakeMultiUse(&op1); - // Vector256 lowerOp2 = Avx2.ConvertToVector256Int16(op2.GetLower()).AsUInt16() - GenTree* lowerOp2 = gtNewSimdWidenLowerNode(type, op2, simdBaseJitType, simdSize); + // op2Dup = op2 + GenTree* op2Dup = fgMakeMultiUse(&op2); - // Vector256 lowerProduct = lowerOp1 * lowerOp2 - GenTree* lowerProduct = - gtNewSimdBinOpNode(GT_MUL, type, lowerOp1, lowerOp2, widenedSimdBaseJitType, simdSize); + // Vector256 lowerOp1 = Avx2.ConvertToVector256Int16(op1.GetLower()).AsUInt16() + GenTree* lowerOp1 = gtNewSimdWidenLowerNode(type, op1, simdBaseJitType, simdSize); - // Vector256 upperOp1 = Avx2.ConvertToVector256Int16(op1.GetUpper()).AsUInt16() - GenTree* upperOp1 = gtNewSimdWidenUpperNode(type, op1Dup, simdBaseJitType, simdSize); + // Vector256 lowerOp2 = Avx2.ConvertToVector256Int16(op2.GetLower()).AsUInt16() + GenTree* lowerOp2 = gtNewSimdWidenLowerNode(type, op2, simdBaseJitType, simdSize); - // Vector256 upperOp2 = Avx2.ConvertToVector256Int16(op2.GetUpper()).AsUInt16() - GenTree* upperOp2 = gtNewSimdWidenUpperNode(type, op2Dup, simdBaseJitType, simdSize); + // Vector256 lowerProduct = lowerOp1 * lowerOp2 + GenTree* lowerProduct = + gtNewSimdBinOpNode(GT_MUL, type, lowerOp1, lowerOp2, widenedSimdBaseJitType, simdSize); - // Vector256 upperProduct = upperOp1 * upperOp2 - GenTree* upperProduct = - gtNewSimdBinOpNode(GT_MUL, type, upperOp1, upperOp2, widenedSimdBaseJitType, simdSize); + // Vector256 upperOp1 = Avx2.ConvertToVector256Int16(op1.GetUpper()).AsUInt16() + GenTree* upperOp1 = gtNewSimdWidenUpperNode(type, op1Dup, simdBaseJitType, simdSize); - // Narrow and merge halves using helper method - return gtNewSimdNarrowNode(type, lowerProduct, upperProduct, simdBaseJitType, simdSize); - } + // Vector256 upperOp2 = Avx2.ConvertToVector256Int16(op2.GetUpper()).AsUInt16() + GenTree* upperOp2 = gtNewSimdWidenUpperNode(type, op2Dup, simdBaseJitType, simdSize); - case TYP_SHORT: - case TYP_USHORT: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_MultiplyLow; - } - else if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512BW)); - intrinsic = NI_AVX512BW_MultiplyLow; - } - else - { - intrinsic = NI_SSE2_MultiplyLow; - } - break; - } + // Vector256 upperProduct = upperOp1 * upperOp2 + GenTree* upperProduct = + gtNewSimdBinOpNode(GT_MUL, type, upperOp1, upperOp2, widenedSimdBaseJitType, simdSize); - case TYP_INT: - case TYP_UINT: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_MultiplyLow; - } - else if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_MultiplyLow; - } - else if (compOpportunisticallyDependsOn(InstructionSet_SSE41)) - { - intrinsic = NI_SSE41_MultiplyLow; - } - else - { - // op1Dup = op1 - GenTree* op1Dup = fgMakeMultiUse(&op1); + // Narrow and merge halves using helper method + return gtNewSimdNarrowNode(type, lowerProduct, upperProduct, simdBaseJitType, simdSize); + } + else if (varTypeIsInt(simdBaseType)) + { + // op1Dup = op1 + GenTree* op1Dup = fgMakeMultiUse(&op1); - // op2Dup = op2 - GenTree* op2Dup = fgMakeMultiUse(&op2); + // op2Dup = op2 + GenTree* op2Dup = fgMakeMultiUse(&op2); - // op1Dup = Sse2.ShiftRightLogical128BitLane(op1Dup, 4) - op1Dup = - gtNewSimdHWIntrinsicNode(type, op1Dup, gtNewIconNode(4, TYP_INT), - NI_SSE2_ShiftRightLogical128BitLane, simdBaseJitType, simdSize); + // op1Dup = Sse2.ShiftRightLogical128BitLane(op1Dup, 4) + op1Dup = gtNewSimdHWIntrinsicNode(type, op1Dup, gtNewIconNode(4, TYP_INT), + NI_SSE2_ShiftRightLogical128BitLane, simdBaseJitType, simdSize); - // op2Dup = Sse2.ShiftRightLogical128BitLane(op2Dup, 4) - op2Dup = - gtNewSimdHWIntrinsicNode(type, op2Dup, gtNewIconNode(4, TYP_INT), - NI_SSE2_ShiftRightLogical128BitLane, simdBaseJitType, simdSize); + // op2Dup = Sse2.ShiftRightLogical128BitLane(op2Dup, 4) + op2Dup = gtNewSimdHWIntrinsicNode(type, op2Dup, gtNewIconNode(4, TYP_INT), + NI_SSE2_ShiftRightLogical128BitLane, simdBaseJitType, simdSize); - // op2Dup = Sse2.Multiply(op1Dup.AsUInt32(), op2Dup.AsUInt32()).AsInt32() - op2Dup = gtNewSimdHWIntrinsicNode(type, op1Dup, op2Dup, NI_SSE2_Multiply, CORINFO_TYPE_ULONG, - simdSize); + // op2Dup = Sse2.Multiply(op1Dup.AsUInt32(), op2Dup.AsUInt32()).AsInt32() + op2Dup = gtNewSimdHWIntrinsicNode(type, op1Dup, op2Dup, NI_SSE2_Multiply, CORINFO_TYPE_ULONG, simdSize); - // op2Dup = Sse2.Shuffle(op2Dup, (0, 0, 2, 0)) - op2Dup = gtNewSimdHWIntrinsicNode(type, op2Dup, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), - NI_SSE2_Shuffle, simdBaseJitType, simdSize); + // op2Dup = Sse2.Shuffle(op2Dup, (0, 0, 2, 0)) + op2Dup = gtNewSimdHWIntrinsicNode(type, op2Dup, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), NI_SSE2_Shuffle, + simdBaseJitType, simdSize); - // op1 = Sse2.Multiply(op1.AsUInt32(), op2.AsUInt32()).AsInt32() - op1 = gtNewSimdHWIntrinsicNode(type, op1, op2, NI_SSE2_Multiply, CORINFO_TYPE_ULONG, simdSize); + // op1 = Sse2.Multiply(op1.AsUInt32(), op2.AsUInt32()).AsInt32() + op1 = gtNewSimdHWIntrinsicNode(type, op1, op2, NI_SSE2_Multiply, CORINFO_TYPE_ULONG, simdSize); - // op1 = Sse2.Shuffle(op1, (0, 0, 2, 0)) - op1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), NI_SSE2_Shuffle, - simdBaseJitType, simdSize); + // op1 = Sse2.Shuffle(op1, (0, 0, 2, 0)) + op1 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), NI_SSE2_Shuffle, + simdBaseJitType, simdSize); - // op2 = op2Dup; - op2 = op2Dup; + // op2 = op2Dup; + op2 = op2Dup; - // result = Sse2.UnpackLow(op1, op2) - intrinsic = NI_SSE2_UnpackLow; - } - break; - } + // result = Sse2.UnpackLow(op1, op2) + return gtNewSimdHWIntrinsicNode(type, op1, op2, NI_SSE2_UnpackLow, simdBaseJitType, simdSize); + } + else if (varTypeIsLong(simdBaseType)) + { + assert((simdSize == 16) || (simdSize == 32) || (simdSize == 64)); - case TYP_LONG: - case TYP_ULONG: - { - assert((simdSize == 16) || (simdSize == 32) || (simdSize == 64)); + assert(((simdSize == 16) && compOpportunisticallyDependsOn(InstructionSet_SSE41)) || + ((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2))); - bool isV512Supported = false; - if (compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512DQ_VL)) - { - if (simdSize == 64) - { - assert(isV512Supported); - intrinsic = NI_AVX512DQ_MultiplyLow; - } - else - { - intrinsic = !isV512Supported ? NI_AVX10v1_MultiplyLow : NI_AVX512DQ_VL_MultiplyLow; - } - } - else - { - assert(((simdSize == 16) && compOpportunisticallyDependsOn(InstructionSet_SSE41)) || - ((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2))); - - // Make op1 and op2 multi-use: - GenTree* op1Dup = fgMakeMultiUse(&op1); - GenTree* op2Dup = fgMakeMultiUse(&op2); - - const bool is256 = simdSize == 32; - - // Vector256 tmp0 = Avx2.Multiply(left, right); - GenTreeHWIntrinsic* tmp0 = - gtNewSimdHWIntrinsicNode(type, op1, op2, is256 ? NI_AVX2_Multiply : NI_SSE2_Multiply, - CORINFO_TYPE_ULONG, simdSize); - - // Vector256 tmp1 = Avx2.Shuffle(right.AsUInt32(), ZWXY); - GenTree* shuffleMask = gtNewIconNode(SHUFFLE_ZWXY, TYP_INT); - GenTreeHWIntrinsic* tmp1 = gtNewSimdHWIntrinsicNode(type, op2Dup, shuffleMask, - is256 ? NI_AVX2_Shuffle : NI_SSE2_Shuffle, - CORINFO_TYPE_UINT, simdSize); - - // Vector256 tmp2 = Avx2.MultiplyLow(left.AsUInt32(), tmp1); - GenTreeHWIntrinsic* tmp2 = - gtNewSimdHWIntrinsicNode(type, op1Dup, tmp1, - is256 ? NI_AVX2_MultiplyLow : NI_SSE41_MultiplyLow, - CORINFO_TYPE_UINT, simdSize); - - // Vector256 tmp3 = Avx2.HorizontalAdd(tmp2.AsInt32(), Vector256.Zero); - GenTreeHWIntrinsic* tmp3 = - gtNewSimdHWIntrinsicNode(type, tmp2, gtNewZeroConNode(type), - is256 ? NI_AVX2_HorizontalAdd : NI_SSSE3_HorizontalAdd, - CORINFO_TYPE_UINT, simdSize); - - // Vector256 tmp4 = Avx2.Shuffle(tmp3, YWXW); - shuffleMask = gtNewIconNode(SHUFFLE_YWXW, TYP_INT); - GenTreeHWIntrinsic* tmp4 = - gtNewSimdHWIntrinsicNode(type, tmp3, shuffleMask, is256 ? NI_AVX2_Shuffle : NI_SSE2_Shuffle, - CORINFO_TYPE_UINT, simdSize); - - // result = tmp0 + tmp4; - op1 = tmp0; - op2 = tmp4; - intrinsic = simdSize == 32 ? NI_AVX2_Add : NI_SSE2_Add; - } + // Make op1 and op2 multi-use: + GenTree* op1Dup = fgMakeMultiUse(&op1); + GenTree* op2Dup = fgMakeMultiUse(&op2); - break; - } + const bool is256 = simdSize == 32; - case TYP_FLOAT: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - intrinsic = NI_AVX_Multiply; - } - else if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Multiply; - } - else - { - intrinsic = NI_SSE_Multiply; - } - break; - } + // Vector256 tmp0 = Avx2.Multiply(left, right); + GenTreeHWIntrinsic* tmp0 = + gtNewSimdHWIntrinsicNode(type, op1, op2, is256 ? NI_AVX2_Multiply : NI_SSE2_Multiply, + CORINFO_TYPE_ULONG, simdSize); - case TYP_DOUBLE: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - intrinsic = NI_AVX_Multiply; - } - else if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Multiply; - } - else - { - intrinsic = NI_SSE2_Multiply; - } - break; - } + // Vector256 tmp1 = Avx2.Shuffle(right.AsUInt32(), ZWXY); + GenTree* shuffleMask = gtNewIconNode(SHUFFLE_ZWXY, TYP_INT); + GenTreeHWIntrinsic* tmp1 = + gtNewSimdHWIntrinsicNode(type, op2Dup, shuffleMask, is256 ? NI_AVX2_Shuffle : NI_SSE2_Shuffle, + CORINFO_TYPE_UINT, simdSize); - default: - { - unreached(); - } - } - break; - } + // Vector256 tmp2 = Avx2.MultiplyLow(left.AsUInt32(), tmp1); + GenTree* tmp2 = gtNewSimdBinOpNode(GT_MUL, type, op1Dup, tmp1, CORINFO_TYPE_UINT, simdSize); - case GT_OR: - { - if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Or; + // Vector256 tmp3 = Avx2.HorizontalAdd(tmp2.AsInt32(), Vector256.Zero); + GenTreeHWIntrinsic* tmp3 = + gtNewSimdHWIntrinsicNode(type, tmp2, gtNewZeroConNode(type), + is256 ? NI_AVX2_HorizontalAdd : NI_SSSE3_HorizontalAdd, CORINFO_TYPE_UINT, + simdSize); - if (varTypeIsIntegral(simdBaseType)) - { - intrinsic = NI_AVX512F_Or; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX512DQ)) - { - intrinsic = NI_AVX512DQ_Or; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction + // Vector256 tmp4 = Avx2.Shuffle(tmp3, YWXW); + shuffleMask = gtNewIconNode(SHUFFLE_YWXW, TYP_INT); + GenTreeHWIntrinsic* tmp4 = + gtNewSimdHWIntrinsicNode(type, tmp3, shuffleMask, is256 ? NI_AVX2_Shuffle : NI_SSE2_Shuffle, + CORINFO_TYPE_UINT, simdSize); - intrinsic = NI_AVX512F_Or; - simdBaseJitType = (simdBaseType == TYP_DOUBLE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_INT; - } + // result = tmp0 + tmp4; + return gtNewSimdBinOpNode(GT_ADD, type, tmp0, tmp4, simdBaseJitType, simdSize); } - else if (simdSize == 32) +#elif defined(TARGET_ARM64) + if (varTypeIsLong(simdBaseType)) { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + GenTree** op1ToDup = &op1; + GenTree** op2ToDup = &op2; - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_Or; - } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX2)) - { - intrinsic = NI_AVX2_Or; - } - else + if (!varTypeIsArithmetic(op1)) { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - - intrinsic = NI_AVX_Or; - simdBaseJitType = varTypeIsLong(simdBaseType) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; + op1 = gtNewSimdToScalarNode(TYP_LONG, op1, simdBaseJitType, simdSize); + op1ToDup = &op1->AsHWIntrinsic()->Op(1); } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_Or; - } - else - { - intrinsic = NI_SSE2_Or; - } - break; - } - - case GT_SUB: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_Subtract; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_Subtract; - } - } - else if (simdSize == 64) - { - if (varTypeIsSmall(simdBaseType)) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512BW)); - intrinsic = NI_AVX512BW_Subtract; - } - else + if (!varTypeIsArithmetic(op2)) { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Subtract; + op2 = gtNewSimdToScalarNode(TYP_LONG, op2, simdBaseJitType, simdSize); + op2ToDup = &op2->AsHWIntrinsic()->Op(1); } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_Subtract; - } - else - { - intrinsic = NI_SSE2_Subtract; - } - break; - } - case GT_XOR: - { - if (simdSize == 64) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX512F)); - intrinsic = NI_AVX512F_Xor; + // lower = op1.GetElement(0) * op2.GetElement(0) + GenTree* lower = gtNewOperNode(GT_MUL, TYP_LONG, op1, op2); + lower = gtNewSimdCreateScalarUnsafeNode(type, lower, simdBaseJitType, simdSize); - if (varTypeIsIntegral(simdBaseType)) + if (simdSize == 8) { - intrinsic = NI_AVX512F_Xor; + // return Vector64.CreateScalarUnsafe(lower) + return lower; } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX512DQ)) - { - intrinsic = NI_AVX512DQ_Xor; - } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - intrinsic = NI_AVX512F_Xor; - simdBaseJitType = (simdBaseType == TYP_DOUBLE) ? CORINFO_TYPE_LONG : CORINFO_TYPE_INT; - } - } - else if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + // Make the original op1 and op2 multi-use: + GenTree* op1Dup = fgMakeMultiUse(op1ToDup); + GenTree* op2Dup = fgMakeMultiUse(op2ToDup); - if (varTypeIsFloating(simdBaseType)) + if (!varTypeIsArithmetic(op1Dup)) { - intrinsic = NI_AVX_Xor; + op1Dup = gtNewSimdGetElementNode(TYP_LONG, op1Dup, gtNewIconNode(1), simdBaseJitType, simdSize); } - else if (compOpportunisticallyDependsOn(InstructionSet_AVX2)) + + if (!varTypeIsArithmetic(op2Dup)) { - intrinsic = NI_AVX2_Xor; + op2Dup = gtNewSimdGetElementNode(TYP_LONG, op2Dup, gtNewIconNode(1), simdBaseJitType, simdSize); } - else - { - // Since this is a bitwise operation, we can still support it by lying - // about the type and doing the operation using a supported instruction - intrinsic = NI_AVX_Xor; - simdBaseJitType = varTypeIsLong(simdBaseType) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; - } - } - else if (simdBaseType == TYP_FLOAT) - { - intrinsic = NI_SSE_Xor; - } - else - { - intrinsic = NI_SSE2_Xor; - } - break; - } -#elif defined(TARGET_ARM64) - case GT_ADD: - { - if (simdBaseType == TYP_DOUBLE) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_AddScalar : NI_AdvSimd_Arm64_Add; - } - else if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - intrinsic = NI_AdvSimd_AddScalar; - } - else - { - intrinsic = NI_AdvSimd_Add; + // upper = op1.GetElement(1) * op2.GetElement(1) + GenTree* upper = gtNewOperNode(GT_MUL, TYP_LONG, op1Dup, op2Dup); + + // return Vector128.Create(lower, upper) + return gtNewSimdWithElementNode(type, lower, gtNewIconNode(1), upper, simdBaseJitType, simdSize); } - break; +#endif // !TARGET_XARCH && !TARGET_ARM64 + unreached(); } - case GT_AND: + default: { - intrinsic = NI_AdvSimd_And; - break; + unreached(); } + } +} - case GT_AND_NOT: - { - intrinsic = NI_AdvSimd_BitwiseClear; - break; - } - - case GT_DIV: - { - // TODO-AARCH-CQ: We could support division by constant for integral types - assert(varTypeIsFloating(simdBaseType)); - - if (varTypeIsArithmetic(op2)) - { - op2 = gtNewSimdCreateBroadcastNode(type, op2, simdBaseJitType, simdSize); - } - - if ((simdSize == 8) && (simdBaseType == TYP_DOUBLE)) - { - intrinsic = NI_AdvSimd_DivideScalar; - } - else - { - intrinsic = NI_AdvSimd_Arm64_Divide; - } - break; - } - - case GT_LSH: - case GT_RSH: - case GT_RSZ: - { - assert((op != GT_RSH) || !varTypeIsUnsigned(simdBaseType)); - - // float and double don't have actual instructions for shifting - // so we'll just use the equivalent integer instruction instead. - - if (simdBaseType == TYP_FLOAT) - { - simdBaseJitType = CORINFO_TYPE_INT; - simdBaseType = TYP_INT; - } - else if (simdBaseType == TYP_DOUBLE) - { - simdBaseJitType = CORINFO_TYPE_LONG; - simdBaseType = TYP_LONG; - } - - // "over shifting" is platform specific behavior. We will match the C# behavior - // this requires we mask with (sizeof(T) * 8) - 1 which ensures the shift cannot - // exceed the number of bits available in `T`. This is roughly equivalent to - // x % (sizeof(T) * 8), but that is "more expensive" and only the same for unsigned - // inputs, where-as we have a signed-input and so negative values would differ. - - unsigned shiftCountMask = (genTypeSize(simdBaseType) * 8) - 1; - - if (op2->IsCnsIntOrI()) - { - op2->AsIntCon()->gtIconVal &= shiftCountMask; - - if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - if (op == GT_LSH) - { - intrinsic = NI_AdvSimd_ShiftLeftLogicalScalar; - } - else if (op == GT_RSH) - { - intrinsic = NI_AdvSimd_ShiftRightArithmeticScalar; - } - else - { - assert(op == GT_RSZ); - intrinsic = NI_AdvSimd_ShiftRightLogicalScalar; - } - } - else if (op == GT_LSH) - { - intrinsic = NI_AdvSimd_ShiftLeftLogical; - } - else if (op == GT_RSH) - { - intrinsic = NI_AdvSimd_ShiftRightArithmetic; - } - else - { - assert(op == GT_RSZ); - intrinsic = NI_AdvSimd_ShiftRightLogical; - } - } - else - { - op2 = gtNewOperNode(GT_AND, TYP_INT, op2, gtNewIconNode(shiftCountMask)); - - if (op != GT_LSH) - { - op2 = gtNewOperNode(GT_NEG, TYP_INT, op2); - } - - op2 = gtNewSimdCreateBroadcastNode(type, op2, simdBaseJitType, simdSize); - - if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - if (op == GT_LSH) - { - intrinsic = NI_AdvSimd_ShiftLogicalScalar; - } - else if (op == GT_RSH) - { - intrinsic = NI_AdvSimd_ShiftArithmeticScalar; - } - else - { - intrinsic = NI_AdvSimd_ShiftLogicalScalar; - } - } - else if (op == GT_LSH) - { - intrinsic = NI_AdvSimd_ShiftLogical; - } - else if (op == GT_RSH) - { - intrinsic = NI_AdvSimd_ShiftArithmetic; - } - else - { - assert(op == GT_RSZ); - intrinsic = NI_AdvSimd_ShiftLogical; - } - } - break; - } - - case GT_MUL: - { - GenTree** scalarOp = nullptr; - - if (varTypeIsArithmetic(op1)) - { - // MultiplyByScalar requires the scalar op to be op2 - std::swap(op1, op2); - scalarOp = &op2; - } - else if (varTypeIsArithmetic(op2)) - { - scalarOp = &op2; - } - - switch (JitType2PreciseVarType(simdBaseJitType)) - { - case TYP_BYTE: - case TYP_UBYTE: - { - if (scalarOp != nullptr) - { - *scalarOp = gtNewSimdCreateBroadcastNode(type, *scalarOp, simdBaseJitType, simdSize); - } - intrinsic = NI_AdvSimd_Multiply; - break; - } - - case TYP_SHORT: - case TYP_USHORT: - case TYP_INT: - case TYP_UINT: - case TYP_FLOAT: - { - if (scalarOp != nullptr) - { - intrinsic = NI_AdvSimd_MultiplyByScalar; - *scalarOp = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD8, *scalarOp, simdBaseJitType, 8); - } - else - { - intrinsic = NI_AdvSimd_Multiply; - } - break; - } - - case TYP_DOUBLE: - { - if (scalarOp != nullptr) - { - intrinsic = NI_AdvSimd_Arm64_MultiplyByScalar; - *scalarOp = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD8, *scalarOp, simdBaseJitType, 8); - } - else - { - intrinsic = NI_AdvSimd_Arm64_Multiply; - } - - if (simdSize == 8) - { - intrinsic = NI_AdvSimd_MultiplyScalar; - } - break; - } - - case TYP_LONG: - case TYP_ULONG: - { - if (simdSize == 8) - { - // Vector64 vec = Vector64.CreateScalar(op1.ToScalar() * op2.ToScalar()) - op1 = gtNewBitCastNode(TYP_LONG, op1); - op2 = gtNewBitCastNode(TYP_LONG, op2); - GenTreeOp* mul = gtNewOperNode(GT_MUL, TYP_LONG, op1, op2); - return gtNewSimdCreateScalarNode(TYP_SIMD8, mul, simdBaseJitType, 8); - } - - // Make op1 and op2 multi-use: - GenTree* op1Dup = fgMakeMultiUse(&op1); - GenTree* op2Dup = fgMakeMultiUse(&op2); - - // long left0 = op1.GetElement(0) - // long right0 = op2.GetElement(0) - GenTree* left0 = gtNewSimdToScalarNode(TYP_LONG, op1, simdBaseJitType, 16); - GenTree* right0 = - scalarOp != nullptr ? op2 : gtNewSimdToScalarNode(TYP_LONG, op2, simdBaseJitType, 16); - - // long left1 = op1.GetElement(1) - // long right1 = op2.GetElement(1) - GenTree* left1 = gtNewSimdGetElementNode(TYP_LONG, op1Dup, gtNewIconNode(1), simdBaseJitType, 16); - GenTree* right1 = scalarOp != nullptr ? op2Dup - : gtNewSimdGetElementNode(TYP_LONG, op2Dup, gtNewIconNode(1), - simdBaseJitType, 16); - - // Vector128 vec = Vector128.Create(left0 * right0, left1 * right1) - op1 = gtNewOperNode(GT_MUL, TYP_LONG, left0, right0); - op2 = gtNewOperNode(GT_MUL, TYP_LONG, left1, right1); - GenTree* vec = gtNewSimdCreateScalarUnsafeNode(type, op1, simdBaseJitType, 16); - return gtNewSimdWithElementNode(type, vec, gtNewIconNode(1), op2, simdBaseJitType, 16); - } - - default: - { - unreached(); - } - } - break; - } - - case GT_OR: - { - intrinsic = NI_AdvSimd_Or; - break; - } - - case GT_SUB: - { - if (simdBaseType == TYP_DOUBLE) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_SubtractScalar : NI_AdvSimd_Arm64_Subtract; - } - else if ((simdSize == 8) && varTypeIsLong(simdBaseType)) - { - intrinsic = NI_AdvSimd_SubtractScalar; - } - else - { - intrinsic = NI_AdvSimd_Subtract; - } - break; - } - - case GT_XOR: - { - intrinsic = NI_AdvSimd_Xor; - break; - } -#else -#error Unsupported platform -#endif // !TARGET_XARCH && !TARGET_ARM64 - - default: - { - unreached(); - } - } - - assert(intrinsic != NI_Illegal); - return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); -} - -GenTree* Compiler::gtNewSimdCeilNode(var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize) -{ - assert(IsBaselineSimdIsaSupportedDebugOnly()); +GenTree* Compiler::gtNewSimdCeilNode(var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize) +{ + assert(IsBaselineSimdIsaSupportedDebugOnly()); assert(varTypeIsSIMD(type)); assert(getSIMDTypeForSize(simdSize) == type); @@ -22454,167 +21753,157 @@ GenTree* Compiler::gtNewSimdCmpOpNode( var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); assert(varTypeIsArithmetic(simdBaseType)); - NamedIntrinsic intrinsic = NI_Illegal; - bool needsConvertMaskToVector = false; + bool needsConvertMaskToVector = false; +#if defined(TARGET_XARCH) switch (op) { -#if defined(TARGET_XARCH) case GT_EQ: { - if (simdSize == 64) - { - assert(canUseEvexEncodingDebugOnly()); - intrinsic = NI_EVEX_CompareEqualMask; - needsConvertMaskToVector = true; - } - else if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_CompareEqual; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_CompareEqual; - } - } - else if (simdBaseType == TYP_FLOAT) - { - assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16)); - intrinsic = NI_SSE_CompareEqual; - } - else if (varTypeIsLong(simdBaseType)) - { - assert(simdSize == 16); - - if (compOpportunisticallyDependsOn(InstructionSet_SSE41)) - { - intrinsic = NI_SSE41_CompareEqual; - } - else - { - // There is no direct SSE2 support for comparing TYP_LONG vectors. - // These have to be implemented in terms of TYP_INT vector comparison operations. - // - // tmp = (op1 == op2) i.e. compare for equality as if op1 and op2 are vector of int - // op1 = tmp - // op2 = Shuffle(op1, (2, 3, 0, 1)) - // result = BitwiseAnd(tmp, op2) - // - // Shuffle is meant to swap the comparison results of low-32-bits and high 32-bits of - // respective long elements. + needsConvertMaskToVector = (simdSize == 64); + break; + } - GenTree* tmp = gtNewSimdCmpOpNode(op, type, op1, op2, CORINFO_TYPE_INT, simdSize); + case GT_GE: + case GT_LE: + { + needsConvertMaskToVector = (simdSize == 64) || (varTypeIsIntegral(simdBaseType) && canUseEvexEncoding()); + break; + } - op1 = fgMakeMultiUse(&tmp); - op2 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(SHUFFLE_ZWXY), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); + case GT_GT: + case GT_LT: + { + needsConvertMaskToVector = (simdSize == 64) || (varTypeIsUnsigned(simdBaseType) && canUseEvexEncoding()); + break; + } - return gtNewSimdBinOpNode(GT_AND, type, tmp, op2, simdBaseJitType, simdSize); - } - } - else - { - assert(simdSize == 16); - intrinsic = NI_SSE2_CompareEqual; - } + default: + { break; } + } +#endif // TARGET_XARCH - case GT_GE: + NamedIntrinsic intrinsic = + GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(this, op, op1, op2, simdBaseType, simdSize, false); + + if (intrinsic != NI_Illegal) + { + if (needsConvertMaskToVector) { - if (canUseEvexEncoding()) - { - if ((simdSize == 64) || !varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_EVEX_CompareGreaterThanOrEqualMask; - needsConvertMaskToVector = true; - break; - } - } + GenTree* retNode = gtNewSimdHWIntrinsicNode(TYP_MASK, op1, op2, intrinsic, simdBaseJitType, simdSize); + return gtNewSimdCvtMaskToVectorNode(type, retNode, simdBaseJitType, simdSize); + } + return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); + } - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + assert(!needsConvertMaskToVector); - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_CompareGreaterThanOrEqual; - } - } - else if (simdBaseType == TYP_FLOAT) - { - assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16)); - intrinsic = NI_SSE_CompareGreaterThanOrEqual; - } - else if (simdBaseType == TYP_DOUBLE) - { - assert(simdSize == 16); - intrinsic = NI_SSE2_CompareGreaterThanOrEqual; - } + switch (op) + { +#if defined(TARGET_XARCH) + case GT_EQ: + { + assert(varTypeIsLong(simdBaseType)); + assert(simdSize == 16); - if (intrinsic == NI_Illegal) + // There is no direct SSE2 support for comparing TYP_LONG vectors. + // These have to be implemented in terms of TYP_INT vector comparison operations. + // + // tmp = (op1 == op2) i.e. compare for equality as if op1 and op2 are vector of int + // op1 = tmp + // op2 = Shuffle(op1, (2, 3, 0, 1)) + // result = BitwiseAnd(tmp, op2) + // + // Shuffle is meant to swap the comparison results of low-32-bits and high 32-bits of + // respective long elements. + + GenTree* tmp = gtNewSimdCmpOpNode(op, type, op1, op2, CORINFO_TYPE_INT, simdSize); + + op1 = fgMakeMultiUse(&tmp); + op2 = gtNewSimdHWIntrinsicNode(type, op1, gtNewIconNode(SHUFFLE_ZWXY), NI_SSE2_Shuffle, CORINFO_TYPE_INT, + simdSize); + + return gtNewSimdBinOpNode(GT_AND, type, tmp, op2, simdBaseJitType, simdSize); + } + + case GT_GE: + case GT_LE: + { + assert(varTypeIsIntegral(simdBaseType)); + assert(!canUseEvexEncodingDebugOnly()); + + // If we don't have an intrinsic set for this, try "Max(op1, op2) == op1" for GE + // and "Min(op1, op2) == op1" for LE + // + // NOTE: technically, we can special case byte type to only require SSE2, but it + // complicates the test matrix for little gains. + + if (((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2)) || + ((simdSize == 16) && compOpportunisticallyDependsOn(InstructionSet_SSE41))) { - // If we don't have an intrinsic set for this, try "Max(op1, op2) == op1" - // NOTE: technically, we can special case byte type to only require SSE2, but it - // complicates the test matrix for little gains. - if (((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2)) || - ((simdSize == 16) && compOpportunisticallyDependsOn(InstructionSet_SSE41))) + // TODO-AVX512: We can use this trick for longs only with AVX-512 + if (!varTypeIsLong(simdBaseType)) { - // TODO-AVX512: We can use this trick for longs only with AVX-512 - if (!varTypeIsLong(simdBaseType)) - { - assert(!varTypeIsFloating(simdBaseType)); - GenTree* op1Dup = fgMakeMultiUse(&op1); + assert(!varTypeIsFloating(simdBaseType)); + GenTree* op1Dup = fgMakeMultiUse(&op1); + if (op == GT_GE) + { // EQ(Max(op1, op2), op1) - GenTree* maxNode = gtNewSimdMaxNode(type, op1, op2, simdBaseJitType, simdSize); - return gtNewSimdCmpOpNode(GT_EQ, type, maxNode, op1Dup, simdBaseJitType, simdSize); + op1 = gtNewSimdMaxNode(type, op1, op2, simdBaseJitType, simdSize); } + else + { + // EQ(Min(op1, op2), op1) + op1 = gtNewSimdMinNode(type, op1, op2, simdBaseJitType, simdSize); + } + return gtNewSimdCmpOpNode(GT_EQ, type, op1, op1Dup, simdBaseJitType, simdSize); } + } - // There is no direct support for doing a combined comparison and equality for integral types. - // These have to be implemented by performing both halves and combining their results. - // - // op1Dup = op1 - // op2Dup = op2 - // - // op1 = GreaterThan(op1, op2) - // op2 = Equals(op1Dup, op2Dup) - // - // result = BitwiseOr(op1, op2) - - GenTree* op1Dup = fgMakeMultiUse(&op1); - GenTree* op2Dup = fgMakeMultiUse(&op2); + // There is no direct support for doing a combined comparison and equality for integral types. + // These have to be implemented by performing both halves and combining their results. + // + // op1Dup = op1 + // op2Dup = op2 + // + // For greater than: + // op1 = GreaterThan(op1, op2) + // op2 = Equals(op1Dup, op2Dup) + // + // For less than: + // op1 = LessThan(op1, op2) + // op2 = Equals(op1Dup, op2Dup) + // + // result = BitwiseOr(op1, op2) - op1 = gtNewSimdCmpOpNode(GT_GT, type, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup, op2Dup, simdBaseJitType, simdSize); + GenTree* op1Dup = fgMakeMultiUse(&op1); + GenTree* op2Dup = fgMakeMultiUse(&op2); - return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); + if (op == GT_GE) + { + op = GT_GT; } - break; + else + { + op = GT_LT; + } + + op1 = gtNewSimdCmpOpNode(op, type, op1, op2, simdBaseJitType, simdSize); + op2 = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup, op2Dup, simdBaseJitType, simdSize); + + return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); } case GT_GT: + case GT_LT: { - if (canUseEvexEncoding()) - { - if ((simdSize == 64) || varTypeIsUnsigned(simdBaseType)) - { - intrinsic = NI_EVEX_CompareGreaterThanMask; - needsConvertMaskToVector = true; - break; - } - } - if (varTypeIsUnsigned(simdBaseType)) { // Vector of byte, ushort, uint and ulong: - // Hardware supports > for signed comparison. Therefore, to use it for + // Hardware supports > and < for signed comparison. Therefore, to use it for // comparing unsigned numbers, we subtract a constant from both the // operands such that the result fits within the corresponding signed // type. The resulting signed numbers are compared using signed comparison. @@ -22685,420 +21974,204 @@ GenTree* Compiler::gtNewSimdCmpOpNode( // op2 = op2 - constVector op2 = gtNewSimdBinOpNode(GT_SUB, type, op2, vecCon2, opJitType, simdSize); + + return gtNewSimdCmpOpNode(op, type, op1, op2, simdBaseJitType, simdSize); } + else + { + assert(varTypeIsLong(simdBaseType)); + assert(simdSize == 16); - // This should have been mutated by the above path - assert(!varTypeIsUnsigned(simdBaseType)); + // There is no direct SSE2 support for comparing TYP_LONG vectors. + // These have to be implemented in terms of TYP_INT vector comparison operations. + // + // Let us consider the case of single long element comparison. + // Say op1 = (x1, y1) and op2 = (x2, y2) where x1, y1, x2, and y2 are 32-bit integers that comprise + // the + // longs op1 and op2. + // + // GreaterThan(op1, op2) can be expressed in terms of > relationship between 32-bit integers that + // comprise op1 and op2 as + // = (x1, y1) > (x2, y2) + // = (x1 > x2) || [(x1 == x2) && (y1 > y2)] - eq (1) + // + // LessThan(op1, op2) can be expressed in terms of < relationship between 32-bit integers that + // comprise op1 and op2 as + // = (x1, y1) < (x2, y2) + // = (x1 < x2) || [(x1 == x2) && (y1 < y2)] - eq (1) + // + // op1Dup1 = op1 + // op1Dup2 = op1Dup1 + // op2Dup1 = op2 + // op2Dup2 = op2Dup1 + // + // t = (op1 cmp op2) - 32-bit signed comparison + // u = (op1Dup1 == op2Dup1) - 32-bit equality comparison + // v = (op1Dup2 cmp op2Dup2) - 32-bit unsigned comparison + // + // op1 = Shuffle(t, (3, 3, 1, 1)) - This corresponds to (x1 > x2) in eq(1) above + // u = Shuffle(u, (3, 3, 1, 1)) - This corresponds to (x1 == x2) in eq(1) above + // v = Shuffle(v, (2, 2, 0, 0)) - This corresponds to (y1 > y2) in eq(1) above + // op2 = BitwiseAnd(u, v) - This corresponds to [(x1 == x2) && (y1 > y2)] in eq(1) above + // + // result = BitwiseOr(op1, op2) - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + GenTree* op1Dup1 = fgMakeMultiUse(&op1); + GenTree* op1Dup2 = fgMakeMultiUse(&op1Dup1); - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_CompareGreaterThan; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_CompareGreaterThan; - } - } - else if (simdBaseType == TYP_FLOAT) - { - assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16)); - intrinsic = NI_SSE_CompareGreaterThan; + GenTree* op2Dup1 = fgMakeMultiUse(&op2); + GenTree* op2Dup2 = fgMakeMultiUse(&op2Dup1); + + GenTree* t = gtNewSimdCmpOpNode(op, type, op1, op2, CORINFO_TYPE_INT, simdSize); + GenTree* u = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup1, op2Dup1, CORINFO_TYPE_INT, simdSize); + GenTree* v = gtNewSimdCmpOpNode(op, type, op1Dup2, op2Dup2, CORINFO_TYPE_UINT, simdSize); + + op1 = gtNewSimdHWIntrinsicNode(type, t, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, + CORINFO_TYPE_INT, simdSize); + u = gtNewSimdHWIntrinsicNode(type, u, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, + CORINFO_TYPE_INT, simdSize); + v = gtNewSimdHWIntrinsicNode(type, v, gtNewIconNode(SHUFFLE_ZZXX, TYP_INT), NI_SSE2_Shuffle, + CORINFO_TYPE_INT, simdSize); + + // Validate we can't use AVX512F_VL_TernaryLogic here + assert(!canUseEvexEncodingDebugOnly()); + + op2 = gtNewSimdBinOpNode(GT_AND, type, u, v, simdBaseJitType, simdSize); + return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); } - else if (varTypeIsLong(simdBaseType)) - { - assert(simdSize == 16); + break; + } +#endif // TARGET_XARCH - if (compOpportunisticallyDependsOn(InstructionSet_SSE42)) - { - intrinsic = NI_SSE42_CompareGreaterThan; - } - else - { - // There is no direct SSE2 support for comparing TYP_LONG vectors. - // These have to be implemented in terms of TYP_INT vector comparison operations. - // - // Let us consider the case of single long element comparison. - // Say op1 = (x1, y1) and op2 = (x2, y2) where x1, y1, x2, and y2 are 32-bit integers that comprise - // the - // longs op1 and op2. - // - // GreaterThan(op1, op2) can be expressed in terms of > relationship between 32-bit integers that - // comprise op1 and op2 as - // = (x1, y1) > (x2, y2) - // = (x1 > x2) || [(x1 == x2) && (y1 > y2)] - eq (1) - // - // op1Dup1 = op1 - // op1Dup2 = op1Dup1 - // op2Dup1 = op2 - // op2Dup2 = op2Dup1 - // - // t = (op1 > op2) - 32-bit signed comparison - // u = (op1Dup1 == op2Dup1) - 32-bit equality comparison - // v = (op1Dup2 > op2Dup2) - 32-bit unsigned comparison - // - // op1 = Shuffle(t, (3, 3, 1, 1)) - This corresponds to (x1 > x2) in eq(1) above - // u = Shuffle(u, (3, 3, 1, 1)) - This corresponds to (x1 == x2) in eq(1) above - // v = Shuffle(v, (2, 2, 0, 0)) - This corresponds to (y1 > y2) in eq(1) above - // op2 = BitwiseAnd(u, v) - This corresponds to [(x1 == x2) && (y1 > y2)] in eq(1) above - // - // result = BitwiseOr(op1, op2) + default: + { + unreached(); + } + } +} + +GenTree* Compiler::gtNewSimdCmpOpAllNode( + genTreeOps op, var_types type, GenTree* op1, GenTree* op2, CorInfoType simdBaseJitType, unsigned simdSize) +{ + assert(IsBaselineSimdIsaSupportedDebugOnly()); + assert(type == TYP_INT); + + var_types simdType = getSIMDTypeForSize(simdSize); + assert(varTypeIsSIMD(simdType)); - GenTree* op1Dup1 = fgMakeMultiUse(&op1); - GenTree* op1Dup2 = fgMakeMultiUse(&op1Dup1); + assert(op1 != nullptr); + assert(op1->TypeIs(simdType)); - GenTree* op2Dup1 = fgMakeMultiUse(&op2); - GenTree* op2Dup2 = fgMakeMultiUse(&op2Dup1); + assert(op2 != nullptr); + assert(op2->TypeIs(simdType)); - GenTree* t = gtNewSimdCmpOpNode(op, type, op1, op2, CORINFO_TYPE_INT, simdSize); - GenTree* u = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup1, op2Dup1, CORINFO_TYPE_INT, simdSize); - GenTree* v = gtNewSimdCmpOpNode(op, type, op1Dup2, op2Dup2, CORINFO_TYPE_UINT, simdSize); + var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); + assert(varTypeIsArithmetic(simdBaseType)); - op1 = gtNewSimdHWIntrinsicNode(type, t, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); - u = gtNewSimdHWIntrinsicNode(type, u, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); - v = gtNewSimdHWIntrinsicNode(type, v, gtNewIconNode(SHUFFLE_ZZXX, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); + NamedIntrinsic intrinsic = NI_Illegal; - // Validate we can't use AVX512F_VL_TernaryLogic here - assert(!canUseEvexEncodingDebugOnly()); + switch (op) + { +#if defined(TARGET_XARCH) + case GT_EQ: + { + if (simdSize == 32) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); - op2 = gtNewSimdBinOpNode(GT_AND, type, u, v, simdBaseJitType, simdSize); - return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); - } + intrinsic = NI_Vector256_op_Equality; + } + else if (simdSize == 64) + { + assert(IsBaselineVector512IsaSupportedDebugOnly()); + intrinsic = NI_Vector512_op_Equality; } else { - assert(simdSize == 16); - intrinsic = NI_SSE2_CompareGreaterThan; + intrinsic = NI_Vector128_op_Equality; } break; } + case GT_GE: + case GT_GT: case GT_LE: + case GT_LT: { - if (canUseEvexEncoding()) - { - if ((simdSize == 64) || !varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_EVEX_CompareLessThanOrEqualMask; - needsConvertMaskToVector = true; - break; - } - } + // We want to generate a comparison along the lines of + // GT_XX(op1, op2).As() == Vector128.AllBitsSet if (simdSize == 32) { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_CompareLessThanOrEqual; - } + // TODO-XArch-CQ: It's a non-trivial amount of work to support these + // for floating-point while only utilizing AVX. It would require, among + // other things, inverting the comparison and potentially support for a + // new Avx.TestNotZ intrinsic to ensure the codegen remains efficient. + assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); + intrinsic = NI_Vector256_op_Equality; } - else if (simdBaseType == TYP_FLOAT) + else if (simdSize == 64) { - assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16)); - intrinsic = NI_SSE_CompareLessThanOrEqual; + assert(IsBaselineVector512IsaSupportedDebugOnly()); + intrinsic = NI_Vector512_op_Equality; } - else if (simdBaseType == TYP_DOUBLE) + else { - assert(simdSize == 16); - intrinsic = NI_SSE2_CompareLessThanOrEqual; + intrinsic = NI_Vector128_op_Equality; } - if (intrinsic == NI_Illegal) - { - // If we don't have an intrinsic set for this, try "Min(op1, op2) == op1" - // NOTE: technically, we can special case byte type to only require SSE2, but it - // complicates the test matrix for little gains. - if (((simdSize == 32) && compOpportunisticallyDependsOn(InstructionSet_AVX2)) || - ((simdSize == 16) && compOpportunisticallyDependsOn(InstructionSet_SSE41))) - { - // TODO-AVX512: We can use this trick for longs only with AVX-512 - if (!varTypeIsLong(simdBaseType)) - { - assert(!varTypeIsFloating(simdBaseType)); - GenTree* op1Dup = fgMakeMultiUse(&op1); - - // EQ(Min(op1, op2), op1) - GenTree* minNode = gtNewSimdMinNode(type, op1, op2, simdBaseJitType, simdSize); - return gtNewSimdCmpOpNode(GT_EQ, type, minNode, op1Dup, simdBaseJitType, simdSize); - } - } - - // There is no direct support for doing a combined comparison and equality for integral types. - // These have to be implemented by performing both halves and combining their results. - // - // op1Dup = op1 - // op2Dup = op2 - // - // op1 = LessThan(op1, op2) - // op2 = Equals(op1Dup, op2Dup) - // - // result = BitwiseOr(op1, op2) - - GenTree* op1Dup = fgMakeMultiUse(&op1); - GenTree* op2Dup = fgMakeMultiUse(&op2); - - op1 = gtNewSimdCmpOpNode(GT_LT, type, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup, op2Dup, simdBaseJitType, simdSize); + op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); + op2 = gtNewAllBitsSetConNode(simdType); - return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); + if (simdBaseType == TYP_FLOAT) + { + simdBaseType = TYP_INT; + simdBaseJitType = CORINFO_TYPE_INT; + } + else if (simdBaseType == TYP_DOUBLE) + { + simdBaseType = TYP_LONG; + simdBaseJitType = CORINFO_TYPE_LONG; } break; } +#elif defined(TARGET_ARM64) + case GT_EQ: + { + intrinsic = (simdSize == 8) ? NI_Vector64_op_Equality : NI_Vector128_op_Equality; + break; + } + case GT_GE: + case GT_GT: + case GT_LE: case GT_LT: { - if (canUseEvexEncoding()) + // We want to generate a comparison along the lines of + // GT_XX(op1, op2).As() == Vector128.AllBitsSet + + if (simdSize == 8) { - if ((simdSize == 64) || varTypeIsUnsigned(simdBaseType)) - { - intrinsic = NI_EVEX_CompareLessThanMask; - needsConvertMaskToVector = true; - break; - } + intrinsic = NI_Vector64_op_Equality; } - - if (varTypeIsUnsigned(simdBaseType)) + else { - // Vector of byte, ushort, uint and ulong: - // Hardware supports < for signed comparison. Therefore, to use it for - // comparing unsigned numbers, we subtract a constant from both the - // operands such that the result fits within the corresponding signed - // type. The resulting signed numbers are compared using signed comparison. - // - // Vector of byte: constant to be subtracted is 2^7 - // Vector of ushort: constant to be subtracted is 2^15 - // Vector of uint: constant to be subtracted is 2^31 - // Vector of ulong: constant to be subtracted is 2^63 - // - // We need to treat op1 and op2 as signed for comparison purpose after - // the transformation. + intrinsic = NI_Vector128_op_Equality; + } - uint64_t constVal = 0; - CorInfoType opJitType = simdBaseJitType; - var_types opType = simdBaseType; + op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); + op2 = gtNewAllBitsSetConNode(simdType); - switch (simdBaseType) - { - case TYP_UBYTE: - { - constVal = 0x8080808080808080; - simdBaseJitType = CORINFO_TYPE_BYTE; - simdBaseType = TYP_BYTE; - break; - } - - case TYP_USHORT: - { - constVal = 0x8000800080008000; - simdBaseJitType = CORINFO_TYPE_SHORT; - simdBaseType = TYP_SHORT; - break; - } - - case TYP_UINT: - { - constVal = 0x8000000080000000; - simdBaseJitType = CORINFO_TYPE_INT; - simdBaseType = TYP_INT; - break; - } - - case TYP_ULONG: - { - constVal = 0x8000000000000000; - simdBaseJitType = CORINFO_TYPE_LONG; - simdBaseType = TYP_LONG; - break; - } - - default: - { - unreached(); - } - } - - GenTreeVecCon* vecCon1 = gtNewVconNode(type); - - for (unsigned i = 0; i < (simdSize / 8); i++) - { - vecCon1->gtSimdVal.u64[i] = constVal; - } - - GenTree* vecCon2 = gtCloneExpr(vecCon1); - - // op1 = op1 - constVector - op1 = gtNewSimdBinOpNode(GT_SUB, type, op1, vecCon1, opJitType, simdSize); - - // op2 = op2 - constVector - op2 = gtNewSimdBinOpNode(GT_SUB, type, op2, vecCon2, opJitType, simdSize); - } - - // This should have been mutated by the above path - assert(!varTypeIsUnsigned(simdBaseType)); - - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - - if (varTypeIsFloating(simdBaseType)) - { - intrinsic = NI_AVX_CompareLessThan; - } - else - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_AVX2_CompareLessThan; - } - } - else if (simdBaseType == TYP_FLOAT) - { - assert((simdSize == 8) || (simdSize == 12) || (simdSize == 16)); - intrinsic = NI_SSE_CompareLessThan; - } - else if (varTypeIsLong(simdBaseType)) - { - assert(simdSize == 16); - - if (compOpportunisticallyDependsOn(InstructionSet_SSE42)) - { - intrinsic = NI_SSE42_CompareLessThan; - } - else - { - // There is no direct SSE2 support for comparing TYP_LONG vectors. - // These have to be implemented in terms of TYP_INT vector comparison operations. - // - // Let us consider the case of single long element comparison. - // Say op1 = (x1, y1) and op2 = (x2, y2) where x1, y1, x2, and y2 are 32-bit integers that comprise - // the - // longs op1 and op2. - // - // LessThan(op1, op2) can be expressed in terms of > relationship between 32-bit integers that - // comprise op1 and op2 as - // = (x1, y1) > (x2, y2) - // = (x1 > x2) || [(x1 == x2) && (y1 > y2)] - eq (1) - // - // op1Dup1 = op1 - // op1Dup2 = op1Dup1 - // op2Dup1 = op2 - // op2Dup2 = op2Dup1 - // - // t = (op1 > op2) - 32-bit signed comparison - // u = (op1Dup1 == op2Dup1) - 32-bit equality comparison - // v = (op1Dup2 > op2Dup2) - 32-bit unsigned comparison - // - // op1 = Shuffle(t, (3, 3, 1, 1)) - This corresponds to (x1 > x2) in eq(1) above - // u = Shuffle(u, (3, 3, 1, 1)) - This corresponds to (x1 == x2) in eq(1) above - // v = Shuffle(v, (2, 2, 0, 0)) - This corresponds to (y1 > y2) in eq(1) above - // op2 = BitwiseAnd(u, v) - This corresponds to [(x1 == x2) && (y1 > y2)] in eq(1) above - // - // result = BitwiseOr(op1, op2) - - GenTree* op1Dup1 = fgMakeMultiUse(&op1); - GenTree* op1Dup2 = fgMakeMultiUse(&op1Dup1); - - GenTree* op2Dup1 = fgMakeMultiUse(&op2); - GenTree* op2Dup2 = fgMakeMultiUse(&op2Dup1); - - GenTree* t = gtNewSimdCmpOpNode(op, type, op1, op2, CORINFO_TYPE_INT, simdSize); - GenTree* u = gtNewSimdCmpOpNode(GT_EQ, type, op1Dup1, op2Dup1, CORINFO_TYPE_INT, simdSize); - GenTree* v = gtNewSimdCmpOpNode(op, type, op1Dup2, op2Dup2, CORINFO_TYPE_UINT, simdSize); - - op1 = gtNewSimdHWIntrinsicNode(type, t, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); - u = gtNewSimdHWIntrinsicNode(type, u, gtNewIconNode(SHUFFLE_WWYY, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); - v = gtNewSimdHWIntrinsicNode(type, v, gtNewIconNode(SHUFFLE_ZZXX, TYP_INT), NI_SSE2_Shuffle, - CORINFO_TYPE_INT, simdSize); - - // Validate we can't use AVX512F_VL_TernaryLogic here - assert(!canUseEvexEncodingDebugOnly()); - - op2 = gtNewSimdBinOpNode(GT_AND, type, u, v, simdBaseJitType, simdSize); - return gtNewSimdBinOpNode(GT_OR, type, op1, op2, simdBaseJitType, simdSize); - } - } - else - { - assert(simdSize == 16); - intrinsic = NI_SSE2_CompareLessThan; - } - break; - } -#elif defined(TARGET_ARM64) - case GT_EQ: - { - if ((varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareEqualScalar : NI_AdvSimd_Arm64_CompareEqual; - } - else - { - intrinsic = NI_AdvSimd_CompareEqual; - } - break; - } - - case GT_GE: - { - if ((varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareGreaterThanOrEqualScalar - : NI_AdvSimd_Arm64_CompareGreaterThanOrEqual; - } - else - { - intrinsic = NI_AdvSimd_CompareGreaterThanOrEqual; - } - break; - } - - case GT_GT: - { - if ((varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))) - { - intrinsic = - (simdSize == 8) ? NI_AdvSimd_Arm64_CompareGreaterThanScalar : NI_AdvSimd_Arm64_CompareGreaterThan; - } - else - { - intrinsic = NI_AdvSimd_CompareGreaterThan; - } - break; - } - - case GT_LE: - { - if ((varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareLessThanOrEqualScalar - : NI_AdvSimd_Arm64_CompareLessThanOrEqual; - } - else - { - intrinsic = NI_AdvSimd_CompareLessThanOrEqual; - } - break; - } - - case GT_LT: - { - if ((varTypeIsLong(simdBaseType) || (simdBaseType == TYP_DOUBLE))) + if (simdBaseType == TYP_FLOAT) { - intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareLessThanScalar : NI_AdvSimd_Arm64_CompareLessThan; + simdBaseType = TYP_INT; + simdBaseJitType = CORINFO_TYPE_INT; } - else + else if (simdBaseType == TYP_DOUBLE) { - intrinsic = NI_AdvSimd_CompareLessThan; + simdBaseType = TYP_LONG; + simdBaseJitType = CORINFO_TYPE_LONG; } break; } @@ -23113,19 +22186,10 @@ GenTree* Compiler::gtNewSimdCmpOpNode( } assert(intrinsic != NI_Illegal); - - if (needsConvertMaskToVector) - { - GenTree* retNode = gtNewSimdHWIntrinsicNode(TYP_MASK, op1, op2, intrinsic, simdBaseJitType, simdSize); - return gtNewSimdCvtMaskToVectorNode(type, retNode, simdBaseJitType, simdSize); - } - else - { - return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); - } + return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); } -GenTree* Compiler::gtNewSimdCmpOpAllNode( +GenTree* Compiler::gtNewSimdCmpOpAnyNode( genTreeOps op, var_types type, GenTree* op1, GenTree* op2, CorInfoType simdBaseJitType, unsigned simdSize) { assert(IsBaselineSimdIsaSupportedDebugOnly()); @@ -23149,33 +22213,13 @@ GenTree* Compiler::gtNewSimdCmpOpAllNode( { #if defined(TARGET_XARCH) case GT_EQ: - { - if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); - - intrinsic = NI_Vector256_op_Equality; - } - else if (simdSize == 64) - { - assert(IsBaselineVector512IsaSupportedDebugOnly()); - intrinsic = NI_Vector512_op_Equality; - } - else - { - intrinsic = NI_Vector128_op_Equality; - } - break; - } - case GT_GE: case GT_GT: case GT_LE: case GT_LT: { // We want to generate a comparison along the lines of - // GT_XX(op1, op2).As() == Vector128.AllBitsSet + // GT_XX(op1, op2).As() != Vector128.Zero if (simdSize == 32) { @@ -23184,20 +22228,21 @@ GenTree* Compiler::gtNewSimdCmpOpAllNode( // other things, inverting the comparison and potentially support for a // new Avx.TestNotZ intrinsic to ensure the codegen remains efficient. assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - intrinsic = NI_Vector256_op_Equality; + + intrinsic = NI_Vector256_op_Inequality; } else if (simdSize == 64) { assert(IsBaselineVector512IsaSupportedDebugOnly()); - intrinsic = NI_Vector512_op_Equality; + intrinsic = NI_Vector512_op_Inequality; } else { - intrinsic = NI_Vector128_op_Equality; + intrinsic = NI_Vector128_op_Inequality; } op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewAllBitsSetConNode(simdType); + op2 = gtNewZeroConNode(simdType); if (simdBaseType == TYP_FLOAT) { @@ -23211,32 +22256,41 @@ GenTree* Compiler::gtNewSimdCmpOpAllNode( } break; } -#elif defined(TARGET_ARM64) - case GT_EQ: + + case GT_NE: { - intrinsic = (simdSize == 8) ? NI_Vector64_op_Equality : NI_Vector128_op_Equality; + if (simdSize == 64) + { + assert(IsBaselineVector512IsaSupportedDebugOnly()); + intrinsic = NI_Vector512_op_Inequality; + } + else if (simdSize == 32) + { + assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); + assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); + + intrinsic = NI_Vector256_op_Inequality; + } + else + { + intrinsic = NI_Vector128_op_Inequality; + } break; } - +#elif defined(TARGET_ARM64) + case GT_EQ: case GT_GE: case GT_GT: case GT_LE: case GT_LT: { // We want to generate a comparison along the lines of - // GT_XX(op1, op2).As() == Vector128.AllBitsSet + // GT_XX(op1, op2).As() != Vector128.Zero - if (simdSize == 8) - { - intrinsic = NI_Vector64_op_Equality; - } - else - { - intrinsic = NI_Vector128_op_Equality; - } + intrinsic = (simdSize == 8) ? NI_Vector64_op_Inequality : NI_Vector128_op_Inequality; op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewAllBitsSetConNode(simdType); + op2 = gtNewZeroConNode(simdType); if (simdBaseType == TYP_FLOAT) { @@ -23250,6 +22304,12 @@ GenTree* Compiler::gtNewSimdCmpOpAllNode( } break; } + + case GT_NE: + { + intrinsic = (simdSize == 8) ? NI_Vector64_op_Inequality : NI_Vector128_op_Inequality; + break; + } #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 @@ -23264,157 +22324,22 @@ GenTree* Compiler::gtNewSimdCmpOpAllNode( return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); } -GenTree* Compiler::gtNewSimdCmpOpAnyNode( - genTreeOps op, var_types type, GenTree* op1, GenTree* op2, CorInfoType simdBaseJitType, unsigned simdSize) +GenTree* Compiler::gtNewSimdCndSelNode( + var_types type, GenTree* op1, GenTree* op2, GenTree* op3, CorInfoType simdBaseJitType, unsigned simdSize) { assert(IsBaselineSimdIsaSupportedDebugOnly()); - assert(type == TYP_INT); - var_types simdType = getSIMDTypeForSize(simdSize); - assert(varTypeIsSIMD(simdType)); + assert(varTypeIsSIMD(type)); + assert(getSIMDTypeForSize(simdSize) == type); assert(op1 != nullptr); - assert(op1->TypeIs(simdType)); + assert(op1->TypeIs(type)); assert(op2 != nullptr); - assert(op2->TypeIs(simdType)); - - var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); - assert(varTypeIsArithmetic(simdBaseType)); - - NamedIntrinsic intrinsic = NI_Illegal; - - switch (op) - { -#if defined(TARGET_XARCH) - case GT_EQ: - case GT_GE: - case GT_GT: - case GT_LE: - case GT_LT: - { - // We want to generate a comparison along the lines of - // GT_XX(op1, op2).As() != Vector128.Zero - - if (simdSize == 32) - { - // TODO-XArch-CQ: It's a non-trivial amount of work to support these - // for floating-point while only utilizing AVX. It would require, among - // other things, inverting the comparison and potentially support for a - // new Avx.TestNotZ intrinsic to ensure the codegen remains efficient. - assert(compIsaSupportedDebugOnly(InstructionSet_AVX2)); - - intrinsic = NI_Vector256_op_Inequality; - } - else if (simdSize == 64) - { - assert(IsBaselineVector512IsaSupportedDebugOnly()); - intrinsic = NI_Vector512_op_Inequality; - } - else - { - intrinsic = NI_Vector128_op_Inequality; - } - - op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewZeroConNode(simdType); - - if (simdBaseType == TYP_FLOAT) - { - simdBaseType = TYP_INT; - simdBaseJitType = CORINFO_TYPE_INT; - } - else if (simdBaseType == TYP_DOUBLE) - { - simdBaseType = TYP_LONG; - simdBaseJitType = CORINFO_TYPE_LONG; - } - break; - } - - case GT_NE: - { - if (simdSize == 64) - { - assert(IsBaselineVector512IsaSupportedDebugOnly()); - intrinsic = NI_Vector512_op_Inequality; - } - else if (simdSize == 32) - { - assert(compIsaSupportedDebugOnly(InstructionSet_AVX)); - assert(varTypeIsFloating(simdBaseType) || compIsaSupportedDebugOnly(InstructionSet_AVX2)); - - intrinsic = NI_Vector256_op_Inequality; - } - else - { - intrinsic = NI_Vector128_op_Inequality; - } - break; - } -#elif defined(TARGET_ARM64) - case GT_EQ: - case GT_GE: - case GT_GT: - case GT_LE: - case GT_LT: - { - // We want to generate a comparison along the lines of - // GT_XX(op1, op2).As() != Vector128.Zero - - intrinsic = (simdSize == 8) ? NI_Vector64_op_Inequality : NI_Vector128_op_Inequality; - - op1 = gtNewSimdCmpOpNode(op, simdType, op1, op2, simdBaseJitType, simdSize); - op2 = gtNewZeroConNode(simdType); - - if (simdBaseType == TYP_FLOAT) - { - simdBaseType = TYP_INT; - simdBaseJitType = CORINFO_TYPE_INT; - } - else if (simdBaseType == TYP_DOUBLE) - { - simdBaseType = TYP_LONG; - simdBaseJitType = CORINFO_TYPE_LONG; - } - break; - } - - case GT_NE: - { - intrinsic = (simdSize == 8) ? NI_Vector64_op_Inequality : NI_Vector128_op_Inequality; - break; - } -#else -#error Unsupported platform -#endif // !TARGET_XARCH && !TARGET_ARM64 - - default: - { - unreached(); - } - } - - assert(intrinsic != NI_Illegal); - return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); -} - -GenTree* Compiler::gtNewSimdCndSelNode( - var_types type, GenTree* op1, GenTree* op2, GenTree* op3, CorInfoType simdBaseJitType, unsigned simdSize) -{ - assert(IsBaselineSimdIsaSupportedDebugOnly()); - - assert(varTypeIsSIMD(type)); - assert(getSIMDTypeForSize(simdSize) == type); - - assert(op1 != nullptr); - assert(op1->TypeIs(type)); - - assert(op2 != nullptr); - assert(op2->TypeIs(type)); - - assert(op3 != nullptr); - assert(op3->TypeIs(type)); + assert(op2->TypeIs(type)); + + assert(op3 != nullptr); + assert(op3->TypeIs(type)); var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); assert(varTypeIsArithmetic(simdBaseType)); @@ -26505,9 +25430,54 @@ GenTree* Compiler::gtNewSimdUnOpNode( var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); assert(varTypeIsArithmetic(simdBaseType)); - NamedIntrinsic intrinsic = NI_Illegal; - GenTree* op2 = nullptr; - GenTree* op3 = nullptr; +#if defined(TARGET_ARM64) + if (op == GT_NEG) + { + switch (simdBaseType) + { + case TYP_UBYTE: + { + simdBaseJitType = CORINFO_TYPE_BYTE; + simdBaseType = TYP_BYTE; + break; + } + + case TYP_USHORT: + { + simdBaseJitType = CORINFO_TYPE_SHORT; + simdBaseType = TYP_SHORT; + break; + } + + case TYP_UINT: + { + simdBaseJitType = CORINFO_TYPE_INT; + simdBaseType = TYP_INT; + break; + } + + case TYP_ULONG: + { + simdBaseJitType = CORINFO_TYPE_LONG; + simdBaseType = TYP_LONG; + break; + } + + default: + { + break; + } + } + } +#endif // TARGET_ARM64 + + NamedIntrinsic intrinsic = + GenTreeHWIntrinsic::GetHWIntrinsicIdForUnOp(this, op, op1, simdBaseType, simdSize, false); + + if (intrinsic != NI_Illegal) + { + return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize); + } switch (op) { @@ -26535,41 +25505,7 @@ GenTree* Compiler::gtNewSimdUnOpNode( GenTree* allBitsSet = gtNewAllBitsSetConNode(type); return gtNewSimdBinOpNode(GT_XOR, type, op1, allBitsSet, simdBaseJitType, simdSize); } -#elif defined(TARGET_ARM64) - case GT_NEG: - { - if (varTypeIsSigned(simdBaseType)) - { - if (simdBaseType == TYP_LONG) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_Arm64_NegateScalar : NI_AdvSimd_Arm64_Negate; - } - else if (simdBaseType == TYP_DOUBLE) - { - intrinsic = (simdSize == 8) ? NI_AdvSimd_NegateScalar : NI_AdvSimd_Arm64_Negate; - } - else - { - intrinsic = NI_AdvSimd_Negate; - } - - return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize); - } - else - { - // Zero - op1 - op2 = gtNewZeroConNode(type); - return gtNewSimdBinOpNode(GT_SUB, type, op2, op1, simdBaseJitType, simdSize); - } - } - - case GT_NOT: - { - return gtNewSimdHWIntrinsicNode(type, op1, NI_AdvSimd_Not, simdBaseJitType, simdSize); - } -#else -#error Unsupported platform -#endif // !TARGET_XARCH && !TARGET_ARM64 +#endif // TARGET_XARCH default: { @@ -27707,7 +26643,7 @@ bool GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(genTreeOps oper) bool GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic() const { bool isScalar = false; - genTreeOps oper = HWOperGet(&isScalar); + genTreeOps oper = GetOperForHWIntrinsicId(&isScalar); return OperIsBitwiseHWIntrinsic(oper); } @@ -28072,13 +27008,21 @@ void GenTreeHWIntrinsic::Initialize(NamedIntrinsic intrinsicId) } //------------------------------------------------------------------------------ -// HWOperGet : Returns Oper based on the HWIntrinsicId +// GetOperForHWIntrinsicId: Returns oper based on the intrinsic ID and base type +// +// Arguments: +// id - The intrinsic ID for which to get the oper +// simdBaseType - The base type on which id is executed +// isScalar - On return, contains true if the oper is over scalar data; otherwise false +// +// Returns: +// The oper based on the intrinsic ID and base type // -genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const +genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_types simdBaseType, bool* isScalar) { *isScalar = false; - switch (GetHWIntrinsicId()) + switch (id) { #if defined(TARGET_XARCH) case NI_SSE_And: @@ -28176,7 +27120,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_AddScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28210,7 +27154,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_DivideScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28242,7 +27186,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const case NI_SSE2_Multiply: case NI_AVX512F_Multiply: { - if (varTypeIsFloating(GetSimdBaseType())) + if (varTypeIsFloating(simdBaseType)) { return GT_MUL; } @@ -28264,7 +27208,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_MultiplyScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28282,7 +27226,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const case NI_AdvSimd_NegateScalar: case NI_AdvSimd_Arm64_NegateScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28322,6 +27266,8 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const case NI_AVX512F_ShiftLeftLogicalVariable: case NI_AVX512BW_ShiftLeftLogical: case NI_AVX512BW_ShiftLeftLogicalVariable: + case NI_AVX512BW_VL_ShiftLeftLogicalVariable: + case NI_AVX10v1_ShiftLeftLogicalVariable: #endif { return GT_LSH; @@ -28330,7 +27276,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #ifdef TARGET_ARM64 case NI_AdvSimd_ShiftLeftLogicalScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28350,6 +27296,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const case NI_AVX512F_VL_ShiftRightArithmeticVariable: case NI_AVX512BW_ShiftRightArithmetic: case NI_AVX512BW_ShiftRightArithmeticVariable: + case NI_AVX512BW_VL_ShiftRightArithmeticVariable: case NI_AVX10v1_ShiftRightArithmetic: case NI_AVX10v1_ShiftRightArithmeticVariable: #endif @@ -28360,7 +27307,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #ifdef TARGET_ARM64 case NI_AdvSimd_ShiftRightArithmeticScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28378,6 +27325,8 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const case NI_AVX512F_ShiftRightLogicalVariable: case NI_AVX512BW_ShiftRightLogical: case NI_AVX512BW_ShiftRightLogicalVariable: + case NI_AVX512BW_VL_ShiftRightLogicalVariable: + case NI_AVX10v1_ShiftRightLogicalVariable: #endif { return GT_RSZ; @@ -28386,7 +27335,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #ifdef TARGET_ARM64 case NI_AdvSimd_ShiftRightLogicalScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28423,7 +27372,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_SubtractScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28457,7 +27406,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_Arm64_CompareEqualScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28491,7 +27440,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_Arm64_CompareGreaterThanScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28523,7 +27472,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_Arm64_CompareGreaterThanOrEqualScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28557,7 +27506,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_Arm64_CompareLessThanScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28589,7 +27538,7 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const #if defined(TARGET_ARM64) case NI_AdvSimd_Arm64_CompareLessThanOrEqualScalar: { - if (genTypeSize(GetSimdBaseType()) != 8) + if (genTypeSize(simdBaseType) != 8) { *isScalar = true; } @@ -28620,6 +27569,1229 @@ genTreeOps GenTreeHWIntrinsic::HWOperGet(bool* isScalar) const } } +//------------------------------------------------------------------------------ +// GetHWIntrinsicIdForUnOp: Returns intrinsic ID based on the oper, base type, and simd size +// +// Arguments: +// comp - The compiler instance +// oper - The oper for which to get the intrinsic ID +// op1 - The first operand on which oper is executed +// simdBaseType - The base type on which oper is executed +// simdSize - The simd size on which oper is executed +// isScalar - True if the oper is over scalar data; otherwise false +// +// Returns: +// The intrinsic ID based on the oper, base type, and simd size +// +NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForUnOp( + Compiler* comp, genTreeOps oper, GenTree* op1, var_types simdBaseType, unsigned simdSize, bool isScalar) +{ + var_types simdType = comp->getSIMDTypeForSize(simdSize); + + assert(varTypeIsArithmetic(simdBaseType)); + assert(varTypeIsSIMD(simdType)); + + if (simdSize == 64) + { + assert(!isScalar); + assert(comp->IsBaselineVector512IsaSupportedDebugOnly()); + } + else if (simdSize == 32) + { + assert(!isScalar); + assert(comp->IsBaselineVector256IsaSupportedDebugOnly()); + } + else + { +#if defined(TARGET_ARM64) + assert(!isScalar || (simdSize == 8)); +#endif // TARGET_ARM64 + + assert(!isScalar || varTypeIsFloating(simdBaseType)); + assert(comp->IsBaselineSimdIsaSupportedDebugOnly()); + } + + assert(op1 != nullptr); + assert(op1->TypeIs(simdType)); + + NamedIntrinsic id = NI_Illegal; + + switch (oper) + { + case GT_NEG: + { +#if defined(TARGET_ARM64) + assert(varTypeIsSigned(simdBaseType) || varTypeIsFloating(simdBaseType)); + + if (varTypeIsLong(simdBaseType)) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_NegateScalar : NI_AdvSimd_Arm64_Negate; + } + else if ((simdSize == 8) && (isScalar || (genTypeSize(simdBaseType) == 8))) + { + id = NI_AdvSimd_NegateScalar; + } + else if (simdBaseType == TYP_DOUBLE) + { + id = NI_AdvSimd_Arm64_Negate; + } + else + { + id = NI_AdvSimd_Negate; + } +#endif // TARGET_ARM64 + break; + } + + case GT_NOT: + { + assert(!isScalar); + +#if defined(TARGET_ARM64) + id = NI_AdvSimd_Not; +#endif // TARGET_ARM64 + break; + } + + default: + { + unreached(); + } + } + + return id; +} + +//------------------------------------------------------------------------------ +// GetHWIntrinsicIdForBinOp: Returns intrinsic ID based on the oper, base type, and simd size +// +// Arguments: +// comp - The compiler instance +// oper - The oper for which to get the intrinsic ID +// op1 - The first operand on which oper is executed +// op2 - The second operand on which oper is executed +// simdBaseType - The base type on which oper is executed +// simdSize - The simd size on which oper is executed +// isScalar - True if the oper is over scalar data; otherwise false +// +// Returns: +// The intrinsic ID based on the oper, base type, and simd size +// +NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForBinOp(Compiler* comp, + genTreeOps oper, + GenTree* op1, + GenTree* op2, + var_types simdBaseType, + unsigned simdSize, + bool isScalar) +{ + var_types simdType = comp->getSIMDTypeForSize(simdSize); + + assert(varTypeIsArithmetic(simdBaseType)); + assert(varTypeIsSIMD(simdType)); + + assert(op1 != nullptr); + assert(op1->TypeIs(simdType)); + assert(op2 != nullptr); + + if (simdSize == 64) + { + assert(!isScalar); + assert(comp->IsBaselineVector512IsaSupportedDebugOnly()); + } + else if (simdSize == 32) + { + assert(!isScalar); + assert(comp->IsBaselineVector256IsaSupportedDebugOnly()); + } + else + { +#if defined(TARGET_ARM64) + assert(!isScalar || (simdSize == 8)); +#endif // TARGET_ARM64 + + assert(!isScalar || varTypeIsFloating(simdBaseType)); + assert(comp->IsBaselineSimdIsaSupportedDebugOnly()); + } + + NamedIntrinsic id = NI_Illegal; + + switch (oper) + { + case GT_ADD: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsSmall(simdBaseType)) + { + id = NI_AVX512BW_Add; + } + else + { + id = NI_AVX512F_Add; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_Add; + } + else + { + id = NI_AVX_Add; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_AddScalar : NI_SSE_Add; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_AddScalar : NI_SSE2_Add; + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (isScalar || (genTypeSize(simdBaseType) == 8))) + { + id = NI_AdvSimd_AddScalar; + } + else if (simdBaseType == TYP_DOUBLE) + { + id = NI_AdvSimd_Arm64_Add; + } + else + { + id = NI_AdvSimd_Add; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_AND: + { + assert(!isScalar); + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX512DQ_And; + } + else + { + id = NI_AVX512F_And; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_And; + } + else + { + id = NI_AVX_And; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = NI_SSE_And; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_And; + } +#elif defined(TARGET_ARM64) + id = NI_AdvSimd_And; +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_AND_NOT: + { + assert(!isScalar); + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX512DQ_AndNot; + } + else + { + id = NI_AVX512F_AndNot; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_AndNot; + } + else + { + id = NI_AVX_AndNot; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = NI_SSE_AndNot; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_AndNot; + } +#elif defined(TARGET_ARM64) + + id = NI_AdvSimd_BitwiseClear; +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_DIV: + { + assert(varTypeIsFloating(simdBaseType)); + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + id = NI_AVX512F_Divide; + } + else if (simdSize == 32) + { + id = NI_AVX_Divide; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_DivideScalar : NI_SSE_Divide; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_DivideScalar : NI_SSE2_Divide; + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (isScalar || (simdBaseType == TYP_DOUBLE))) + { + id = NI_AdvSimd_DivideScalar; + } + else + { + id = NI_AdvSimd_Arm64_Divide; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_LSH: + { + assert(!isScalar); + assert(op2->TypeIs(simdType) || varTypeIsInt(op2)); + assert(varTypeIsIntegral(simdBaseType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsShort(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512BW_ShiftLeftLogical : NI_AVX512BW_ShiftLeftLogicalVariable; + } + else if (!varTypeIsByte(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512F_ShiftLeftLogical : NI_AVX512F_ShiftLeftLogicalVariable; + } + } + else if (varTypeIsShort(simdBaseType)) + { + if (varTypeIsInt(op2)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_ShiftLeftLogical; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftLeftLogical; + } + } + else + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512BW_VL)) + { + id = isV512Supported ? NI_AVX512BW_VL_ShiftLeftLogicalVariable + : NI_AVX10v1_ShiftLeftLogicalVariable; + } + } + } + else if (!varTypeIsByte(simdBaseType)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = varTypeIsInt(op2) ? NI_AVX2_ShiftLeftLogical : NI_AVX2_ShiftLeftLogicalVariable; + } + else if (varTypeIsInt(op2)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftLeftLogical; + } + else if (comp->compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + id = NI_AVX2_ShiftLeftLogicalVariable; + } + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (genTypeSize(simdBaseType) == 8)) + { + id = op2->IsCnsIntOrI() ? NI_AdvSimd_ShiftLeftLogicalScalar : NI_AdvSimd_ShiftLogicalScalar; + } + else + { + id = op2->IsCnsIntOrI() ? NI_AdvSimd_ShiftLeftLogical : NI_AdvSimd_ShiftLogical; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_MUL: + { +#if defined(TARGET_XARCH) + assert(op2->TypeIs(simdType)); + + if (simdSize == 64) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX512F_Multiply; + } + else if (varTypeIsLong(simdBaseType)) + { + id = NI_AVX512DQ_MultiplyLow; + } + else if (varTypeIsInt(simdBaseType)) + { + id = NI_AVX512F_MultiplyLow; + } + else if (varTypeIsShort(simdBaseType)) + { + id = NI_AVX512BW_MultiplyLow; + } + } + else if (varTypeIsLong(simdBaseType)) + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512DQ_VL)) + { + id = isV512Supported ? NI_AVX512DQ_VL_MultiplyLow : NI_AVX10v1_MultiplyLow; + } + } + else if (simdSize == 32) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX_Multiply; + } + else if (!varTypeIsByte(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_MultiplyLow; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_MultiplyScalar : NI_SSE_Multiply; + } + else if (simdBaseType == TYP_DOUBLE) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_MultiplyScalar : NI_SSE2_Multiply; + } + else if (varTypeIsInt(simdBaseType)) + { + if (comp->compOpportunisticallyDependsOn(InstructionSet_SSE41)) + { + id = NI_SSE41_MultiplyLow; + } + } + else if (varTypeIsShort(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_MultiplyLow; + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (isScalar || (simdBaseType == TYP_DOUBLE))) + { + id = NI_AdvSimd_MultiplyScalar; + } + else if (simdBaseType == TYP_DOUBLE) + { + id = op2->TypeIs(simdType) ? NI_AdvSimd_Arm64_Multiply : NI_AdvSimd_Arm64_MultiplyByScalar; + } + else if (!varTypeIsLong(simdBaseType)) + { + id = op2->TypeIs(simdType) ? NI_AdvSimd_Multiply : NI_AdvSimd_MultiplyByScalar; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_OR: + { + assert(!isScalar); + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX512DQ_Or; + } + else + { + id = NI_AVX512F_Or; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_Or; + } + else + { + id = NI_AVX_Or; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = NI_SSE_Or; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_Or; + } +#elif defined(TARGET_ARM64) + + id = NI_AdvSimd_Or; +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_ROL: + { + assert(!isScalar); + assert(op2->TypeIs(simdType) || varTypeIsInt(op2)); + assert(varTypeIsIntegral(simdBaseType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (!varTypeIsSmall(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512F_RotateLeft : NI_AVX512F_RotateLeftVariable; + } + } + else if (!varTypeIsSmall(simdBaseType)) + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512DQ_VL)) + { + if (isV512Supported) + { + id = varTypeIsInt(op2) ? NI_AVX512F_VL_RotateLeft : NI_AVX512F_VL_RotateLeftVariable; + } + else + { + id = varTypeIsInt(op2) ? NI_AVX10v1_RotateLeft : NI_AVX10v1_RotateLeftVariable; + } + } + } +#endif // TARGET_XARCH + break; + } + + case GT_ROR: + { + assert(!isScalar); + assert(op2->TypeIs(simdType) || varTypeIsInt(op2)); + assert(varTypeIsIntegral(simdBaseType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (!varTypeIsSmall(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512F_RotateRight : NI_AVX512F_RotateRightVariable; + } + } + else if (!varTypeIsSmall(simdBaseType)) + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512DQ_VL)) + { + if (isV512Supported) + { + id = varTypeIsInt(op2) ? NI_AVX512F_VL_RotateRight : NI_AVX512F_VL_RotateRightVariable; + } + else + { + id = varTypeIsInt(op2) ? NI_AVX10v1_RotateRight : NI_AVX10v1_RotateRightVariable; + } + } + } +#endif // TARGET_XARCH + break; + } + + case GT_RSH: + { + assert(!isScalar); + assert(op2->TypeIs(simdType) || varTypeIsInt(op2)); + assert(varTypeIsIntegral(simdBaseType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsShort(simdBaseType)) + { + id = + varTypeIsInt(op2) ? NI_AVX512BW_ShiftRightArithmetic : NI_AVX512BW_ShiftRightArithmeticVariable; + } + else if (!varTypeIsByte(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512F_ShiftRightArithmetic : NI_AVX512F_ShiftRightArithmeticVariable; + } + } + else if (genTypeSize(simdBaseType) == 8) + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512F_VL)) + { + if (isV512Supported) + { + id = varTypeIsInt(op2) ? NI_AVX512F_VL_ShiftRightArithmetic + : NI_AVX512F_VL_ShiftRightArithmeticVariable; + } + else + { + id = varTypeIsInt(op2) ? NI_AVX10v1_ShiftRightArithmetic + : NI_AVX10v1_ShiftRightArithmeticVariable; + } + } + } + else if (varTypeIsShort(simdBaseType)) + { + if (varTypeIsInt(op2)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_ShiftRightArithmetic; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftRightArithmetic; + } + } + else + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512BW_VL)) + { + id = isV512Supported ? NI_AVX512BW_VL_ShiftRightArithmeticVariable + : NI_AVX10v1_ShiftRightArithmeticVariable; + } + } + } + else if (!varTypeIsByte(simdBaseType)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = varTypeIsInt(op2) ? NI_AVX2_ShiftRightArithmetic : NI_AVX2_ShiftRightArithmeticVariable; + } + else if (varTypeIsInt(op2)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftRightArithmetic; + } + else if (comp->compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + id = NI_AVX2_ShiftRightArithmeticVariable; + } + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (genTypeSize(simdBaseType) == 8)) + { + id = op2->IsCnsIntOrI() ? NI_AdvSimd_ShiftRightArithmeticScalar : NI_AdvSimd_ShiftArithmeticScalar; + } + else + { + id = op2->IsCnsIntOrI() ? NI_AdvSimd_ShiftRightArithmetic : NI_AdvSimd_ShiftArithmetic; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_RSZ: + { + assert(!isScalar); + assert(op2->TypeIs(simdType) || varTypeIsInt(op2)); + assert(varTypeIsIntegral(simdBaseType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsShort(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512BW_ShiftRightLogical : NI_AVX512BW_ShiftRightLogicalVariable; + } + else if (!varTypeIsByte(simdBaseType)) + { + id = varTypeIsInt(op2) ? NI_AVX512F_ShiftRightLogical : NI_AVX512F_ShiftRightLogicalVariable; + } + } + else if (varTypeIsShort(simdBaseType)) + { + if (varTypeIsInt(op2)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_ShiftRightLogical; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftRightLogical; + } + } + else + { + bool isV512Supported = false; + if (comp->compIsEvexOpportunisticallySupported(isV512Supported, InstructionSet_AVX512BW_VL)) + { + id = isV512Supported ? NI_AVX512BW_VL_ShiftRightLogicalVariable + : NI_AVX10v1_ShiftRightLogicalVariable; + } + } + } + else if (!varTypeIsByte(simdBaseType)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = varTypeIsInt(op2) ? NI_AVX2_ShiftRightLogical : NI_AVX2_ShiftRightLogicalVariable; + } + else if (varTypeIsInt(op2)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_ShiftRightLogical; + } + else if (comp->compOpportunisticallyDependsOn(InstructionSet_AVX2)) + { + id = NI_AVX2_ShiftRightLogicalVariable; + } + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (genTypeSize(simdBaseType) == 8)) + { + id = varTypeIsInt(op2) ? NI_AdvSimd_ShiftRightLogicalScalar : NI_AdvSimd_ShiftLogicalScalar; + } + else + { + id = varTypeIsInt(op2) ? NI_AdvSimd_ShiftRightLogical : NI_AdvSimd_ShiftLogical; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_SUB: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsSmall(simdBaseType)) + { + id = NI_AVX512BW_Subtract; + } + else + { + id = NI_AVX512F_Subtract; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_Subtract; + } + else + { + id = NI_AVX_Subtract; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_SubtractScalar : NI_SSE_Subtract; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_SubtractScalar : NI_SSE2_Subtract; + } +#elif defined(TARGET_ARM64) + if ((simdSize == 8) && (isScalar || (genTypeSize(simdBaseType) == 8))) + { + id = NI_AdvSimd_SubtractScalar; + } + else if (simdBaseType == TYP_DOUBLE) + { + id = NI_AdvSimd_Arm64_Subtract; + } + else + { + id = NI_AdvSimd_Subtract; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_XOR: + { + assert(!isScalar); + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + if (varTypeIsFloating(simdBaseType)) + { + id = NI_AVX512DQ_Xor; + } + else + { + id = NI_AVX512F_Xor; + } + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_Xor; + } + else + { + id = NI_AVX_Xor; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = NI_SSE_Xor; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_Xor; + } +#elif defined(TARGET_ARM64) + id = NI_AdvSimd_Xor; +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + default: + { + unreached(); + } + } + + return id; +} + +//------------------------------------------------------------------------------ +// GetHWIntrinsicIdForCmpOp: Returns intrinsic ID based on the oper, base type, and simd size +// +// Arguments: +// comp - The compiler instance +// oper - The oper for which to get the intrinsic ID +// op1 - The first operand on which oper is executed +// op2 - The second operand on which oper is executed +// simdBaseType - The base type on which oper is executed +// simdSize - The simd size on which oper is executed +// isScalar - True if the oper is over scalar data; otherwise false +// +// Returns: +// The intrinsic ID based on the oper, base type, and simd size +// +NamedIntrinsic GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(Compiler* comp, + genTreeOps oper, + GenTree* op1, + GenTree* op2, + var_types simdBaseType, + unsigned simdSize, + bool isScalar) +{ + var_types simdType = comp->getSIMDTypeForSize(simdSize); + + assert(varTypeIsArithmetic(simdBaseType)); + assert(varTypeIsSIMD(simdType)); + + assert(op1 != nullptr); + assert(op1->TypeIs(simdType)); + assert(op2 != nullptr); + + if (simdSize == 64) + { + assert(!isScalar); + assert(comp->IsBaselineVector512IsaSupportedDebugOnly()); + } + else if (simdSize == 32) + { + assert(!isScalar); + assert(comp->IsBaselineVector256IsaSupportedDebugOnly()); + } + else + { +#if defined(TARGET_ARM64) + assert(!isScalar || (simdSize == 8)); +#endif // TARGET_ARM64 + + assert(!isScalar || varTypeIsFloating(simdBaseType)); + assert(comp->IsBaselineSimdIsaSupportedDebugOnly()); + } + + NamedIntrinsic id = NI_Illegal; + + switch (oper) + { + case GT_EQ: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + id = NI_EVEX_CompareEqualMask; + } + else if (simdSize == 32) + { + if (varTypeIsIntegral(simdBaseType)) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_CompareEqual; + } + else + { + id = NI_AVX_CompareEqual; + } + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarEqual : NI_SSE_CompareEqual; + } + else if (varTypeIsLong(simdBaseType)) + { + if (comp->compOpportunisticallyDependsOn(InstructionSet_SSE41)) + { + id = NI_SSE41_CompareEqual; + } + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarEqual : NI_SSE2_CompareEqual; + } +#elif defined(TARGET_ARM64) + if (genTypeSize(simdBaseType) == 8) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareEqualScalar : NI_AdvSimd_Arm64_CompareEqual; + } + else + { + id = NI_AdvSimd_CompareEqual; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_GE: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (varTypeIsIntegral(simdBaseType)) + { + if (comp->canUseEvexEncoding()) + { + id = NI_EVEX_CompareGreaterThanOrEqualMask; + } + } + else if (simdSize == 64) + { + id = NI_EVEX_CompareGreaterThanOrEqualMask; + } + else if (simdSize == 32) + { + id = NI_AVX_CompareGreaterThanOrEqual; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarGreaterThanOrEqual : NI_SSE_CompareGreaterThanOrEqual; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarGreaterThanOrEqual : NI_SSE2_CompareGreaterThanOrEqual; + } +#elif defined(TARGET_ARM64) + if (genTypeSize(simdBaseType) == 8) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareGreaterThanOrEqualScalar + : NI_AdvSimd_Arm64_CompareGreaterThanOrEqual; + } + else + { + id = NI_AdvSimd_CompareGreaterThanOrEqual; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_GT: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + id = NI_EVEX_CompareGreaterThanMask; + } + else if (varTypeIsIntegral(simdBaseType)) + { + if (varTypeIsSigned(simdBaseType)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_CompareGreaterThan; + } + else if (varTypeIsLong(simdBaseType)) + { + if (comp->compOpportunisticallyDependsOn(InstructionSet_SSE42)) + { + id = NI_SSE42_CompareGreaterThan; + } + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_CompareGreaterThan; + } + } + else if (comp->canUseEvexEncoding()) + { + id = NI_EVEX_CompareGreaterThanMask; + } + } + else if (simdSize == 32) + { + id = NI_AVX_CompareGreaterThan; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarGreaterThan : NI_SSE_CompareGreaterThan; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarGreaterThan : NI_SSE2_CompareGreaterThan; + } +#elif defined(TARGET_ARM64) + if (genTypeSize(simdBaseType) == 8) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareGreaterThanScalar : NI_AdvSimd_Arm64_CompareGreaterThan; + } + else + { + id = NI_AdvSimd_CompareGreaterThan; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_LE: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (varTypeIsIntegral(simdBaseType)) + { + if (comp->canUseEvexEncoding()) + { + id = NI_EVEX_CompareLessThanOrEqualMask; + } + } + else if (simdSize == 64) + { + id = NI_EVEX_CompareLessThanOrEqualMask; + } + else if (simdSize == 32) + { + id = NI_AVX_CompareLessThanOrEqual; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarLessThanOrEqual : NI_SSE_CompareLessThanOrEqual; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarLessThanOrEqual : NI_SSE2_CompareLessThanOrEqual; + } +#elif defined(TARGET_ARM64) + if (genTypeSize(simdBaseType) == 8) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareLessThanOrEqualScalar + : NI_AdvSimd_Arm64_CompareLessThanOrEqual; + } + else + { + id = NI_AdvSimd_CompareLessThanOrEqual; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_LT: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (simdSize == 64) + { + id = NI_EVEX_CompareLessThanMask; + } + else if (varTypeIsIntegral(simdBaseType)) + { + if (varTypeIsSigned(simdBaseType)) + { + if (simdSize == 32) + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_AVX2)); + id = NI_AVX2_CompareLessThan; + } + else if (varTypeIsLong(simdBaseType)) + { + if (comp->compOpportunisticallyDependsOn(InstructionSet_SSE42)) + { + id = NI_SSE42_CompareLessThan; + } + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = NI_SSE2_CompareLessThan; + } + } + else if (comp->canUseEvexEncoding()) + { + id = NI_EVEX_CompareLessThanMask; + } + } + else if (simdSize == 32) + { + id = NI_AVX_CompareLessThan; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarLessThan : NI_SSE_CompareLessThan; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarLessThan : NI_SSE2_CompareLessThan; + } +#elif defined(TARGET_ARM64) + if (genTypeSize(simdBaseType) == 8) + { + id = (simdSize == 8) ? NI_AdvSimd_Arm64_CompareLessThanScalar : NI_AdvSimd_Arm64_CompareLessThan; + } + else + { + id = NI_AdvSimd_CompareLessThan; + } +#endif // !TARGET_XARCH && !TARGET_ARM64 + break; + } + + case GT_NE: + { + assert(op2->TypeIs(simdType)); + +#if defined(TARGET_XARCH) + if (varTypeIsIntegral(simdBaseType)) + { + if (comp->canUseEvexEncoding()) + { + id = NI_EVEX_CompareNotEqualMask; + } + } + else if (simdSize == 64) + { + id = NI_EVEX_CompareNotEqualMask; + } + else if (simdSize == 32) + { + id = NI_AVX_CompareNotEqual; + } + else if (simdBaseType == TYP_FLOAT) + { + id = isScalar ? NI_SSE_CompareScalarNotEqual : NI_SSE_CompareNotEqual; + } + else + { + assert(comp->compIsaSupportedDebugOnly(InstructionSet_SSE2)); + id = isScalar ? NI_SSE2_CompareScalarNotEqual : NI_SSE2_CompareNotEqual; + } +#endif // TARGET_XARCH + break; + } + + default: + { + unreached(); + } + } + + return id; +} + //--------------------------------------------------------------------------------------- // GenTreeHWIntrinsic::ShouldConstantProp: // Determines if a given operand should be constant propagated @@ -29543,10 +29715,10 @@ uint8_t GenTreeHWIntrinsic::GetTernaryControlByte(GenTreeHWIntrinsic* second) co bool isScalar = false; - genTreeOps firstOper = HWOperGet(&isScalar); + genTreeOps firstOper = GetOperForHWIntrinsicId(&isScalar); assert(!isScalar); - genTreeOps secondOper = second->HWOperGet(&isScalar); + genTreeOps secondOper = second->GetOperForHWIntrinsicId(&isScalar); assert(!isScalar); uint8_t AB = 0; @@ -29669,7 +29841,7 @@ bool GenTree::IsVectorPerElementMask(var_types simdBaseType, unsigned simdSize) } bool isScalar = false; - genTreeOps oper = intrinsic->HWOperGet(&isScalar); + genTreeOps oper = intrinsic->GetOperForHWIntrinsicId(&isScalar); switch (oper) { @@ -29981,7 +30153,7 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) } bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); #if defined(TARGET_XARCH) if (oper == GT_AND_NOT) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 04c5605fe7bfdf..ec043090da3465 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6624,7 +6624,28 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic static bool Equals(GenTreeHWIntrinsic* op1, GenTreeHWIntrinsic* op2); - genTreeOps HWOperGet(bool* isScalar) const; + static NamedIntrinsic GetHWIntrinsicIdForUnOp( + Compiler* comp, genTreeOps oper, GenTree* op1, var_types simdBaseType, unsigned simdSize, bool isScalar); + static NamedIntrinsic GetHWIntrinsicIdForBinOp(Compiler* comp, + genTreeOps oper, + GenTree* op1, + GenTree* op2, + var_types simdBaseType, + unsigned simdSize, + bool isScalar); + static NamedIntrinsic GetHWIntrinsicIdForCmpOp(Compiler* comp, + genTreeOps oper, + GenTree* op1, + GenTree* op2, + var_types simdBaseType, + unsigned simdSize, + bool isScalar); + static genTreeOps GetOperForHWIntrinsicId(NamedIntrinsic id, var_types simdBaseType, bool* isScalar); + + genTreeOps GetOperForHWIntrinsicId(bool* isScalar) const + { + return GetOperForHWIntrinsicId(GetHWIntrinsicId(), GetSimdBaseType(), isScalar); + } bool ShouldConstantProp(GenTree* operand, GenTreeVecCon* vecCon); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index cfb2b2bd97c90c..97800d5d10ddb8 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -1416,7 +1416,7 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) } bool isScalar = false; - genTreeOps oper = node->HWOperGet(&isScalar); + genTreeOps oper = node->GetOperForHWIntrinsicId(&isScalar); switch (oper) { @@ -1452,7 +1452,7 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) } bool nestedIsScalar = false; - genTreeOps nestedOper = second->AsHWIntrinsic()->HWOperGet(&isScalar); + genTreeOps nestedOper = second->AsHWIntrinsic()->GetOperForHWIntrinsicId(&isScalar); if (nestedOper == GT_NONE) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 6798832d1f8780..0205f020825ff4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -9968,7 +9968,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) default: { bool isScalar = false; - genTreeOps actualOper = node->HWOperGet(&isScalar); + genTreeOps actualOper = node->GetOperForHWIntrinsicId(&isScalar); genTreeOps oper = actualOper; if (GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(oper)) @@ -10125,7 +10125,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) } bool isScalar = false; - genTreeOps oper = node->HWOperGet(&isScalar); + genTreeOps oper = node->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -10148,7 +10148,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) { // Try handle: ~op1 & op2 GenTreeHWIntrinsic* hw = op1->AsHWIntrinsic(); - genTreeOps hwOper = hw->HWOperGet(&isScalar); + genTreeOps hwOper = hw->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -10174,7 +10174,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) { // Try handle: op1 & ~op2 GenTreeHWIntrinsic* hw = op2->AsHWIntrinsic(); - genTreeOps hwOper = hw->HWOperGet(&isScalar); + genTreeOps hwOper = hw->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -10229,7 +10229,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) if (op1->OperIsHWIntrinsic()) { GenTreeHWIntrinsic* hw = op1->AsHWIntrinsic(); - genTreeOps hwOper = hw->HWOperGet(&isScalar); + genTreeOps hwOper = hw->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -10266,7 +10266,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) if (op1->OperIsHWIntrinsic()) { GenTreeHWIntrinsic* hw = op1->AsHWIntrinsic(); - genTreeOps hwOper = hw->HWOperGet(&isScalar); + genTreeOps hwOper = hw->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -10308,7 +10308,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) if (op2->OperIsHWIntrinsic()) { GenTreeHWIntrinsic* hw = op2->AsHWIntrinsic(); - genTreeOps hwOper = hw->HWOperGet(&isScalar); + genTreeOps hwOper = hw->GetOperForHWIntrinsicId(&isScalar); var_types hwSimdBaseType = hw->GetSimdBaseType(); if (isScalar) @@ -10414,7 +10414,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* tree) } bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); bool needsMatchingBaseType = false; switch (oper) @@ -10457,7 +10457,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsicAssociative(GenTreeHWIntrinsic* tree) GenTreeHWIntrinsic* intrinOp1 = effectiveOp1->AsHWIntrinsic(); bool op1IsScalar = false; - genTreeOps op1Oper = intrinOp1->HWOperGet(&op1IsScalar); + genTreeOps op1Oper = intrinOp1->GetOperForHWIntrinsicId(&op1IsScalar); if ((op1Oper != oper) || (op1IsScalar != isScalar)) { diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 624b0a53967960..09ea63f5e0ae4b 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -7563,7 +7563,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunUnary( if (IsVNConstant(arg0VN)) { bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); if (oper != GT_NONE) { @@ -7915,7 +7915,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree, assert(IsVNConstant(arg0VN) && IsVNConstant(arg1VN)); bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); if (oper != GT_NONE) { @@ -8045,7 +8045,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree, else if (cnsVN != NoVN) { bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { @@ -8395,7 +8395,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree, else if (arg0VN == arg1VN) { bool isScalar = false; - genTreeOps oper = tree->HWOperGet(&isScalar); + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); if (isScalar) { From acfb91f9a9f98451e59191c4810a23aef9d40459 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:31:05 +0100 Subject: [PATCH 45/72] Remove link to fixed GitHub issue (#104521) --- src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs index 160c2870a1b96a..1abb6be7c3bc68 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeSpan.cs @@ -670,7 +670,7 @@ private static TimeSpan IntervalFromDoubleTicks(double ticks) /// /// is equal to . /// - public static TimeSpan FromMicroseconds(double value) => Interval(value, TicksPerMicrosecond); // ISSUE: https://github.com/dotnet/runtime/issues/66815 + public static TimeSpan FromMicroseconds(double value) => Interval(value, TicksPerMicrosecond); public static TimeSpan FromMinutes(double value) => Interval(value, TicksPerMinute); From 7b712815b51ce099c0665fe1a2b37947924ccc37 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Mon, 8 Jul 2024 00:22:08 +0800 Subject: [PATCH 46/72] Convert Array.IsSimpleCopy and CanAssignArray type to managed (#104103) * Revert "Push back changes around IsSimpleCopy and CanAssignArrayType" This reverts commit 4ce4f5118c20f438c1e33a5a73d626f1180c86d5. * Reduce use range of CorElementType * Add test for pointer array * Rearrange test * NativeAot compat * Mono compat * Disable test on mono * Don't lookup cast cache in QCall --- .../src/System/Array.CoreCLR.cs | 129 +++++++++++--- .../RuntimeHelpers.CoreCLR.cs | 25 ++- .../classlibnative/bcltype/arraynative.cpp | 160 ------------------ .../classlibnative/bcltype/arraynative.h | 4 - .../src/System/Array.NativeAot.cs | 11 +- src/coreclr/vm/comutilnative.cpp | 35 ++++ src/coreclr/vm/comutilnative.h | 1 + src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 2 +- .../System.Runtime.Tests/System/ArrayTests.cs | 20 +++ .../src/System/Array.Mono.cs | 2 +- 11 files changed, 195 insertions(+), 195 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs index 2efcb649844afc..f05992c2352cf9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs @@ -62,7 +62,10 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de if ((uint)(destinationIndex + length) > destinationArray.NativeLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); - if (sourceArray.GetType() == destinationArray.GetType() || IsSimpleCopy(sourceArray, destinationArray)) + ArrayAssignType assignType = ArrayAssignType.WrongType; + + if (sourceArray.GetType() == destinationArray.GetType() + || (assignType = CanAssignArrayType(sourceArray, destinationArray)) == ArrayAssignType.SimpleCopy) { MethodTable* pMT = RuntimeHelpers.GetMethodTable(sourceArray); @@ -86,44 +89,50 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_ConstrainedCopy); // Rare - CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + CopySlow(sourceArray, sourceIndex, destinationArray, destinationIndex, length, assignType); } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool IsSimpleCopy(Array sourceArray, Array destinationArray); + private static CorElementType GetNormalizedIntegralArrayElementType(CorElementType elementType) + { + Debug.Assert(elementType.IsPrimitiveType()); + + // Array Primitive types such as E_T_I4 and E_T_U4 are interchangeable + // Enums with interchangeable underlying types are interchangeable + // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 + + // U1/U2/U4/U8/U + int shift = (0b0010_0000_0000_0000_1010_1010_0000 >> (int)elementType) & 1; + return (CorElementType)((int)elementType - shift); + } // Reliability-wise, this method will either possibly corrupt your // instance & might fail when called from within a CER, or if the // reliable flag is true, it will either always succeed or always // throw an exception with no side effects. - private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) + private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length, ArrayAssignType assignType) { Debug.Assert(sourceArray.Rank == destinationArray.Rank); - void* srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->ElementType; - void* destTH = RuntimeHelpers.GetMethodTable(destinationArray)->ElementType; - AssignArrayEnum r = CanAssignArrayType(srcTH, destTH); - - if (r == AssignArrayEnum.AssignWrongType) + if (assignType == ArrayAssignType.WrongType) throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); if (length > 0) { - switch (r) + switch (assignType) { - case AssignArrayEnum.AssignUnboxValueClass: + case ArrayAssignType.UnboxValueClass: CopyImplUnBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignBoxValueClassOrPrimitive: + case ArrayAssignType.BoxValueClassOrPrimitive: CopyImplBoxEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignMustCast: + case ArrayAssignType.MustCast: CopyImplCastCheckEachElement(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; - case AssignArrayEnum.AssignPrimitiveWiden: + case ArrayAssignType.PrimitiveWiden: CopyImplPrimitiveWiden(sourceArray, sourceIndex, destinationArray, destinationIndex, length); break; @@ -134,18 +143,90 @@ private static unsafe void CopySlow(Array sourceArray, int sourceIndex, Array de } } - // Must match the definition in arraynative.cpp - private enum AssignArrayEnum + private enum ArrayAssignType { - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, + SimpleCopy, + WrongType, + MustCast, + BoxValueClassOrPrimitive, + UnboxValueClass, + PrimitiveWiden, } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Array_CanAssignArrayType")] - private static unsafe partial AssignArrayEnum CanAssignArrayType(void* srcTH, void* dstTH); + private static unsafe ArrayAssignType CanAssignArrayType(Array sourceArray, Array destinationArray) + { + TypeHandle srcTH = RuntimeHelpers.GetMethodTable(sourceArray)->GetArrayElementTypeHandle(); + TypeHandle destTH = RuntimeHelpers.GetMethodTable(destinationArray)->GetArrayElementTypeHandle(); + + if (TypeHandle.AreSameType(srcTH, destTH)) // This check kicks for different array kind or dimensions + return ArrayAssignType.SimpleCopy; + + if (!srcTH.IsTypeDesc && !destTH.IsTypeDesc) + { + MethodTable* pMTsrc = srcTH.AsMethodTable(); + MethodTable* pMTdest = destTH.AsMethodTable(); + + // Value class boxing + if (pMTsrc->IsValueType && !pMTdest->IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.BoxValueClassOrPrimitive; + else + return ArrayAssignType.WrongType; + } + + // Value class unboxing. + if (!pMTsrc->IsValueType && pMTdest->IsValueType) + { + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.UnboxValueClass; + else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. + return ArrayAssignType.UnboxValueClass; + else + return ArrayAssignType.WrongType; + } + + // Copying primitives from one type to another + if (pMTsrc->IsPrimitive && pMTdest->IsPrimitive) + { + CorElementType srcElType = pMTsrc->GetPrimitiveCorElementType(); + CorElementType destElType = pMTdest->GetPrimitiveCorElementType(); + + if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) + return ArrayAssignType.SimpleCopy; + else if (RuntimeHelpers.CanPrimitiveWiden(srcElType, destElType)) + return ArrayAssignType.PrimitiveWiden; + else + return ArrayAssignType.WrongType; + } + + // src Object extends dest + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.SimpleCopy; + + // dest Object extends src + if (destTH.CanCastTo(srcTH)) + return ArrayAssignType.MustCast; + + // class X extends/implements src and implements dest. + if (pMTdest->IsInterface) + return ArrayAssignType.MustCast; + + // class X implements src and extends/implements dest + if (pMTsrc->IsInterface) + return ArrayAssignType.MustCast; + } + else + { + // Only pointers are valid for TypeDesc in array element + + // Compatible pointers + if (srcTH.CanCastTo(destTH)) + return ArrayAssignType.SimpleCopy; + } + + return ArrayAssignType.WrongType; + } // Unboxes from an Object[] into a value class or primitive array. private static unsafe void CopyImplUnBoxEachElement(Array sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 052f6970a298e1..77e579ce4680bd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -818,7 +818,7 @@ public bool CanCompareBitsOrUseFastGetHashCode /// /// A type handle, which can wrap either a pointer to a TypeDesc or to a . /// - internal unsafe struct TypeHandle + internal readonly unsafe partial struct TypeHandle { // Subset of src\vm\typehandle.h @@ -865,6 +865,29 @@ public static TypeHandle TypeHandleOf() { return new TypeHandle((void*)RuntimeTypeHandle.ToIntPtr(typeof(T).TypeHandle)); } + + public static bool AreSameType(TypeHandle left, TypeHandle right) => left.m_asTAddr == right.m_asTAddr; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CanCastTo(TypeHandle destTH) + { + if (m_asTAddr == destTH.m_asTAddr) + return true; + + if (!IsTypeDesc && destTH.IsTypeDesc) + return false; + + CastResult result = CastCache.TryGet(CastHelpers.s_table!, (nuint)m_asTAddr, (nuint)destTH.m_asTAddr); + + if (result != CastResult.MaybeCast) + return result == CastResult.CanCast; + + return CanCastTo_NoCacheLookup(m_asTAddr, destTH.m_asTAddr); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeHandle_CanCastTo_NoCacheLookup")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); } // Helper structs used for tail calls via helper. diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index 7a42245f76888a..c5e95e7e824709 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -47,166 +47,6 @@ extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHand return ctorEntrypoint; } - // Returns whether you can directly copy an array of srcType into destType. -FCIMPL2(FC_BOOL_RET, ArrayNative::IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst) -{ - FCALL_CONTRACT; - - _ASSERTE(pSrc != NULL); - _ASSERTE(pDst != NULL); - - // This case is expected to be handled by the fast path - _ASSERTE(pSrc->GetMethodTable() != pDst->GetMethodTable()); - - TypeHandle srcTH = pSrc->GetMethodTable()->GetArrayElementTypeHandle(); - TypeHandle destTH = pDst->GetMethodTable()->GetArrayElementTypeHandle(); - if (srcTH == destTH) // This check kicks for different array kind or dimensions - FC_RETURN_BOOL(true); - - if (srcTH.IsValueType()) - { - // Value class boxing - if (!destTH.IsValueType()) - FC_RETURN_BOOL(false); - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - if (GetNormalizedIntegralArrayElementType(srcElType) == GetNormalizedIntegralArrayElementType(destElType)) - FC_RETURN_BOOL(true); - } - } - else - { - // Value class unboxing - if (destTH.IsValueType()) - FC_RETURN_BOOL(false); - } - - TypeHandle::CastResult r = srcTH.CanCastToCached(destTH); - if (r != TypeHandle::MaybeCast) - { - FC_RETURN_BOOL(r); - } - - struct - { - OBJECTREF src; - OBJECTREF dst; - } gc; - - gc.src = ObjectToOBJECTREF(pSrc); - gc.dst = ObjectToOBJECTREF(pDst); - - BOOL iRetVal = FALSE; - - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - iRetVal = srcTH.CanCastTo(destTH); - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(iRetVal); -} -FCIMPLEND - - -// Return values for CanAssignArrayType -enum AssignArrayEnum -{ - AssignWrongType, - AssignMustCast, - AssignBoxValueClassOrPrimitive, - AssignUnboxValueClass, - AssignPrimitiveWiden, -}; - -// Returns an enum saying whether you can copy an array of srcType into destType. -static AssignArrayEnum CanAssignArrayType(const TypeHandle srcTH, const TypeHandle destTH) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - PRECONDITION(!srcTH.IsNull()); - PRECONDITION(!destTH.IsNull()); - } - CONTRACTL_END; - - _ASSERTE(srcTH != destTH); // Handled by fast path - - // Value class boxing - if (srcTH.IsValueType() && !destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignBoxValueClassOrPrimitive; - else - return AssignWrongType; - } - - // Value class unboxing. - if (!srcTH.IsValueType() && destTH.IsValueType()) - { - if (srcTH.CanCastTo(destTH)) - return AssignUnboxValueClass; - else if (destTH.CanCastTo(srcTH)) // V extends IV. Copying from IV to V, or Object to V. - return AssignUnboxValueClass; - else - return AssignWrongType; - } - - const CorElementType srcElType = srcTH.GetVerifierCorElementType(); - const CorElementType destElType = destTH.GetVerifierCorElementType(); - _ASSERTE(srcElType < ELEMENT_TYPE_MAX); - _ASSERTE(destElType < ELEMENT_TYPE_MAX); - - // Copying primitives from one type to another - if (CorTypeInfo::IsPrimitiveType_NoThrow(srcElType) && CorTypeInfo::IsPrimitiveType_NoThrow(destElType)) - { - _ASSERTE(srcElType != destElType); // Handled by fast path - if (InvokeUtil::CanPrimitiveWiden(destElType, srcElType)) - return AssignPrimitiveWiden; - else - return AssignWrongType; - } - - // dest Object extends src - _ASSERTE(!srcTH.CanCastTo(destTH)); // Handled by fast path - - // src Object extends dest - if (destTH.CanCastTo(srcTH)) - return AssignMustCast; - - // class X extends/implements src and implements dest. - if (destTH.IsInterface() && srcElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - // class X implements src and extends/implements dest - if (srcTH.IsInterface() && destElType != ELEMENT_TYPE_VALUETYPE) - return AssignMustCast; - - return AssignWrongType; -} - -extern "C" int QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH) -{ - QCALL_CONTRACT; - - INT32 ret = 0; - - BEGIN_QCALL; - - ret = CanAssignArrayType(TypeHandle::FromPtr(srcTH), TypeHandle::FromPtr(destTH)); - - END_QCALL; - - return ret; -} - - // // This is a GC safe variant of the memmove intrinsic. It sets the cards, and guarantees that the object references in the GC heap are // updated atomically. diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 86b125c416bf0b..aa4430c9b21d8f 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -13,19 +13,15 @@ #ifndef _ARRAYNATIVE_H_ #define _ARRAYNATIVE_H_ -#include "fcall.h" #include "qcall.h" class ArrayNative { public: static FCDECL1(INT32, GetCorElementTypeOfElementType, ArrayBase* arrayUNSAFE); - - static FCDECL2(FC_BOOL_RET, IsSimpleCopy, ArrayBase* pSrc, ArrayBase* pDst); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); extern "C" PCODE QCALLTYPE Array_GetElementConstructorEntrypoint(QCall::TypeHandle pArrayTypeHnd); -extern "C" INT32 QCALLTYPE Array_CanAssignArrayType(void* srcTH, void* destTH); #endif // _ARRAYNATIVE_H_ diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs index 0c9c571ed0f7e8..58378b92a9c5f0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Array.NativeAot.cs @@ -239,9 +239,14 @@ private static unsafe void CopyImpl(Array sourceArray, int sourceIndex, Array de { // CLR compat note: CLR only allows Array.Copy between pointee types that would be assignable // to using array covariance rules (so int*[] can be copied to uint*[], but not to float*[]). - // This is rather weird since e.g. we don't allow casting int*[] to uint*[] otherwise. - // Instead of trying to replicate the behavior, we're choosing to be simply more permissive here. - CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + if (RuntimeImports.AreTypesAssignable(sourceElementEEType, destinationElementEEType)) + { + CopyImplValueTypeArrayNoInnerGcRefs(sourceArray, sourceIndex, destinationArray, destinationIndex, length); + } + else + { + throw new ArrayTypeMismatchException(SR.ArrayTypeMismatch_CantAssignType); + } } else if (IsSourceElementABaseClassOrInterfaceOfDestinationValueType(sourceElementEEType, destinationElementEEType)) { diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 73547a59e4df3c..d30177f9dc7978 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1846,6 +1846,41 @@ extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, Metho return bResult; } +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd) +{ + QCALL_CONTRACT; + + BOOL ret = false; + + BEGIN_QCALL; + + // Cache lookup and trivial cases are already handled at managed side. Call the uncached versions directly. + _ASSERTE(fromTypeHnd != toTypeHnd); + + GCX_COOP(); + + TypeHandle fromTH = TypeHandle::FromPtr(fromTypeHnd); + TypeHandle toTH = TypeHandle::FromPtr(toTypeHnd); + + if (fromTH.IsTypeDesc()) + { + ret = fromTH.AsTypeDesc()->CanCastTo(toTH, NULL); + } + else if (Nullable::IsNullableForType(toTH, fromTH.AsMethodTable())) + { + // do not allow type T to be cast to Nullable + ret = FALSE; + } + else + { + ret = fromTH.AsMethodTable()->CanCastTo(toTH.AsMethodTable(), NULL); + } + + END_QCALL; + + return ret; +} + static MethodTable * g_pStreamMT; static WORD g_slotBeginRead, g_slotEndRead; static WORD g_slotBeginWrite, g_slotEndWrite; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 0f00aca48466a1..4e70173758f887 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -252,6 +252,7 @@ class MethodTableNative { extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); +extern "C" BOOL QCALLTYPE TypeHandle_CanCastTo_NoCacheLookup(void* fromTypeHnd, void* toTypeHnd); extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize, MethodTable** fieldMT); class StreamNative { diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index db21d429daa9ad..47f630717ab902 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -358,7 +358,6 @@ FCFuncEnd() FCFuncStart(gArrayFuncs) FCFuncElement("GetCorElementTypeOfElementType", ArrayNative::GetCorElementTypeOfElementType) - FCFuncElement("IsSimpleCopy", ArrayNative::IsSimpleCopy) FCFuncEnd() FCFuncStart(gBufferFuncs) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 329989cde6db48..d4d073d9b3abf5 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -103,6 +103,7 @@ static const Entry s_QCall[] = DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) + DllImportEntry(TypeHandle_CanCastTo_NoCacheLookup) DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) @@ -172,7 +173,6 @@ static const Entry s_QCall[] = DllImportEntry(MdUtf8String_EqualsCaseInsensitive) DllImportEntry(Array_CreateInstance) DllImportEntry(Array_GetElementConstructorEntrypoint) - DllImportEntry(Array_CanAssignArrayType) DllImportEntry(AssemblyName_InitializeAssemblySpec) DllImportEntry(AssemblyNative_GetFullName) DllImportEntry(AssemblyNative_GetLocation) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs index 832d890ab61c21..998094377be4f4 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ArrayTests.cs @@ -1,6 +1,7 @@ // 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; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -1597,13 +1598,32 @@ public static void Copy_SourceAndDestinationNeverConvertible_ThrowsArrayTypeMism Assert.Throws(() => sourceArray.CopyTo(destinationArray, (long)destinationArray.GetLowerBound(0))); } + [Fact] + [SkipOnMono("https://github.com/dotnet/runtime/issues/104197")] + public static unsafe void Copy_CompatiblePointers() + { + // Can copy between compatible pointers + uint*[] uintPointerArray = new uint*[1]; + Array.ConstrainedCopy(new int*[1] { (int*)0x12345678 }, 0, uintPointerArray, 0, 1); + Assert.Equal((UIntPtr)0x12345678, (UIntPtr)uintPointerArray[0]); + } + [Fact] public static void Copy_SourceAndDestinationPointers_ThrowsArrayTypeMismatchException() { unsafe { + // Can't copy between pointer and object Assert.Throws(() => Array.Copy(new void*[1], new object[1], 0)); Assert.Throws(() => Array.Copy(new object[1], new void*[1], 0)); + + // Can't copy between pointer and interface + Assert.Throws(() => Array.Copy(new int*[1], new IConvertible[1], 1)); + Assert.Throws(() => Array.Copy(new IConvertible[1], new int*[1], 1)); + + // Can't copy between incompatible pointer types + Assert.Throws(() => Array.Copy(new int*[1], new bool*[1], 0)); + Assert.Throws(() => Array.Copy(new int*[1], new void*[1], 0)); } } diff --git a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs index 7ca268d6cc050b..cded34be47b1f8 100644 --- a/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Array.Mono.cs @@ -259,7 +259,7 @@ private static bool CanAssignArrayElement(Type source, Type target) } else if (source.IsPointer && target.IsPointer) { - return true; + return target.IsAssignableFrom(source); } else if (source.IsPrimitive && target.IsPrimitive) { From 4e278fe17f69ea31fbdcbab74ac47ec6fa84914b Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sun, 7 Jul 2024 17:47:43 +0100 Subject: [PATCH 47/72] Use `XmlResolverIsNetworkingEnabledByDefault` property in `AotCompilerCommon.props` (#104520) --- src/coreclr/tools/aot/AotCompilerCommon.props | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/coreclr/tools/aot/AotCompilerCommon.props b/src/coreclr/tools/aot/AotCompilerCommon.props index 3d20ee261a4b54..847ae3893b1b2d 100644 --- a/src/coreclr/tools/aot/AotCompilerCommon.props +++ b/src/coreclr/tools/aot/AotCompilerCommon.props @@ -7,12 +7,6 @@ Guard true false + false - - - - - From e71e0f4f845ed2e5e2715bef0a6a129d327f914b Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 7 Jul 2024 21:22:12 -0700 Subject: [PATCH 48/72] update proxy setting on registry changes (#103364) * update proxy setting on registry changes * udpate * build fixes * console * registry * winhttp * invalid * feedback * feedback * 'feedback' * Apply suggestions from code review Co-authored-by: Jan Kotas * MemberNotNull --------- Co-authored-by: Jan Kotas --- .../Interop.RegNotifyChangeKeyValue.cs | 26 ++++ .../Net/Http/HttpClientHandlerTest.Proxy.cs | 2 +- .../tests/UnitTests/HttpWindowsProxyTest.cs | 11 +- ....Net.Http.WinHttpHandler.Unit.Tests.csproj | 2 + .../src/System.Net.Http.csproj | 3 + .../SocketsHttpHandler/HttpWindowsProxy.cs | 130 ++++++++++++------ .../SystemProxyInfo.Windows.cs | 5 +- .../tests/UnitTests/HttpWindowsProxyTest.cs | 55 +------- .../System.Net.Http.Unit.Tests.csproj | 2 + 9 files changed, 132 insertions(+), 104 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs new file mode 100644 index 00000000000000..547ad136ccb61f --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegNotifyChangeKeyValue.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Advapi32 + { + internal const int REG_NOTIFY_CHANGE_NAME = 0x1; + internal const int REG_NOTIFY_CHANGE_ATTRIBUTES = 0x2; + internal const int REG_NOTIFY_CHANGE_LAST_SET = 0x4; + internal const int REG_NOTIFY_CHANGE_SECURITY = 0x8; + internal const int REG_NOTIFY_THREAD_AGNOSTIC = 0x10000000; + + [LibraryImport(Libraries.Advapi32, EntryPoint = "RegNotifyChangeKeyValue", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int RegNotifyChangeKeyValue( + SafeHandle hKey, + [MarshalAs(UnmanagedType.Bool)] bool watchSubtree, + uint notifyFilter, + SafeHandle hEvent, + [MarshalAs(UnmanagedType.Bool)] bool asynchronous); + } +} diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index a7bab6a78b6f0f..d3e00ce0327b07 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -505,7 +505,7 @@ public async Task MultiProxy_PAC_Failover_Succeeds() winInetProxyHelperType.GetField("_proxyBypass", Reflection.BindingFlags.Instance | Reflection.BindingFlags.NonPublic).SetValue(winInetProxyHelper, null); // Create a HttpWindowsProxy with our custom WinInetProxyHelper. - IWebProxy httpWindowsProxy = (IWebProxy)Activator.CreateInstance(Type.GetType("System.Net.Http.HttpWindowsProxy, System.Net.Http", true), Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance, null, new[] { winInetProxyHelper, null }, null); + IWebProxy httpWindowsProxy = (IWebProxy)Activator.CreateInstance(Type.GetType("System.Net.Http.HttpWindowsProxy, System.Net.Http", true), Reflection.BindingFlags.Public | Reflection.BindingFlags.NonPublic| Reflection.BindingFlags.Instance, null, new[] { winInetProxyHelper }, null); Task nextFailedConnection = null; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs index aa5cccbb28886e..f0f28e9fc205dc 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/HttpWindowsProxyTest.cs @@ -28,11 +28,6 @@ public static IEnumerable ManualSettingsMemberData() yield return new object[] { new Uri("http://localhost"), true }; } - [Fact] - public void TryCreate_WinInetProxySettingsAllOff_ReturnsFalse() - { - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); - } [Theory] [MemberData(nameof(ManualSettingsMemberData))] @@ -44,7 +39,7 @@ public void GetProxy_BothAutoDetectAndManualSettingsButFailedAutoDetect_ManualSe FakeRegistry.WinInetProxySettings.ProxyBypass = ManualSettingsProxyBypassList; TestControl.PACFileNotDetectedOnNetwork = true; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); // The first GetProxy() call will try using WinInetProxyHelper (and thus WinHTTP) since AutoDetect is on. Uri proxyUri1 = webProxy.GetProxy(destination); @@ -74,7 +69,7 @@ public void GetProxy_ManualSettingsOnly_ManualSettingsUsed( FakeRegistry.WinInetProxySettings.Proxy = ManualSettingsProxyHost; FakeRegistry.WinInetProxySettings.ProxyBypass = ManualSettingsProxyBypassList; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); Uri proxyUri = webProxy.GetProxy(destination); if (bypassProxy) { @@ -90,7 +85,7 @@ public void GetProxy_ManualSettingsOnly_ManualSettingsUsed( public void IsBypassed_ReturnsFalse() { FakeRegistry.WinInetProxySettings.AutoDetect = true; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy webProxy)); + IWebProxy webProxy = new HttpWindowsProxy(); Assert.False(webProxy.IsBypassed(new Uri("http://www.microsoft.com/"))); } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj index fdb2638bdc0288..f8b72896871da4 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/System.Net.Http.WinHttpHandler.Unit.Tests.csproj @@ -96,6 +96,8 @@ Link="ProductionCode\IMultiWebProxy.cs" /> + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index dea20f206cdc9d..fbaf1d950a196a 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -384,6 +384,8 @@ Link="Common\Interop\Windows\WinHttp\Interop.winhttp_types.cs" /> + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs index 9104ace31623d4..caf9104abc8d13 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpWindowsProxy.cs @@ -6,45 +6,81 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO.Compression; using System.Net.NetworkInformation; using System.Runtime.InteropServices; using System.Text; using System.Threading; +using Microsoft.Win32; using SafeWinHttpHandle = Interop.WinHttp.SafeWinHttpHandle; namespace System.Net.Http { internal sealed class HttpWindowsProxy : IMultiWebProxy, IDisposable { - private readonly MultiProxy _insecureProxy; // URI of the http system proxy if set - private readonly MultiProxy _secureProxy; // URI of the https system proxy if set - private readonly FailedProxyCache _failedProxies = new FailedProxyCache(); - private readonly List? _bypass; // list of domains not to proxy - private readonly bool _bypassLocal; // we should bypass domain considered local - private readonly List? _localIp; + private readonly RegistryKey? _internetSettingsRegistry = Registry.CurrentUser?.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); + private MultiProxy _insecureProxy; // URI of the http system proxy if set + private MultiProxy _secureProxy; // URI of the https system proxy if set + private FailedProxyCache _failedProxies = new FailedProxyCache(); + private List? _bypass; // list of domains not to proxy + private List? _localIp; private ICredentials? _credentials; - private readonly WinInetProxyHelper _proxyHelper; + private WinInetProxyHelper _proxyHelper; private SafeWinHttpHandle? _sessionHandle; private bool _disposed; + private EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); + private const int RegistrationFlags = Interop.Advapi32.REG_NOTIFY_CHANGE_NAME | Interop.Advapi32.REG_NOTIFY_CHANGE_LAST_SET | Interop.Advapi32.REG_NOTIFY_CHANGE_ATTRIBUTES | Interop.Advapi32.REG_NOTIFY_THREAD_AGNOSTIC; + private RegisteredWaitHandle? _registeredWaitHandle; - public static bool TryCreate([NotNullWhen(true)] out IWebProxy? proxy) + // 'proxy' used from tests via Reflection + public HttpWindowsProxy(WinInetProxyHelper? proxy = null) { - // This will get basic proxy setting from system using existing - // WinInetProxyHelper functions. If no proxy is enabled, it will return null. - SafeWinHttpHandle? sessionHandle = null; - proxy = null; - WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - if (!proxyHelper.ManualSettingsOnly && !proxyHelper.AutoSettingsUsed) + if (_internetSettingsRegistry != null && proxy == null) { - return false; + // we register for change notifications so we can react to changes during lifetime. + if (Interop.Advapi32.RegNotifyChangeKeyValue(_internetSettingsRegistry.Handle, true, RegistrationFlags, _waitHandle.SafeWaitHandle, true) == 0) + { + _registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(_waitHandle, RegistryChangeNotificationCallback, this, -1, false); + } + } + + UpdateConfiguration(proxy); + } + + private static void RegistryChangeNotificationCallback(object? state, bool timedOut) + { + HttpWindowsProxy proxy = (HttpWindowsProxy)state!; + if (!proxy._disposed) + { + + // This is executed from threadpool. we should not ever throw here. + try + { + // We need to register for notification every time. We regisrerand lock before we process configuration + // so if there is update it would be serialized to ensure consistency. + Interop.Advapi32.RegNotifyChangeKeyValue(proxy._internetSettingsRegistry!.Handle, true, RegistrationFlags, proxy._waitHandle.SafeWaitHandle, true); + lock (proxy) + { + proxy.UpdateConfiguration(); + } + } + catch (Exception ex) + { + if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(proxy, $"Failed to refresh proxy configuration: {ex.Message}"); + } } + } + + [MemberNotNull(nameof(_proxyHelper))] + private void UpdateConfiguration(WinInetProxyHelper? proxyHelper = null) + { + + proxyHelper ??= new WinInetProxyHelper(); if (proxyHelper.AutoSettingsUsed) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(proxyHelper, $"AutoSettingsUsed, calling {nameof(Interop.WinHttp.WinHttpOpen)}"); - sessionHandle = Interop.WinHttp.WinHttpOpen( + SafeWinHttpHandle? sessionHandle = Interop.WinHttp.WinHttpOpen( IntPtr.Zero, Interop.WinHttp.WINHTTP_ACCESS_TYPE_NO_PROXY, Interop.WinHttp.WINHTTP_NO_PROXY_NAME, @@ -56,18 +92,10 @@ public static bool TryCreate([NotNullWhen(true)] out IWebProxy? proxy) // Proxy failures are currently ignored by managed handler. if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(proxyHelper, $"{nameof(Interop.WinHttp.WinHttpOpen)} returned invalid handle"); sessionHandle.Dispose(); - return false; } - } - - proxy = new HttpWindowsProxy(proxyHelper, sessionHandle); - return true; - } - private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sessionHandle) - { - _proxyHelper = proxyHelper; - _sessionHandle = sessionHandle; + _sessionHandle = sessionHandle; + } if (proxyHelper.ManualSettingsUsed) { @@ -80,10 +108,12 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess { int idx = 0; string? tmp; + bool bypassLocal = false; + List? localIp = null; // Process bypass list for manual setting. // Initial list size is best guess based on string length assuming each entry is at least 5 characters on average. - _bypass = new List(proxyHelper.ProxyBypass.Length / 5); + List? bypass = new List(proxyHelper.ProxyBypass.Length / 5); while (idx < proxyHelper.ProxyBypass.Length) { @@ -114,7 +144,7 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess } else if (string.Compare(proxyHelper.ProxyBypass, start, "", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) { - _bypassLocal = true; + bypassLocal = true; tmp = null; } else @@ -137,28 +167,29 @@ private HttpWindowsProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle? sess continue; } - _bypass.Add(tmp); - } - if (_bypass.Count == 0) - { - // Bypass string only had garbage we did not parse. - _bypass = null; + bypass.Add(tmp); } - } - if (_bypassLocal) - { - _localIp = new List(); - foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) + _bypass = bypass.Count > 0 ? bypass : null; + + if (bypassLocal) { - IPInterfaceProperties ipProps = netInterface.GetIPProperties(); - foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) + localIp = new List(); + foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces()) { - _localIp.Add(addr.Address); + IPInterfaceProperties ipProps = netInterface.GetIPProperties(); + foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses) + { + localIp.Add(addr.Address); + } } } + + _localIp = localIp?.Count > 0 ? localIp : null; } } + + _proxyHelper = proxyHelper; } public void Dispose() @@ -171,6 +202,10 @@ public void Dispose() { SafeWinHttpHandle.DisposeAndClearHandle(ref _sessionHandle); } + + _waitHandle?.Dispose(); + _internetSettingsRegistry?.Dispose(); + _registeredWaitHandle?.Unregister(null); } } @@ -179,6 +214,11 @@ public void Dispose() /// public Uri? GetProxy(Uri uri) { + if (!_proxyHelper.AutoSettingsUsed && !_proxyHelper.ManualSettingsOnly) + { + return null; + } + GetMultiProxy(uri).ReadNext(out Uri? proxyUri, out _); return proxyUri; } @@ -240,7 +280,7 @@ public MultiProxy GetMultiProxy(Uri uri) // Fallback to manual settings if present. if (_proxyHelper.ManualSettingsUsed) { - if (_bypassLocal) + if (_localIp != null) { IPAddress? address; @@ -261,7 +301,7 @@ public MultiProxy GetMultiProxy(Uri uri) { // Host is valid IP address. // Check if it belongs to local system. - foreach (IPAddress a in _localIp!) + foreach (IPAddress a in _localIp) { if (a.Equals(address)) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs index c49163a90602bd..4f51c72ac666a4 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/SystemProxyInfo.Windows.cs @@ -10,10 +10,11 @@ public static IWebProxy ConstructSystemProxy() { if (!HttpEnvironmentProxy.TryCreate(out IWebProxy? proxy)) { - HttpWindowsProxy.TryCreate(out proxy); + // We create instance even if there is currently no proxy as that can change during application run. + proxy = new HttpWindowsProxy(); } - return proxy ?? new HttpNoProxy(); + return proxy; } } } diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs index b09ad4a29d9671..9e966e301a3a68 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpWindowsProxyTest.cs @@ -39,8 +39,6 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { FakeRegistry.Reset(); - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy p)); - FakeRegistry.WinInetProxySettings.Proxy = proxyString; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); Assert.Null(proxyHelper.AutoConfigUrl); @@ -48,8 +46,7 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => Assert.False(proxyHelper.AutoSettingsUsed); Assert.True(proxyHelper.ManualSettingsUsed); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); Assert.Equal(!string.IsNullOrEmpty(insecureProxy) ? new Uri(insecureProxy) : null, p.GetProxy(new Uri(fooHttp))); Assert.Equal(!string.IsNullOrEmpty(secureProxy) ? new Uri(secureProxy) : null, p.GetProxy(new Uri(fooHttps))); @@ -81,8 +78,6 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => { TestControl.ResetAll(); - Assert.False(HttpWindowsProxy.TryCreate(out IWebProxy p)); - FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://127.0.0.1/proxy.pac"; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); Assert.Null(proxyHelper.Proxy); @@ -90,8 +85,7 @@ await RemoteExecutor.Invoke((proxyString, insecureProxy, secureProxy) => Assert.False(proxyHelper.ManualSettingsUsed); Assert.True(proxyHelper.AutoSettingsUsed); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); // With a HttpWindowsProxy created configured to use auto-config, now set Proxy so when it // attempts to resolve a proxy, it resolves our string. @@ -137,15 +131,12 @@ public async Task HttpProxy_WindowsProxy_Loaded(string rawProxyString, string ex { await RemoteExecutor.Invoke((proxyString, expectedString) => { - IWebProxy p; - FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = proxyString; WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(proxyHelper); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttp)).ToString()); Assert.Equal(expectedString, p.GetProxy(new Uri(fooHttps)).ToString()); }, rawProxyString, expectedUri).DisposeAsync(); @@ -174,14 +165,12 @@ public async Task HttpProxy_Local_Bypassed(string name, bool shouldBypass) await RemoteExecutor.Invoke((url, expected) => { bool expectedResult = Boolean.Parse(expected); - IWebProxy p; FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri; FakeRegistry.WinInetProxySettings.ProxyBypass = "23.23.86.44;*.foo.com;;BAR.COM; ; 162*;[2002::11];[*:f8b0:4005:80a::200e]; http://www.xn--mnchhausen-9db.at;http://*.xn--bb-bjab.eu;http://xn--bb-bjab.eu;"; - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(); Uri u = new Uri(url); Assert.Equal(expectedResult, p.GetProxy(u) == null); @@ -199,14 +188,12 @@ public async Task HttpProxy_Local_Parsing(string bypass, int count) await RemoteExecutor.Invoke((bypassValue, expected) => { int expectedCount = Convert.ToInt32(expected); - IWebProxy p; FakeRegistry.Reset(); FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri; FakeRegistry.WinInetProxySettings.ProxyBypass = bypassValue; - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy sp = p as HttpWindowsProxy; Assert.NotNull(sp); @@ -222,34 +209,6 @@ await RemoteExecutor.Invoke((bypassValue, expected) => }, bypass, count.ToString()).DisposeAsync(); } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - [InlineData("http://")] - [InlineData("http=")] - [InlineData("http://;")] - [InlineData("http=;")] - [InlineData(" ; ")] - public async Task HttpProxy_InvalidWindowsProxy_Null(string rawProxyString) - { - await RemoteExecutor.Invoke((proxyString) => - { - IWebProxy p; - - FakeRegistry.Reset(); - Assert.False(HttpWindowsProxy.TryCreate(out p)); - - FakeRegistry.WinInetProxySettings.Proxy = proxyString; - WinInetProxyHelper proxyHelper = new WinInetProxyHelper(); - - Assert.True(HttpWindowsProxy.TryCreate(out p)); - Assert.NotNull(p); - - Assert.Null(p.GetProxy(new Uri(fooHttp))); - Assert.Null(p.GetProxy(new Uri(fooHttps))); - Assert.Null(p.GetProxy(new Uri(fooWs))); - Assert.Null(p.GetProxy(new Uri(fooWss))); - }, rawProxyString).DisposeAsync(); - } - [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(HttpProxy_Multi_Data))] public async Task HttpProxy_Multi_Success(string proxyConfig, string url, string expected) @@ -262,7 +221,7 @@ await RemoteExecutor.Invoke((proxyConfigValue, urlValue, expectedValue) => TestControl.ResetAll(); FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://dummy.com"; - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy p)); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy wp = Assert.IsType(p); // Now that HttpWindowsProxy has been constructed to use autoconfig, @@ -319,7 +278,7 @@ await RemoteExecutor.Invoke(manualValue => FakeRegistry.WinInetProxySettings.AutoConfigUrl = "http://dummy.com"; } - Assert.True(HttpWindowsProxy.TryCreate(out IWebProxy p)); + IWebProxy p = new HttpWindowsProxy(); HttpWindowsProxy wp = Assert.IsType(p); if (!manual) diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 05f9963958ced7..f0f23f16ccb3ce 100755 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -353,6 +353,8 @@ Link="ProductionCode\Common\Interop\Windows\Interop.Libraries.cs" /> + Date: Mon, 8 Jul 2024 09:55:19 +0000 Subject: [PATCH 49/72] [browser] Add test case for `maxParallelDownloads` (#104476) --- .../MaxParallelDownloadsTests.cs | 48 +++++++++++++++++++ .../WasmBasicTestApp/App/wwwroot/main.js | 21 ++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/MaxParallelDownloadsTests.cs diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/MaxParallelDownloadsTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/MaxParallelDownloadsTests.cs new file mode 100644 index 00000000000000..d9ae89ef0c6e5a --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/MaxParallelDownloadsTests.cs @@ -0,0 +1,48 @@ +// 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.Linq; +using System.Threading.Tasks; +using Xunit.Abstractions; +using System.Text.RegularExpressions; +using Xunit; + +#nullable enable + +namespace Wasm.Build.Tests.TestAppScenarios; + +public class MaxParallelDownloadsTests : AppTestBase +{ + public MaxParallelDownloadsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [Theory] + [InlineData("Release", "1")] + [InlineData("Release", "4")] + public async Task NeverFetchMoreThanMaxAllowed(string config, string maxParallelDownloads) + { + CopyTestAsset("WasmBasicTestApp", "MaxParallelDownloadsTests", "App"); + BuildProject(config); + + var result = await RunSdkStyleAppForBuild(new( + Configuration: config, + TestScenario: "MaxParallelDownloads", + BrowserQueryString: new Dictionary { ["maxParallelDownloads"] = maxParallelDownloads } + )); + var resultTestOutput = result.TestOutput.ToList(); + var regex = new Regex(@"Active downloads: (\d+)"); + foreach (var line in resultTestOutput) + { + var match = regex.Match(line); + if (match.Success) + { + int activeDownloads = int.Parse(match.Groups[1].Value); + Assert.True(activeDownloads <= int.Parse(maxParallelDownloads), $"Active downloads exceeded the limit: {activeDownloads} > {maxParallelDownloads}"); + } + } + } +} diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 21cc18cc3e7157..0023e28f4c3338 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -93,6 +93,26 @@ switch (testCase) { await dotnet.download(); testOutput("download finished"); break; + case "MaxParallelDownloads": + const maxParallelDownloads = params.get("maxParallelDownloads"); + let activeFetchCount = 0; + const originalFetch2 = globalThis.fetch; + globalThis.fetch = async (...args) => { + activeFetchCount++; + testOutput(`Fetch started. Active downloads: ${activeFetchCount}`); + try { + const response = await originalFetch2(...args); + activeFetchCount--; + testOutput(`Fetch completed. Active downloads: ${activeFetchCount}`); + return response; + } catch (error) { + activeFetchCount--; + testOutput(`Fetch failed. Active downloads: ${activeFetchCount}`); + throw error; + } + }; + dotnet.withConfig({ maxParallelDownloads: maxParallelDownloads }); + break; } const { setModuleImports, getAssemblyExports, getConfig, INTERNAL } = await dotnet.create(); @@ -145,6 +165,7 @@ try { exit(0); break; case "DownloadThenInit": + case "MaxParallelDownloads": exit(0); break; default: From 819826683dbda4a50ffeb0a8ef0a986013dcd3f2 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 8 Jul 2024 12:38:24 +0200 Subject: [PATCH 50/72] [wasi] enable WASMTIME_BACKTRACE_DETAILS (#104446) --- src/libraries/sendtohelix-wasi.targets | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/sendtohelix-wasi.targets b/src/libraries/sendtohelix-wasi.targets index 8a1902eb378fc5..ff1b8f17b277fd 100644 --- a/src/libraries/sendtohelix-wasi.targets +++ b/src/libraries/sendtohelix-wasi.targets @@ -71,6 +71,7 @@ + @@ -79,6 +80,7 @@ + From 26b6161c6b67af3e01051f3188b2102df2bedc8c Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Mon, 8 Jul 2024 12:02:48 +0000 Subject: [PATCH 51/72] [wasm] Re-try when browser's WBT fail with `System.TimeoutException` (#104481) --- .../wasm/Wasm.Build.Tests/BrowserRunner.cs | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs index b37deb5781b290..e632ab472791ab 100644 --- a/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs +++ b/src/mono/wasm/Wasm.Build.Tests/BrowserRunner.cs @@ -102,24 +102,43 @@ public async Task StartServerAndGetUrlAsync( public async Task SpawnBrowserAsync( string browserUrl, - bool headless = true + bool headless = true, + int timeout = 10000, + int maxRetries = 3 ) { var url = new Uri(browserUrl); Playwright = await Microsoft.Playwright.Playwright.CreateAsync(); // codespaces: ignore certificate error -> Microsoft.Playwright.PlaywrightException : net::ERR_CERT_AUTHORITY_INVALID string[] chromeArgs = new[] { $"--explicitly-allowed-ports={url.Port}", "--ignore-certificate-errors" }; _testOutput.WriteLine($"Launching chrome ('{s_chromePath.Value}') via playwright with args = {string.Join(',', chromeArgs)}"); - Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions{ - ExecutablePath = s_chromePath.Value, - Headless = headless, - Args = chromeArgs - }); - Browser.Disconnected += (sender, e) => + + int attempt = 0; + while (attempt < maxRetries) { - Browser = null; - _testOutput.WriteLine("Browser has been disconnected"); - }; - return Browser; + try + { + Browser = await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { + ExecutablePath = s_chromePath.Value, + Headless = headless, + Args = chromeArgs, + Timeout = timeout + }); + Browser.Disconnected += (sender, e) => + { + Browser = null; + _testOutput.WriteLine("Browser has been disconnected"); + }; + break; + } + catch (System.TimeoutException ex) + { + attempt++; + _testOutput.WriteLine($"Attempt {attempt} failed with TimeoutException: {ex.Message}"); + } + } + if (attempt == maxRetries) + throw new Exception($"Failed to launch browser after {maxRetries} attempts"); + return Browser!; } // FIXME: options From 2d9203420a77b4d8969a808e2537bf70f350aa8d Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 8 Jul 2024 14:03:36 +0200 Subject: [PATCH 52/72] Reduce allocations in Quic/HTTP3 (#104394) * Reduce allocations in Quic/HTTP3 * Also wait for WritesClosed when there's no content --- .../SocketsHttpHandler/Http3RequestStream.cs | 44 ++++++++++--------- .../Internal/ResettableValueTaskSource.cs | 11 +++-- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 11c74bd1571f7c..9382047b6fb450 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -172,15 +172,9 @@ public async Task SendAsync(CancellationToken cancellationT await FlushSendBufferAsync(endStream: _request.Content == null, _requestBodyCancellationSource.Token).ConfigureAwait(false); } - Task sendRequestTask; - if (_request.Content != null) - { - sendRequestTask = SendContentAsync(_request.Content!, _requestBodyCancellationSource.Token); - } - else - { - sendRequestTask = Task.CompletedTask; - } + Task sendRequestTask = _request.Content != null + ? SendContentAsync(_request.Content, _requestBodyCancellationSource.Token) + : Task.CompletedTask; // In parallel, send content and read response. // Depending on Expect 100 Continue usage, one will depend on the other making progress. @@ -219,6 +213,23 @@ await Task.WhenAny(sendRequestTask, readResponseTask).ConfigureAwait(false) == s // Wait for the response headers to be read. await readResponseTask.ConfigureAwait(false); + // If we've sent a body, wait for the writes to be closed (most likely already done). + // If sendRequestTask hasn't completed yet, we're doing duplex content transfers and can't wait for writes to be closed yet. + if (sendRequestTask.IsCompletedSuccessfully && + _stream.WritesClosed is { IsCompletedSuccessfully: false } writesClosed) + { + try + { + await writesClosed.WaitAsync(_requestBodyCancellationSource.Token).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + // If the request got cancelled before WritesClosed completed, avoid leaking an unobserved task exception. + _connection.LogExceptions(writesClosed); + throw; + } + } + Debug.Assert(_response != null && _response.Content != null); // Set our content stream. var responseContent = (HttpConnectionResponseContent)_response.Content; @@ -460,7 +471,6 @@ private async Task SendContentAsync(HttpContent content, CancellationToken cance else { _stream.CompleteWrites(); - await _stream.WritesClosed.ConfigureAwait(false); } if (HttpTelemetry.Log.IsEnabled()) HttpTelemetry.Log.RequestContentStop(bytesWritten); @@ -523,17 +533,11 @@ private async ValueTask WriteRequestContentAsync(ReadOnlyMemory buffer, Ca } } - private async ValueTask FlushSendBufferAsync(bool endStream, CancellationToken cancellationToken) + private ValueTask FlushSendBufferAsync(bool endStream, CancellationToken cancellationToken) { - await _stream.WriteAsync(_sendBuffer.ActiveMemory, endStream, cancellationToken).ConfigureAwait(false); - _sendBuffer.Discard(_sendBuffer.ActiveLength); - - await _stream.FlushAsync(cancellationToken).ConfigureAwait(false); - - if (endStream) - { - await _stream.WritesClosed.ConfigureAwait(false); - } + ReadOnlyMemory toSend = _sendBuffer.ActiveMemory; + _sendBuffer.Discard(toSend.Length); + return _stream.WriteAsync(toSend, endStream, cancellationToken); } private async ValueTask DrainContentLength0Frames(CancellationToken cancellationToken) diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs index 8fb8d90a7b37c7..c53fb1b0741425 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs @@ -310,6 +310,13 @@ public Task GetTask(object? keepAlive) { if (_finalTaskSource is null) { + if (_isSignaled) + { + return _exception is null + ? Task.CompletedTask + : Task.FromException(_exception); + } + _finalTaskSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); if (!_isCompleted) { @@ -319,10 +326,6 @@ public Task GetTask(object? keepAlive) ((GCHandle)state!).Free(); }, handle, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } - if (_isSignaled) - { - TrySignal(out _); - } } return _finalTaskSource.Task; } From dbe63a7d908dac867c98f2d4811c59fd286b6d9a Mon Sep 17 00:00:00 2001 From: Omair Majid Date: Mon, 8 Jul 2024 08:11:51 -0400 Subject: [PATCH 53/72] Handle top-level UseSystemLibs argument from the VMR (#104440) This will be used by https://github.com/dotnet/sdk/pull/41984 It's another attempt at https://github.com/dotnet/runtime/pull/101797 --- eng/DotNetBuild.props | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index 9d68b8265b0c1f..3569781b9af498 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -85,6 +85,16 @@ $(InnerBuildArgs) /p:SourceBuiltAssetManifestsDir=$(SourceBuiltAssetManifestsDir) $(InnerBuildArgs) /p:SourceBuiltSymbolsDir=$(SourceBuiltSymbolsDir) $(InnerBuildArgs) /p:GitHubRepositoryName=$(GitHubRepositoryName) + + + +$(UseSystemLibs)+ + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_BROTLI=true + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_LIBUNWIND=true + + + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_RAPIDJSON=true + $(InnerBuildArgs) --cmakeargs -DCLR_CMAKE_USE_SYSTEM_ZLIB=true + From e0d8b0deeb243ac370a97f240ff462cf577e87ca Mon Sep 17 00:00:00 2001 From: Ahmet Ibrahim Aksoy Date: Mon, 8 Jul 2024 14:27:43 +0200 Subject: [PATCH 54/72] Refactor WinHttpHandler test to use LoopbackServer.CreateClientAndServer (#104400) --- .../FunctionalTests/WinHttpHandlerTest.cs | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs index 358344d6708a0c..3738cc4b655a82 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/WinHttpHandlerTest.cs @@ -100,36 +100,26 @@ public async Task SendAsync_SlowServerAndCancel_ThrowsTaskCanceledException() [Fact] public async Task SendAsync_SlowServerRespondsAfterDefaultReceiveTimeout_ThrowsHttpRequestException() { - var handler = new WinHttpHandler(); - using (var client = new HttpClient(handler)) + TaskCompletionSource tcs = new TaskCompletionSource(); + await LoopbackServer.CreateClientAndServerAsync(async uri => { - var triggerResponseWrite = new TaskCompletionSource(); - var triggerRequestWait = new TaskCompletionSource(); - - await LoopbackServer.CreateServerAsync(async (server, url) => + using var client = new HttpClient(new WinHttpHandler()); + HttpRequestException ex = await Assert.ThrowsAsync(() => client.GetAsync(uri)); + _output.WriteLine($"Exception: {ex}"); + Assert.IsType(ex.InnerException); + Assert.NotNull(ex.InnerException.InnerException); + Assert.Contains("The operation timed out", ex.InnerException.InnerException.Message); + tcs.TrySetResult(true); + }, + async server => + { + await server.AcceptConnectionAsync(async connection => { - Task serverTask = server.AcceptConnectionAsync(async connection => - { - await connection.SendResponseAsync($"HTTP/1.1 200 OK\r\nContent-Length: 16000\r\n\r\n"); - - triggerRequestWait.SetResult(true); - await triggerResponseWrite.Task; - }); - - HttpRequestException ex = await Assert.ThrowsAsync(async () => - { - Task t = client.GetAsync(url); - await triggerRequestWait.Task; - var _ = await t; - }); - _output.WriteLine($"ex: {ex}"); - Assert.IsType(ex.InnerException); - Assert.NotNull(ex.InnerException.InnerException); - Assert.Contains("The operation timed out", ex.InnerException.InnerException.Message); - - triggerResponseWrite.SetResult(true); + await connection.ReadRequestDataAsync(); + await connection.SendResponseAsync($"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R)}\r\nContent-Length: 1000\r\n\r\n"); + await tcs.Task; }); - } + }); } [Fact] From e3363265b20905a55f4c6a7560f70150692548f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Mon, 8 Jul 2024 09:09:23 -0400 Subject: [PATCH 55/72] [cdac] RuntimeTypeSystem contract; rename ContainsPointers -> ContainsGCPointers (#103444) * Implement GetThreadStoreData in cDAC * [dac] Return canonical MethodTable instead of EEClass Instead of storing the EEClass pointer in DacpMethodTableData, store the canonical method table instead. Correspondingly, update GetMethodTableForEEClass to expect a canonical method table pointer instead of an EEClass Also update cDAC to do likewise * document GetMethodTableData string baseSize adjustment * Apply suggestions from code review Co-Authored-By: Aaron Robinson * [vm] rename ContainsPointers flag to ContainsGCPointers also rename getter/setter methods in MethodTable * code style suggestions from code review * DAC: always set wNumVirtuals and wNumVtableSlots to 0 This information can be retreived from the MethodTable using normal lldb/windbg primitives and doesn't need to be part of the DAC API contract * Remove NumVirtuals and NumVtableSlots from RuntimeTypeSystem.md Co-authored-by: Jan Kotas * "untrusted" -> "non-validated" * pull test target helpers out goal is to be able to use this for testing contracts that depend on some data in the heap * Add one FreeObjectMethodTable unit test * validate that a mock system object is a valid method table * code review feedback and more tests: 1. rename AttrClass data descriptor field to CorTypeAttr 2. fixup HasComponentSize / RawGetComponentSize comments and code 3. update "system.object" mock methodtable with more field values 4. update "system.string" mock methodtable with more field values * Update src/coreclr/gc/env/gcenv.object.h Co-authored-by: Elinor Fung * Update src/native/managed/cdacreader/src/Contracts/Metadata_1.MethodTableFlags.cs Co-authored-by: Elinor Fung * move non-validated MethodTable handling to a separate class * clear up ComponentSize contract spec and impl * rename Metadata -> RuntimeTypeSystem * add validation failure test; change validation to throw InvalidOperationException * Update src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs Co-authored-by: Jan Kotas * Add a generic instance test * add array instance test --------- Co-authored-by: Elinor Fung Co-authored-by: Aaron Robinson Co-authored-by: Jan Kotas --- .../design/datacontracts/RuntimeTypeSystem.md | 224 +++++++++++ .../RuntimeHelpers.CoreCLR.cs | 4 +- src/coreclr/debug/daccess/dacimpl.h | 2 + src/coreclr/debug/daccess/request.cpp | 146 +++++-- .../debug/runtimeinfo/contractpointerdata.cpp | 2 +- src/coreclr/debug/runtimeinfo/contracts.jsonc | 4 +- .../debug/runtimeinfo/datadescriptor.cpp | 1 + .../debug/runtimeinfo/datadescriptor.h | 22 + src/coreclr/gc/env/gcenv.object.h | 10 +- src/coreclr/gc/gc.cpp | 24 +- src/coreclr/gc/gcdesc.h | 4 +- src/coreclr/gc/sample/GCSample.cpp | 2 +- src/coreclr/inc/dacprivate.h | 3 + src/coreclr/nativeaot/Runtime/GCHelpers.cpp | 4 +- .../nativeaot/Runtime/inc/MethodTable.h | 4 +- .../nativeaot/Runtime/profheapwalkhelper.cpp | 2 +- src/coreclr/vm/amd64/JitHelpers_Slow.asm | 2 +- src/coreclr/vm/amd64/asmconstants.h | 6 +- src/coreclr/vm/arm/asmconstants.h | 4 +- src/coreclr/vm/array.cpp | 8 +- src/coreclr/vm/cdacoffsets.h | 4 +- src/coreclr/vm/class.cpp | 2 +- src/coreclr/vm/class.h | 8 + src/coreclr/vm/classlayoutinfo.cpp | 4 +- src/coreclr/vm/comutilnative.cpp | 2 +- src/coreclr/vm/dllimport.cpp | 2 +- src/coreclr/vm/gchelpers.cpp | 10 +- src/coreclr/vm/generics.cpp | 2 +- src/coreclr/vm/i386/jitinterfacex86.cpp | 4 +- src/coreclr/vm/ilmarshalers.cpp | 8 +- src/coreclr/vm/interpreter.cpp | 8 +- src/coreclr/vm/jitinterface.cpp | 24 +- src/coreclr/vm/method.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 12 +- src/coreclr/vm/methodtable.h | 30 +- src/coreclr/vm/methodtablebuilder.cpp | 24 +- src/coreclr/vm/mlinfo.cpp | 2 +- src/coreclr/vm/object.cpp | 2 +- src/coreclr/vm/proftoeeinterfaceimpl.cpp | 4 +- src/coreclr/vm/reflectioninvocation.cpp | 2 +- src/coreclr/vm/siginfo.cpp | 4 +- src/coreclr/vm/stubgen.cpp | 2 +- src/coreclr/vm/tailcallhelp.cpp | 2 +- .../managed/cdacreader/src/Constants.cs | 2 + .../cdacreader/src/Contracts/Registry.cs | 1 + .../src/Contracts/RuntimeTypeSystem.cs | 67 ++++ .../RuntimeTypeSystem_1.MethodTableFlags.cs | 87 ++++ .../RuntimeTypeSystem_1.NonValidated.cs | 198 +++++++++ .../src/Contracts/RuntimeTypeSystem_1.cs | 157 ++++++++ .../managed/cdacreader/src/Data/EEClass.cs | 23 ++ .../cdacreader/src/Data/MethodTable.cs | 33 ++ .../src/Data/MethodTableAuxiliaryData.cs | 21 + src/native/managed/cdacreader/src/DataType.cs | 6 +- .../cdacreader/src/Legacy/ISOSDacInterface.cs | 21 +- .../cdacreader/src/Legacy/SOSDacImpl.cs | 67 +++- src/native/managed/cdacreader/src/Target.cs | 22 +- .../managed/cdacreader/src/cdacreader.csproj | 3 + .../cdacreader/tests/MethodTableTests.cs | 376 ++++++++++++++++++ .../cdacreader/tests/MockMemorySpace.cs | 257 ++++++++++++ .../managed/cdacreader/tests/MockTarget.cs | 41 ++ .../cdacreader/tests/TargetTestHelpers.cs | 251 ++++++++++++ .../managed/cdacreader/tests/TargetTests.cs | 274 ++----------- 62 files changed, 2155 insertions(+), 394 deletions(-) create mode 100644 docs/design/datacontracts/RuntimeTypeSystem.md create mode 100644 src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs create mode 100644 src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs create mode 100644 src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.NonValidated.cs create mode 100644 src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs create mode 100644 src/native/managed/cdacreader/src/Data/EEClass.cs create mode 100644 src/native/managed/cdacreader/src/Data/MethodTable.cs create mode 100644 src/native/managed/cdacreader/src/Data/MethodTableAuxiliaryData.cs create mode 100644 src/native/managed/cdacreader/tests/MethodTableTests.cs create mode 100644 src/native/managed/cdacreader/tests/MockMemorySpace.cs create mode 100644 src/native/managed/cdacreader/tests/MockTarget.cs create mode 100644 src/native/managed/cdacreader/tests/TargetTestHelpers.cs diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md new file mode 100644 index 00000000000000..f8382ee4fdc599 --- /dev/null +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -0,0 +1,224 @@ +# Contract RuntimeTypeSystem + +This contract is for exploring the properties of the runtime types of values on the managed heap or on the stack in a .NET process. + +## APIs of contract + +A `MethodTable` is the runtime representation of the type information about a value. Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `MethodTableHandle` for querying the `MethodTable`. + +``` csharp +struct MethodTableHandle +{ + // no public properties or constructors + + internal TargetPointer Address { get; } +} +``` + +``` csharp + #region MethodTable inspection APIs + public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer); + + public virtual TargetPointer GetModule(MethodTableHandle methodTable); + // A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the + // MethodTable of the prototypical instance. + public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable); + public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable); + + public virtual uint GetBaseSize(MethodTableHandle methodTable); + // The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes) + public virtual uint GetComponentSize(MethodTableHandle methodTable); + + // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap + public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable); + public virtual bool IsString(MethodTableHandle methodTable); + // True if the MethodTable represents a type that contains managed references + public virtual bool ContainsGCPointers(MethodTableHandle methodTable); + public virtual bool IsDynamicStatics(MethodTableHandle methodTable); + public virtual ushort GetNumMethods(MethodTableHandle methodTable); + public virtual ushort GetNumInterfaces(MethodTableHandle methodTable); + + // Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation + public virtual uint GetTypeDefToken(MethodTableHandle methodTable); + // Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type, + // or for its generic type definition if it is a generic instantiation + public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable); + #endregion MethodTable inspection APIs +``` + +## Version 1 + +The `MethodTable` inspection APIs are implemented in terms of the following flags on the runtime `MethodTable` structure: + +``` csharp +internal partial struct RuntimeTypeSystem_1 +{ + // The lower 16-bits of the MTFlags field are used for these flags, + // if WFLAGS_HIGH.HasComponentSize is unset + [Flags] + internal enum WFLAGS_LOW : uint + { + GenericsMask = 0x00000030, + GenericsMask_NonGeneric = 0x00000000, // no instantiation + + StringArrayValues = GenericsMask_NonGeneric, + } + + // Upper bits of MTFlags + [Flags] + internal enum WFLAGS_HIGH : uint + { + Category_Mask = 0x000F0000, + Category_Array = 0x00080000, + Category_Array_Mask = 0x000C0000, + Category_Interface = 0x000C0000, + ContainsGCPointers = 0x01000000, + HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size, + // otherwise the lower bits are used for WFLAGS_LOW + } + + [Flags] + internal enum WFLAGS2_ENUM : uint + { + DynamicStatics = 0x0002, + } + + // Encapsulates the MethodTable flags v1 uses + internal struct MethodTableFlags + { + public uint MTFlags { get; } + public uint MTFlags2 { get; } + public uint BaseSize { get; } + + public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) { ... /* mask & lower 16 bits of MTFlags */ } + public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) { ... /* mask & upper 16 bits of MTFlags */ } + + public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) { ... /* mask & MTFlags2*/ } + + private bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag) + { + if (IsStringOrArray) + { + return (WFLAGS_LOW.StringArrayValues & mask) == flag; + } + else + { + return (FlagsLow & mask) == flag; + } + } + + public ushort ComponentSizeBits => (ushort)(MTFlags & 0x0000ffff); // only meaningful if HasComponentSize is set + + public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0; + public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface; + public bool IsString => HasComponentSize && !IsArray && ComponentSizeBits == 2; + public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array; + public bool IsStringOrArray => HasComponentSize; + public ushort ComponentSize => HasComponentSize ? ComponentSizeBits : (ushort)0; + public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric); + public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0; + public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0; + } + + [Flags] + internal enum EEClassOrCanonMTBits + { + EEClass = 0, + CanonMT = 1, + Mask = 1, + } +} +``` + +Internally the contract has a `MethodTable_1` struct that depends on the `MethodTable` data descriptor + +```csharp +internal struct MethodTable_1 +{ + internal RuntimeTypeSystem_1.MethodTableFlags Flags { get; } + internal ushort NumInterfaces { get; } + internal ushort NumVirtuals { get; } + internal TargetPointer ParentMethodTable { get; } + internal TargetPointer Module { get; } + internal TargetPointer EEClassOrCanonMT { get; } + internal MethodTable_1(Data.MethodTable data) + { + Flags = new RuntimeTypeSystem_1.MethodTableFlags + { + MTFlags = data.MTFlags, + MTFlags2 = data.MTFlags2, + BaseSize = data.BaseSize, + }; + NumInterfaces = data.NumInterfaces; + NumVirtuals = data.NumVirtuals; + EEClassOrCanonMT = data.EEClassOrCanonMT; + Module = data.Module; + ParentMethodTable = data.ParentMethodTable; + } +} +``` + +The contract depends on the global pointer value `FreeObjectMethodTablePointer`. +The contract additionally depends on the `EEClass` data descriptor. + +```csharp + private readonly Dictionary _methodTables; + + internal TargetPointer FreeObjectMethodTablePointer {get; } + + public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer) + { + ... // validate that methodTablePointer points to something that looks like a MethodTable. + ... // read Data.MethodTable from methodTablePointer. + ... // create a MethodTable_1 and add it to _methodTables. + return MethodTableHandle { Address = methodTablePointer } + } + + internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr) + { + return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask); + } + + public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize; + + public uint GetComponentSize(MethodTableHandle methodTableHandle) => GetComponentSize(_methodTables[methodTableHandle.Address]); + + private TargetPointer GetClassPointer(MethodTableHandle methodTableHandle) + { + ... // if the MethodTable stores a pointer to the EEClass, return it + // otherwise the MethodTable stores a pointer to the canonical MethodTable + // in that case, return the canonical MethodTable's EEClass. + // Canonical MethodTables always store an EEClass pointer. + } + + private Data.EEClass GetClassData(MethodTableHandle methodTableHandle) + { + TargetPointer eeClassPtr = GetClassPointer(methodTableHandle); + ... // read Data.EEClass data from eeClassPtr + } + + + public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable; + + public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module; + public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable; + + public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address; + + public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString; + public bool ContainsGCPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsGCPointers; + + public uint GetTypeDefToken(MethodTableHandle methodTableHandle) + { + MethodTable_1 methodTable = _methodTables[methodTableHandle.Address]; + return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24)); + } + + public ushort GetNumMethods(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).NumMethods; + + public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces; + + public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).CorTypeAttr; + + public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsDynamicStatics; +``` diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 77e579ce4680bd..a0af2fedefac53 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -654,7 +654,7 @@ internal unsafe struct MethodTable private const uint enum_flag_IsByRefLike = 0x00001000; // WFLAGS_HIGH_ENUM - private const uint enum_flag_ContainsPointers = 0x01000000; + private const uint enum_flag_ContainsGCPointers = 0x01000000; private const uint enum_flag_ContainsGenericVariables = 0x20000000; private const uint enum_flag_HasComponentSize = 0x80000000; private const uint enum_flag_HasTypeEquivalence = 0x02000000; @@ -707,7 +707,7 @@ internal unsafe struct MethodTable public bool HasComponentSize => (Flags & enum_flag_HasComponentSize) != 0; - public bool ContainsGCPointers => (Flags & enum_flag_ContainsPointers) != 0; + public bool ContainsGCPointers => (Flags & enum_flag_ContainsGCPointers) != 0; public bool NonTrivialInterfaceCast => (Flags & enum_flag_NonTrivialInterfaceCast) != 0; diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index f480eea6baaa56..57be1538e2bb7a 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1232,6 +1232,8 @@ class ClrDataAccess HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData); HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data); HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException); + HRESULT GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data); + HRESULT GetMethodTableForEEClassImpl (CLRDATA_ADDRESS eeClassReallyMT, CLRDATA_ADDRESS *value); BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord); #ifndef TARGET_UNIX diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 445acc64b10191..1021fc3db128fa 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -161,11 +161,6 @@ BOOL DacValidateMethodTable(PTR_MethodTable pMT, BOOL &bIsFree) // In rare cases, we've seen the standard check above pass when it shouldn't. // Insert additional/ad-hoc tests below. - // Metadata token should look valid for a class - mdTypeDef td = pMT->GetCl(); - if (td != mdTokenNil && TypeFromToken(td) != mdtTypeDef) - goto BadMethodTable; - // BaseSize should always be greater than 0 for valid objects (unless it's an interface) // For strings, baseSize is not ptr-aligned if (!pMT->IsInterface() && !pMT->IsString()) @@ -1785,42 +1780,87 @@ ClrDataAccess::GetMethodTableData(CLRDATA_ADDRESS mt, struct DacpMethodTableData return E_INVALIDARG; SOSDacEnter(); - - PTR_MethodTable pMT = PTR_MethodTable(TO_TADDR(mt)); - BOOL bIsFree = FALSE; - if (!DacValidateMethodTable(pMT, bIsFree)) + if (m_cdacSos != NULL) { - hr = E_INVALIDARG; + // Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC. + hr = m_cdacSos->GetMethodTableData(mt, MTData); + if (FAILED(hr)) + { + hr = GetMethodTableDataImpl(mt, MTData); + } +#ifdef _DEBUG + else + { + // Assert that the data is the same as what we get from the DAC. + DacpMethodTableData mtDataLocal; + HRESULT hrLocal = GetMethodTableDataImpl(mt, &mtDataLocal); + _ASSERTE(hr == hrLocal); + _ASSERTE(MTData->BaseSize == mtDataLocal.BaseSize); + _ASSERTE(MTData->ComponentSize == mtDataLocal.ComponentSize); + _ASSERTE(MTData->bIsFree == mtDataLocal.bIsFree); + _ASSERTE(MTData->Module == mtDataLocal.Module); + _ASSERTE(MTData->Class == mtDataLocal.Class); + _ASSERTE(MTData->ParentMethodTable == mtDataLocal.ParentMethodTable); + _ASSERTE(MTData->wNumInterfaces == mtDataLocal.wNumInterfaces); + _ASSERTE(MTData->wNumMethods == mtDataLocal.wNumMethods); + _ASSERTE(MTData->wNumVtableSlots == mtDataLocal.wNumVtableSlots); + _ASSERTE(MTData->wNumVirtuals == mtDataLocal.wNumVirtuals); + _ASSERTE(MTData->cl == mtDataLocal.cl); + _ASSERTE(MTData->dwAttrClass = mtDataLocal.dwAttrClass); + _ASSERTE(MTData->bContainsPointers == mtDataLocal.bContainsPointers); + _ASSERTE(MTData->bIsShared == mtDataLocal.bIsShared); + _ASSERTE(MTData->bIsDynamic == mtDataLocal.bIsDynamic); + } +#endif } else { - ZeroMemory(MTData,sizeof(DacpMethodTableData)); - MTData->BaseSize = pMT->GetBaseSize(); - if(pMT->IsString()) - MTData->BaseSize -= sizeof(WCHAR); - MTData->ComponentSize = (DWORD)pMT->GetComponentSize(); - MTData->bIsFree = bIsFree; - if(!bIsFree) - { - MTData->Module = HOST_CDADDR(pMT->GetModule()); - MTData->Class = HOST_CDADDR(pMT->GetClass()); - MTData->ParentMethodTable = HOST_CDADDR(pMT->GetParentMethodTable());; - MTData->wNumInterfaces = (WORD)pMT->GetNumInterfaces(); - MTData->wNumMethods = pMT->GetNumMethods(); - MTData->wNumVtableSlots = pMT->GetNumVtableSlots(); - MTData->wNumVirtuals = pMT->GetNumVirtuals(); - MTData->cl = pMT->GetCl(); - MTData->dwAttrClass = pMT->GetAttrClass(); - MTData->bContainsPointers = pMT->ContainsPointers(); - MTData->bIsShared = FALSE; - MTData->bIsDynamic = pMT->IsDynamicStatics(); - } + hr = GetMethodTableDataImpl (mt, MTData); } - SOSDacLeave(); return hr; } +HRESULT +ClrDataAccess::GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *MTData) +{ + PTR_MethodTable pMT = PTR_MethodTable(TO_TADDR(mt)); + BOOL bIsFree = FALSE; + if (!DacValidateMethodTable(pMT, bIsFree)) + { + return E_INVALIDARG; + } + + ZeroMemory(MTData,sizeof(DacpMethodTableData)); + MTData->BaseSize = pMT->GetBaseSize(); + // [compat] SOS DAC APIs added this base size adjustment for strings + // due to: "2008/09/25 Title: New implementation of StringBuilder and improvements in String class" + // which changed StringBuilder not to use a String as an internal buffer and in the process + // changed the String internals so that StringObject::GetBaseSize() now includes the nul terminator character, + // which is apparently not expected by SOS. + if(pMT->IsString()) + MTData->BaseSize -= sizeof(WCHAR); + MTData->ComponentSize = (DWORD)pMT->GetComponentSize(); + MTData->bIsFree = bIsFree; + if(!bIsFree) + { + MTData->Module = HOST_CDADDR(pMT->GetModule()); + // Note: DacpMethodTableData::Class is really a pointer to the canonical method table + MTData->Class = HOST_CDADDR(pMT->GetClass()->GetMethodTable()); + MTData->ParentMethodTable = HOST_CDADDR(pMT->GetParentMethodTable());; + MTData->wNumInterfaces = (WORD)pMT->GetNumInterfaces(); + MTData->wNumMethods = pMT->GetNumMethods(); // printed as "number of vtable slots" and used to iterate over method slots + MTData->wNumVtableSlots = 0; // always return 0 since .NET 9 + MTData->wNumVirtuals = 0; // always return 0 since .NET 9 + MTData->cl = pMT->GetCl(); + MTData->dwAttrClass = pMT->GetAttrClass(); + MTData->bContainsPointers = pMT->ContainsGCPointers(); + MTData->bIsShared = FALSE; + MTData->bIsDynamic = pMT->IsDynamicStatics(); + } + return S_OK; +} + HRESULT ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, _Inout_updates_z_(count) WCHAR *mtName, unsigned int *pNeeded) { @@ -2063,27 +2103,53 @@ ClrDataAccess::GetMethodTableTransparencyData(CLRDATA_ADDRESS mt, struct DacpMet } HRESULT -ClrDataAccess::GetMethodTableForEEClass(CLRDATA_ADDRESS eeClass, CLRDATA_ADDRESS *value) +ClrDataAccess::GetMethodTableForEEClass(CLRDATA_ADDRESS eeClassReallyCanonMT, CLRDATA_ADDRESS *value) { - if (eeClass == 0 || value == NULL) + if (eeClassReallyCanonMT == 0 || value == NULL) return E_INVALIDARG; SOSDacEnter(); - - PTR_EEClass pClass = PTR_EEClass(TO_TADDR(eeClass)); - if (!DacValidateEEClass(pClass)) + if (m_cdacSos != NULL) { - hr = E_INVALIDARG; + // Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC. + hr = m_cdacSos->GetMethodTableForEEClass(eeClassReallyCanonMT, value); + if (FAILED(hr)) + { + hr = GetMethodTableForEEClassImpl(eeClassReallyCanonMT, value); + } +#ifdef _DEBUG + else + { + // Assert that the data is the same as what we get from the DAC. + CLRDATA_ADDRESS valueLocal; + HRESULT hrLocal = GetMethodTableForEEClassImpl(eeClassReallyCanonMT, &valueLocal); + _ASSERTE(hr == hrLocal); + _ASSERTE(*value == valueLocal); + } +#endif } else { - *value = HOST_CDADDR(pClass->GetMethodTable()); + hr = GetMethodTableForEEClassImpl (eeClassReallyCanonMT, value); } - SOSDacLeave(); return hr; } +HRESULT +ClrDataAccess::GetMethodTableForEEClassImpl(CLRDATA_ADDRESS eeClassReallyCanonMT, CLRDATA_ADDRESS *value) +{ + PTR_MethodTable pCanonMT = PTR_MethodTable(TO_TADDR(eeClassReallyCanonMT)); + BOOL bIsFree; + if (!DacValidateMethodTable(pCanonMT, bIsFree)) + { + return E_INVALIDARG; + } + + *value = HOST_CDADDR(pCanonMT); + return S_OK; +} + HRESULT ClrDataAccess::GetFrameName(CLRDATA_ADDRESS vtable, unsigned int count, _Inout_updates_z_(count) WCHAR *frameName, unsigned int *pNeeded) { diff --git a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp index ae1440af4219a2..cf7d914e728d78 100644 --- a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp +++ b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp @@ -7,10 +7,10 @@ #include #include "threads.h" +#include "vars.hpp" extern "C" { - // without an extern declaration, clang does not emit this global into the object file extern const uintptr_t contractDescriptorPointerData[]; diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc index 0c94cf58c23222..44e7f914f9b075 100644 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -10,6 +10,6 @@ // so to conditionally include contracts, put additional contracts in a separate file { "Exception": 1, - "Thread": 1, - "SOSBreakingChangeVersion": 1 // example contract: "runtime exports an SOS breaking change version global" + "RuntimeTypeSystem": 1, + "Thread": 1 } diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp index 99fe1cca7eeca7..bea29213783ebc 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp @@ -9,6 +9,7 @@ #include "static_assert.h" #include +#include "methodtable.h" #include "threads.h" // begin blob definition diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 0c7e3f70b4d333..0e0aa0c8a819d2 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -156,6 +156,27 @@ CDAC_TYPE_BEGIN(GCHandle) CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE)) CDAC_TYPE_END(GCHandle) +// Metadata + +CDAC_TYPE_BEGIN(MethodTable) +CDAC_TYPE_INDETERMINATE(MethodTable) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags, cdac_offsets::MTFlags) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, BaseSize, cdac_offsets::BaseSize) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags2, cdac_offsets::MTFlags2) +CDAC_TYPE_FIELD(MethodTable, /*nuint*/, EEClassOrCanonMT, cdac_offsets::EEClassOrCanonMT) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, Module, cdac_offsets::Module) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, ParentMethodTable, cdac_offsets::ParentMethodTable) +CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumInterfaces, cdac_offsets::NumInterfaces) +CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumVirtuals, cdac_offsets::NumVirtuals) +CDAC_TYPE_END(MethodTable) + +CDAC_TYPE_BEGIN(EEClass) +CDAC_TYPE_INDETERMINATE(EEClass) +CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_offsets::MethodTable) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_offsets::NumMethods) +CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_offsets::CorTypeAttr) +CDAC_TYPE_END(EEClass) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -169,6 +190,7 @@ CDAC_GLOBAL(FeatureEHFunclets, uint8, 1) CDAC_GLOBAL(FeatureEHFunclets, uint8, 0) #endif CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) +CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable) CDAC_GLOBALS_END() #undef CDAC_BASELINE diff --git a/src/coreclr/gc/env/gcenv.object.h b/src/coreclr/gc/env/gcenv.object.h index ff0dbb343ed1dd..f515536f6a66f8 100644 --- a/src/coreclr/gc/env/gcenv.object.h +++ b/src/coreclr/gc/env/gcenv.object.h @@ -49,7 +49,7 @@ static_assert(sizeof(ObjHeader) == sizeof(uintptr_t), "this assumption is made b #define MTFlag_RequiresAlign8 0x00001000 // enum_flag_RequiresAlign8 #define MTFlag_Category_ValueType 0x00040000 // enum_flag_Category_ValueType #define MTFlag_Category_ValueType_Mask 0x000C0000 // enum_flag_Category_ValueType_Mask -#define MTFlag_ContainsPointers 0x01000000 // enum_flag_ContainsPointers +#define MTFlag_ContainsGCPointers 0x01000000 // enum_flag_ContainsGCPointers #define MTFlag_HasCriticalFinalizer 0x00000002 // enum_flag_HasCriticalFinalizer #define MTFlag_HasFinalizer 0x00100000 // enum_flag_HasFinalizer #define MTFlag_IsArray 0x00080000 // enum_flag_Category_Array @@ -100,14 +100,14 @@ class MethodTable return (m_flags & MTFlag_Collectible) != 0; } - bool ContainsPointers() + bool ContainsGCPointers() { - return (m_flags & MTFlag_ContainsPointers) != 0; + return (m_flags & MTFlag_ContainsGCPointers) != 0; } - bool ContainsPointersOrCollectible() + bool ContainsGCPointersOrCollectible() { - return ContainsPointers() || Collectible(); + return ContainsGCPointers() || Collectible(); } bool RequiresAlign8() diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index c6cb0082285845..c6de4e2584355c 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -4829,7 +4829,7 @@ class CObjectHeader : public Object CGCDesc *GetSlotMap () { - assert (GetMethodTable()->ContainsPointers()); + assert (GetMethodTable()->ContainsGCPointers()); return CGCDesc::GetCGCDescFromMT(GetMethodTable()); } @@ -4893,9 +4893,9 @@ class CObjectHeader : public Object } #endif // FEATURE_STRUCTALIGN - BOOL ContainsPointers() const + BOOL ContainsGCPointers() const { - return GetMethodTable()->ContainsPointers(); + return GetMethodTable()->ContainsGCPointers(); } #ifdef COLLECTIBLE_CLASS @@ -4904,10 +4904,10 @@ class CObjectHeader : public Object return GetMethodTable()->Collectible(); } - FORCEINLINE BOOL ContainsPointersOrCollectible() const + FORCEINLINE BOOL ContainsGCPointersOrCollectible() const { MethodTable *pMethodTable = GetMethodTable(); - return (pMethodTable->ContainsPointers() || pMethodTable->Collectible()); + return (pMethodTable->ContainsGCPointers() || pMethodTable->Collectible()); } #endif //COLLECTIBLE_CLASS @@ -6066,7 +6066,7 @@ void gc_heap::release_segment (heap_segment* sg) FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg)); size_t reserved_size = (uint8_t*)heap_segment_reserved (sg) - (uint8_t*)sg; reduce_committed_bytes ( - sg, + sg, ((uint8_t*)heap_segment_committed (sg) - (uint8_t*)sg), (int) heap_segment_oh (sg) #ifdef MULTIPLE_HEAPS @@ -9083,7 +9083,7 @@ void destroy_card_table (uint32_t* c_table) void gc_heap::destroy_card_table_helper (uint32_t* c_table) { uint8_t* lowest = card_table_lowest_address (c_table); - uint8_t* highest = card_table_highest_address (c_table); + uint8_t* highest = card_table_highest_address (c_table); get_card_table_element_layout(lowest, highest, card_table_element_layout); size_t result = card_table_element_layout[seg_mapping_table_element + 1]; gc_heap::reduce_committed_bytes (&card_table_refcount(c_table), result, recorded_committed_bookkeeping_bucket, -1, true); @@ -11555,14 +11555,14 @@ inline size_t my_get_size (Object* ob) #define size(i) my_get_size (header(i)) -#define contain_pointers(i) header(i)->ContainsPointers() +#define contain_pointers(i) header(i)->ContainsGCPointers() #ifdef COLLECTIBLE_CLASS -#define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible() +#define contain_pointers_or_collectible(i) header(i)->ContainsGCPointersOrCollectible() #define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i) #define is_collectible(i) method_table(i)->Collectible() #else //COLLECTIBLE_CLASS -#define contain_pointers_or_collectible(i) header(i)->ContainsPointers() +#define contain_pointers_or_collectible(i) header(i)->ContainsGCPointers() #endif //COLLECTIBLE_CLASS #ifdef BACKGROUND_GC @@ -26683,7 +26683,7 @@ BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high) #ifndef COLLECTIBLE_CLASS #define go_through_object_cl(mt,o,size,parm,exp) \ { \ - if (header(o)->ContainsPointers()) \ + if (header(o)->ContainsGCPointers()) \ { \ go_through_object_nostart(mt,o,size,parm,exp); \ } \ @@ -26697,7 +26697,7 @@ BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high) uint8_t** parm = &class_obj; \ do {exp} while (false); \ } \ - if (header(o)->ContainsPointers()) \ + if (header(o)->ContainsGCPointers()) \ { \ go_through_object_nostart(mt,o,size,parm,exp); \ } \ diff --git a/src/coreclr/gc/gcdesc.h b/src/coreclr/gc/gcdesc.h index 54a13dfdb8cdff..8d91e776ac4284 100644 --- a/src/coreclr/gc/gcdesc.h +++ b/src/coreclr/gc/gcdesc.h @@ -161,7 +161,7 @@ class CGCDesc // If it doesn't contain pointers, there isn't a GCDesc PTR_MethodTable mt(pMT); - _ASSERTE(mt->ContainsPointers()); + _ASSERTE(mt->ContainsGCPointers()); return PTR_CGCDesc(mt); } @@ -195,7 +195,7 @@ class CGCDesc { size_t NumOfPointers = 0; - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { CGCDesc* map = GetCGCDescFromMT(pMT); CGCDescSeries* cur = map->GetHighestSeries(); diff --git a/src/coreclr/gc/sample/GCSample.cpp b/src/coreclr/gc/sample/GCSample.cpp index 0f2afc7c20a717..3b9bf63103dc4f 100644 --- a/src/coreclr/gc/sample/GCSample.cpp +++ b/src/coreclr/gc/sample/GCSample.cpp @@ -179,7 +179,7 @@ int __cdecl main(int argc, char* argv[]) My_MethodTable.m_MT.m_baseSize = max(baseSize, (uint32_t)MIN_OBJECT_SIZE); My_MethodTable.m_MT.m_componentSize = 0; // Array component size - My_MethodTable.m_MT.m_flags = MTFlag_ContainsPointers; + My_MethodTable.m_MT.m_flags = MTFlag_ContainsGCPointers; My_MethodTable.m_numSeries = 2; diff --git a/src/coreclr/inc/dacprivate.h b/src/coreclr/inc/dacprivate.h index 3e96334430da5c..305029634406c9 100644 --- a/src/coreclr/inc/dacprivate.h +++ b/src/coreclr/inc/dacprivate.h @@ -274,11 +274,14 @@ struct MSLAYOUT DacpMethodTableData { BOOL bIsFree = FALSE; // everything else is NULL if this is true. CLRDATA_ADDRESS Module = 0; + // Note: DacpMethodTableData::Class is really a pointer to the canonical method table CLRDATA_ADDRESS Class = 0; CLRDATA_ADDRESS ParentMethodTable = 0; WORD wNumInterfaces = 0; WORD wNumMethods = 0; + // Note: Always 0, since .NET 9 WORD wNumVtableSlots = 0; + // Note: Always 0, since .NET 9 WORD wNumVirtuals = 0; DWORD BaseSize = 0; DWORD ComponentSize = 0; diff --git a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp index 6fecd5ac04768e..b038d9d33541bd 100644 --- a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp @@ -476,7 +476,7 @@ static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t ASSERT(!pThread->IsDoNotTriggerGcSet()); ASSERT(pThread->IsCurrentThreadInCooperativeMode()); - if (pEEType->ContainsPointers()) + if (pEEType->ContainsGCPointers()) { uFlags |= GC_ALLOC_CONTAINS_REF; uFlags &= ~GC_ALLOC_ZEROING_OPTIONAL; @@ -693,7 +693,7 @@ EXTERN_C void QCALLTYPE RhUnregisterFrozenSegment(void* pSegmentHandle) FCIMPL1(uint32_t, RhGetGCDescSize, MethodTable* pMT) { - if (!pMT->ContainsPointersOrCollectible()) + if (!pMT->ContainsGCPointersOrCollectible()) return 0; return (uint32_t)CGCDesc::GetCGCDescFromMT(pMT)->GetSize(); diff --git a/src/coreclr/nativeaot/Runtime/inc/MethodTable.h b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h index b5c41b14d92bf5..f33a5d066dc3ec 100644 --- a/src/coreclr/nativeaot/Runtime/inc/MethodTable.h +++ b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h @@ -289,8 +289,8 @@ class MethodTable public: // Methods expected by the GC - uint32_t ContainsPointers() { return HasReferenceFields(); } - uint32_t ContainsPointersOrCollectible() { return HasReferenceFields(); } + uint32_t ContainsGCPointers() { return HasReferenceFields(); } + uint32_t ContainsGCPointersOrCollectible() { return HasReferenceFields(); } UInt32_BOOL SanityCheck() { return Validate(); } }; diff --git a/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp index 3cecf152f3a855..6d9d7edc6fea46 100644 --- a/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp +++ b/src/coreclr/nativeaot/Runtime/profheapwalkhelper.cpp @@ -138,7 +138,7 @@ bool HeapWalkHelper(Object * pBO, void * pvContext) ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext; - //if (pMT->ContainsPointersOrCollectible()) + //if (pMT->ContainsGCPointersOrCollectible()) { // First round through calculates the number of object refs for this class GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); diff --git a/src/coreclr/vm/amd64/JitHelpers_Slow.asm b/src/coreclr/vm/amd64/JitHelpers_Slow.asm index e2f58ac6618db4..6d322248cdeeec 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Slow.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Slow.asm @@ -224,7 +224,7 @@ NESTED_ENTRY JIT_BoxFastUP, _TEXT mov [g_global_alloc_lock], -1 ; Check whether the object contains pointers - test dword ptr [rcx + OFFSETOF__MethodTable__m_dwFlags], MethodTable__enum_flag_ContainsPointers + test dword ptr [rcx + OFFSETOF__MethodTable__m_dwFlags], MethodTable__enum_flag_ContainsGCPointers jnz ContainsPointers ; We have no pointers - emit a simple inline copy loop diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index c629192da5cb9b..524e1fd40b7ae8 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -176,9 +176,9 @@ ASMCONSTANTS_C_ASSERT(METHODTABLE_EQUIVALENCE_FLAGS #define METHODTABLE_EQUIVALENCE_FLAGS 0x0 #endif -#define MethodTable__enum_flag_ContainsPointers 0x01000000 -ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers - == MethodTable::enum_flag_ContainsPointers); +#define MethodTable__enum_flag_ContainsGCPointers 0x01000000 +ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsGCPointers + == MethodTable::enum_flag_ContainsGCPointers); #define OFFSETOF__InterfaceInfo_t__m_pMethodTable 0 ASMCONSTANTS_C_ASSERT(OFFSETOF__InterfaceInfo_t__m_pMethodTable diff --git a/src/coreclr/vm/arm/asmconstants.h b/src/coreclr/vm/arm/asmconstants.h index 9995068d852792..1a65e1e45351da 100644 --- a/src/coreclr/vm/arm/asmconstants.h +++ b/src/coreclr/vm/arm/asmconstants.h @@ -76,8 +76,8 @@ ASMCONSTANTS_C_ASSERT(MethodTable__m_BaseSize == offsetof(MethodTable, m_BaseSiz #define MethodTable__m_dwFlags 0x0 ASMCONSTANTS_C_ASSERT(MethodTable__m_dwFlags == offsetof(MethodTable, m_dwFlags)); -#define MethodTable__enum_flag_ContainsPointers 0x01000000 -ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsPointers == MethodTable::enum_flag_ContainsPointers); +#define MethodTable__enum_flag_ContainsGCPointers 0x01000000 +ASMCONSTANTS_C_ASSERT(MethodTable__enum_flag_ContainsGCPointers == MethodTable::enum_flag_ContainsGCPointers); #define MethodTable__m_ElementType DBG_FRE(0x24, 0x20) ASMCONSTANTS_C_ASSERT(MethodTable__m_ElementType == offsetof(MethodTable, m_ElementTypeHnd)); diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp index 3b2b778f4c50f0..546b2292b35270 100644 --- a/src/coreclr/vm/array.cpp +++ b/src/coreclr/vm/array.cpp @@ -238,7 +238,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy } BOOL containsPointers = CorTypeInfo::IsObjRef(elemType); - if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsPointers()) + if (elemType == ELEMENT_TYPE_VALUETYPE && pElemMT->ContainsGCPointers()) containsPointers = TRUE; // this is the base for every array type @@ -520,7 +520,7 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy if (CorTypeInfo::IsObjRef(elemType) || ((elemType == ELEMENT_TYPE_VALUETYPE) && pElemMT->IsAllGCPointers())) { - pMT->SetContainsPointers(); + pMT->SetContainsGCPointers(); // This array is all GC Pointers CGCDesc::GetCGCDescFromMT(pMT)->Init( pMT, 1 ); @@ -536,9 +536,9 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy else if (elemType == ELEMENT_TYPE_VALUETYPE) { // If it's an array of value classes, there is a different format for the GCDesc if it contains pointers - if (pElemMT->ContainsPointers()) + if (pElemMT->ContainsGCPointers()) { - pMT->SetContainsPointers(); + pMT->SetContainsGCPointers(); CGCDescSeries* pElemSeries = CGCDesc::GetCGCDescFromMT(pElemMT)->GetHighestSeries(); diff --git a/src/coreclr/vm/cdacoffsets.h b/src/coreclr/vm/cdacoffsets.h index 317ef41f736037..38c5b316c4244f 100644 --- a/src/coreclr/vm/cdacoffsets.h +++ b/src/coreclr/vm/cdacoffsets.h @@ -8,8 +8,8 @@ // // If the offset of some field F in class C must be provided to cDAC, but the field is private, the // class C should declare cdac_offsets as a friend: -// -// friend template struct cdac_offsets; +// +// template friend struct ::cdac_offsets; // // and provide a specialization cdac_offsets with a constexpr size_t member providing the offset: // diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 6c0052636f6ae1..9d85bc141e115a 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -2690,7 +2690,7 @@ MethodTable::DebugDumpGCDesc( LOG((LF_ALWAYS, LL_ALWAYS, "GC description for '%s':\n\n", pszClassName)); } - if (ContainsPointers()) + if (ContainsGCPointers()) { CGCDescSeries *pSeries; CGCDescSeries *pHighest; diff --git a/src/coreclr/vm/class.h b/src/coreclr/vm/class.h index e20b29b9e5f122..74c66714555f36 100644 --- a/src/coreclr/vm/class.h +++ b/src/coreclr/vm/class.h @@ -1798,6 +1798,14 @@ class EEClass // DO NOT CREATE A NEW EEClass USING NEW! } #endif // !DACCESS_COMPILE + template friend struct ::cdac_offsets; +}; + +template<> struct cdac_offsets +{ + static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable); + static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods); + static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass); }; // -------------------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/classlayoutinfo.cpp b/src/coreclr/vm/classlayoutinfo.cpp index 8336f890660329..b7290c5a5c3b5f 100644 --- a/src/coreclr/vm/classlayoutinfo.cpp +++ b/src/coreclr/vm/classlayoutinfo.cpp @@ -285,7 +285,7 @@ namespace } else #endif // FEATURE_64BIT_ALIGNMENT - if (pNestedType.GetMethodTable()->ContainsPointers()) + if (pNestedType.GetMethodTable()->ContainsGCPointers()) { // this field type has GC pointers in it, which need to be pointer-size aligned placementInfo.m_alignment = TARGET_POINTER_SIZE; @@ -310,7 +310,7 @@ namespace if (corElemType == ELEMENT_TYPE_VALUETYPE) { _ASSERTE(!pNestedType.IsNull()); - return pNestedType.GetMethodTable()->ContainsPointers() != FALSE; + return pNestedType.GetMethodTable()->ContainsGCPointers() != FALSE; } return TRUE; } diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index d30177f9dc7978..a6c9ae123ae44e 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1623,7 +1623,7 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt) return mt->CanCompareBitsOrUseFastGetHashCode(); } - if (mt->ContainsPointers() + if (mt->ContainsGCPointers() || mt->IsNotTightlyPacked() || mt->GetClass()->IsInlineArray()) { diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index a1e64dae2d285e..41fa1842c5c13a 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -3368,7 +3368,7 @@ BOOL NDirect::MarshalingRequired( // as long as they aren't auto-layout and don't have any auto-layout fields. if (!runtimeMarshallingEnabled && !hndArgType.IsEnum() && - (hndArgType.GetMethodTable()->ContainsPointers() + (hndArgType.GetMethodTable()->ContainsGCPointers() || hndArgType.GetMethodTable()->IsAutoLayoutOrHasAutoLayoutField())) { return TRUE; diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index 06db3076ef4ebf..335bd3cb25caba 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -413,7 +413,7 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS if (totalSize >= LARGE_OBJECT_SIZE && totalSize >= GCHeapUtilities::GetGCHeap()->GetLOHThreshold()) flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - if (pArrayMT->ContainsPointers()) + if (pArrayMT->ContainsGCPointers()) flags |= GC_ALLOC_CONTAINS_REF; ArrayBase* orArray = NULL; @@ -513,7 +513,7 @@ OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 cElements) // The initial validation is copied from AllocateSzArray impl - if (pArrayMT->ContainsPointers() && cElements > 0) + if (pArrayMT->ContainsGCPointers() && cElements > 0) { // For arrays with GC pointers we can only work with empty arrays return NULL; @@ -720,7 +720,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, if (totalSize >= LARGE_OBJECT_SIZE && totalSize >= GCHeapUtilities::GetGCHeap()->GetLOHThreshold()) flags |= GC_ALLOC_LARGE_OBJECT_HEAP; - if (pArrayMT->ContainsPointers()) + if (pArrayMT->ContainsGCPointers()) flags |= GC_ALLOC_CONTAINS_REF; ArrayBase* orArray = NULL; @@ -1066,7 +1066,7 @@ OBJECTREF AllocateObject(MethodTable *pMT #endif // FEATURE_COMINTEROP else { - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) flags |= GC_ALLOC_CONTAINS_REF; if (pMT->HasFinalizer()) @@ -1122,7 +1122,7 @@ OBJECTREF TryAllocateFrozenObject(MethodTable* pObjMT) SetTypeHandleOnThreadForAlloc(TypeHandle(pObjMT)); - if (pObjMT->ContainsPointers() || pObjMT->IsComObjectType()) + if (pObjMT->ContainsGCPointers() || pObjMT->IsComObjectType()) { return NULL; } diff --git a/src/coreclr/vm/generics.cpp b/src/coreclr/vm/generics.cpp index 988229d8009a8f..384b1007cf8bda 100644 --- a/src/coreclr/vm/generics.cpp +++ b/src/coreclr/vm/generics.cpp @@ -217,7 +217,7 @@ ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( #endif // FEATURE_COMINTEROP // The number of bytes used for GC info - size_t cbGC = pOldMT->ContainsPointers() ? ((CGCDesc*) pOldMT)->GetSize() : 0; + size_t cbGC = pOldMT->ContainsGCPointers() ? ((CGCDesc*) pOldMT)->GetSize() : 0; // Bytes are required for the vtable itself S_SIZE_T safe_cbMT = S_SIZE_T( cbGC ) + S_SIZE_T( sizeof(MethodTable) ); diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 73603f2752969d..3807b00a8ca6e1 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -421,9 +421,9 @@ void *JIT_TrialAlloc::GenBox(Flags flags) // Here we are at the end of the success case // Check whether the object contains pointers - // test [ecx]MethodTable.m_dwFlags,MethodTable::enum_flag_ContainsPointers + // test [ecx]MethodTable.m_dwFlags,MethodTable::enum_flag_ContainsGCPointers sl.X86EmitOffsetModRM(0xf7, (X86Reg)0x0, kECX, offsetof(MethodTable, m_dwFlags)); - sl.Emit32(MethodTable::enum_flag_ContainsPointers); + sl.Emit32(MethodTable::enum_flag_ContainsGCPointers); CodeLabel *pointerLabel = sl.NewCodeLabel(); diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index b0fcef12d6b8d7..75d979076bd3a9 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -4383,7 +4383,7 @@ extern "C" void QCALLTYPE MngdNativeArrayMarshaler_ConvertContentsToNative(MngdN if ( (!ClrSafeInt::multiply(cElements, OleVariant::GetElementSizeForVarType(pThis->m_vt, pThis->m_pElementMT), cElements)) || cElements > MAX_SIZE_FOR_INTEROP) COMPlusThrow(kArgumentException, IDS_EE_STRUCTARRAYTOOLARGE); - _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsPointers()); + _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsGCPointers()); memcpyNoGCRefs(*pNativeHome, arrayRef->GetDataPtr(), cElements); } else @@ -4452,7 +4452,7 @@ extern "C" void QCALLTYPE MngdNativeArrayMarshaler_ConvertContentsToManaged(Mngd COMPlusThrow(kArgumentException, IDS_EE_STRUCTARRAYTOOLARGE); // If we are copying variants, strings, etc, we need to use write barrier - _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsPointers()); + _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsGCPointers()); memcpyNoGCRefs(arrayRef->GetDataPtr(), *pNativeHome, cElements); } else @@ -4567,7 +4567,7 @@ extern "C" void QCALLTYPE MngdFixedArrayMarshaler_ConvertContentsToNative(MngdFi SIZE_T cElements = arrayRef->GetNumComponents(); if (pMarshaler == NULL || pMarshaler->ComToOleArray == NULL) { - _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsPointers()); + _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsGCPointers()); memcpyNoGCRefs(pNativeHome, arrayRef->GetDataPtr(), nativeSize); } else @@ -4641,7 +4641,7 @@ extern "C" void QCALLTYPE MngdFixedArrayMarshaler_ConvertContentsToManaged(MngdF if (pMarshaler == NULL || pMarshaler->OleToComArray == NULL) { // If we are copying variants, strings, etc, we need to use write barrier - _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsPointers()); + _ASSERTE(!GetTypeHandleForCVType(OleVariant::GetCVTypeForVarType(pThis->m_vt)).GetMethodTable()->ContainsGCPointers()); memcpyNoGCRefs(arrayRef->GetDataPtr(), pNativeHome, nativeSize); } else diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 908077f7ba3899..181dc83cbea833 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -10959,7 +10959,7 @@ void Interpreter::DoIsReferenceOrContainsReferences(CORINFO_METHOD_HANDLE method MethodTable* typeArg = GetMethodTableFromClsHnd(sigInfoFull.sigInst.methInst[0]); - bool containsGcPtrs = typeArg->ContainsPointers(); + bool containsGcPtrs = typeArg->ContainsGCPointers(); // Return true for byref-like structs with ref fields (they might not have them) if (!containsGcPtrs && typeArg->IsByRefLike()) @@ -10981,7 +10981,7 @@ bool Interpreter::DoInterlockedCompareExchange(CorInfoType retType) } CONTRACTL_END; // These CompareExchange are must-expand: - // + // // long CompareExchange(ref long location1, long value, long comparand) // int CompareExchange(ref int location1, int value, int comparand) // ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) @@ -11033,7 +11033,7 @@ bool Interpreter::DoInterlockedExchange(CorInfoType retType) } CONTRACTL_END; // These Exchange are must-expand: - // + // // long Exchange(ref long location1, long value) // int Exchange(ref int location1, int value) // ushort Exchange(ref ushort location1, ushort value) @@ -11082,7 +11082,7 @@ bool Interpreter::DoInterlockedExchangeAdd(CorInfoType retType) } CONTRACTL_END; // These ExchangeAdd are must-expand: - // + // // long ExchangeAdd(ref long location1, long value) // int ExchangeAdd(ref int location1, int value) // diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index ca66bba77f49d6..935f1ccde8202c 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1182,7 +1182,7 @@ size_t CEEInfo::getClassThreadStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) EE_TO_JIT_TRANSITION_LEAF(); - return result; + return result; } size_t CEEInfo::getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) @@ -1203,7 +1203,7 @@ size_t CEEInfo::getClassStaticDynamicInfo(CORINFO_CLASS_HANDLE cls) EE_TO_JIT_TRANSITION_LEAF(); - return result; + return result; } CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable * pFieldMT) @@ -1218,7 +1218,7 @@ CorInfoHelpFunc CEEInfo::getSharedStaticsHelper(FieldDesc * pField, MethodTable bool isCollectible = pFieldMT->Collectible(); _ASSERTE(!isInexactMT); CorInfoHelpFunc helper; - + if (threadStatic) { if (GCStatic) @@ -1551,9 +1551,9 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, Object* frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr); _ASSERT(frozenObj != nullptr); - // ContainsPointers here is unnecessary but it's cheaper than IsInFrozenSegment + // ContainsGCPointers here is unnecessary but it's cheaper than IsInFrozenSegment // for structs containing gc handles - if (!frozenObj->GetMethodTable()->ContainsPointers() && + if (!frozenObj->GetMethodTable()->ContainsGCPointers() && GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(frozenObj)) { pResult->fieldLookup.addr = frozenObj->GetData(); @@ -2014,7 +2014,7 @@ unsigned CEEInfo::getClassAlignmentRequirementStatic(TypeHandle clsHnd) } else if (pInfo->IsManagedSequential() || pInfo->IsBlittable()) { - _ASSERTE(!pMT->ContainsPointers()); + _ASSERTE(!pMT->ContainsGCPointers()); // if it's managed sequential, we use the managed alignment requirement result = pInfo->m_ManagedLargestAlignmentRequirementOfAllMembers; @@ -2425,7 +2425,7 @@ unsigned CEEInfo::getClassGClayoutStatic(TypeHandle VMClsHnd, BYTE* gcPtrs) (size + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE); // walk the GC descriptors, turning on the correct bits - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); CGCDescSeries * pByValueSeries = map->GetLowestSeries(); @@ -3829,7 +3829,7 @@ uint32_t CEEInfo::getClassAttribsInternal (CORINFO_CLASS_HANDLE clsHnd) if (VMClsHnd.IsCanonicalSubtype()) ret |= CORINFO_FLG_SHAREDINST; - if (pMT->ContainsPointers() || pMT == g_TypedReferenceMT) + if (pMT->ContainsGCPointers() || pMT == g_TypedReferenceMT) ret |= CORINFO_FLG_CONTAINS_GC_PTR; if (pMT->IsDelegate()) @@ -11750,7 +11750,7 @@ bool CEEInfo::getStaticFieldContent(CORINFO_FIELD_HANDLE fieldHnd, uint8_t* buff { TypeHandle structType = field->GetFieldTypeHandleThrowing(); PTR_MethodTable structTypeMT = structType.AsMethodTable(); - if (!structTypeMT->ContainsPointers()) + if (!structTypeMT->ContainsGCPointers()) { // Fast-path: no GC pointers in the struct, we can use memcpy useMemcpy = true; @@ -11849,7 +11849,7 @@ bool CEEInfo::getObjectContent(CORINFO_OBJECT_HANDLE handle, uint8_t* buffer, in { Object* obj = OBJECTREFToObject(objRef); PTR_MethodTable type = obj->GetMethodTable(); - if (type->ContainsPointers()) + if (type->ContainsGCPointers()) { // RuntimeType has a gc field (object m_keepAlive), but if the object is in a frozen segment // it means that field is always nullptr so we can read any part of the object: @@ -13155,7 +13155,7 @@ void ComputeGCRefMap(MethodTable * pMT, BYTE * pGCRefMap, size_t cbGCRefMap) ZeroMemory(pGCRefMap, cbGCRefMap); - if (!pMT->ContainsPointers()) + if (!pMT->ContainsGCPointers()) return; CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); @@ -13304,7 +13304,7 @@ BOOL TypeLayoutCheck(MethodTable * pMT, PCCOR_SIGNATURE pBlob, BOOL printDiff) { if (dwFlags & READYTORUN_LAYOUT_GCLayout_Empty) { - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { if (printDiff) { diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 4a8dd9efbb57c3..628b8c6f6e45dc 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -1192,7 +1192,7 @@ ReturnKind MethodDesc::ParseReturnKindFromSig(INDEBUG(bool supportStringConstruc } #endif // UNIX_AMD64_ABI - if (pReturnTypeMT->ContainsPointers() || pReturnTypeMT->IsByRefLike()) + if (pReturnTypeMT->ContainsGCPointers() || pReturnTypeMT->IsByRefLike()) { if (pReturnTypeMT->GetNumInstanceFields() == 1) { diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index f93b3b2a1f4dcc..dde37703a384ce 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -658,7 +658,7 @@ void MethodTable::AllocateAuxiliaryData(LoaderAllocator *pAllocator, Module *pLo } prependedAllocationSpace = prependedAllocationSpace + sizeofStaticsStructure; - + cbAuxiliaryData = cbAuxiliaryData + S_SIZE_T(prependedAllocationSpace) + extraAllocation; if (cbAuxiliaryData.IsOverflow()) ThrowHR(COR_E_OVERFLOW); @@ -1569,7 +1569,7 @@ MethodTable::IsExternallyVisible() BOOL MethodTable::IsAllGCPointers() { - if (this->ContainsPointers()) + if (this->ContainsGCPointers()) { // check for canonical GC encoding for all-pointer types CGCDesc* pDesc = CGCDesc::GetCGCDescFromMT(this); @@ -3368,7 +3368,7 @@ void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStat bool hasFixedAddr = HasFixedAddressVTStatics(); LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName())); - const bool canBeFrozen = !pFieldMT->ContainsPointers() && !Collectible(); + const bool canBeFrozen = !pFieldMT->ContainsGCPointers() && !Collectible(); OBJECTREF obj = AllocateStaticBox(pFieldMT, hasFixedAddr, canBeFrozen); SetObjectReference((OBJECTREF*)(boxedStaticHandle), obj); GCPROTECT_END(); @@ -3394,7 +3394,7 @@ OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, bo if (canBeFrozen) { // In case if we don't plan to collect this handle we may try to allocate it on FOH - _ASSERT(!pFieldMT->ContainsPointers()); + _ASSERT(!pFieldMT->ContainsGCPointers()); FrozenObjectHeapManager* foh = SystemDomain::GetFrozenObjectHeapManager(); obj = ObjectToOBJECTREF(foh->TryAllocateObject(pFieldMT, pFieldMT->GetBaseSize())); // obj can be null in case if struct is huge (>64kb) @@ -3837,7 +3837,7 @@ bool MethodTable::IsInitedIfStaticDataAllocated() // If there is a class constructor, then the class cannot be preinitted. return false; } - + if (GetClass()->GetNonGCRegularStaticFieldBytes() == 0 && GetClass()->GetNumHandleRegularStatics() == 0) { // If there aren't static fields that are not thread statics, then the class is preinitted. @@ -7455,7 +7455,7 @@ MethodTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) DacEnumMemoryRegion(dac_cast(this), size); // Make sure the GCDescs are added to the dump - if (ContainsPointers()) + if (ContainsGCPointers()) { PTR_CGCDesc gcdesc = CGCDesc::GetCGCDescFromMT(this); size_t size = gcdesc->GetSize(); diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 23fcb92e5291aa..80abb784df1faa 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -601,6 +601,7 @@ struct DynamicStaticsInfo { oldValFromInterlockedOp = InterlockedCompareExchangeT(pAddr, newVal | oldVal, oldVal); } + } while(oldValFromInterlockedOp != oldVal); return true; } @@ -1784,10 +1785,10 @@ class MethodTable inline WORD GetNumIntroducedInstanceFields(); - BOOL ContainsPointers() + BOOL ContainsGCPointers() { LIMITED_METHOD_CONTRACT; - return !!GetFlag(enum_flag_ContainsPointers); + return !!GetFlag(enum_flag_ContainsGCPointers); } BOOL Collectible() @@ -1800,10 +1801,10 @@ class MethodTable #endif } - BOOL ContainsPointersOrCollectible() + BOOL ContainsGCPointersOrCollectible() { LIMITED_METHOD_CONTRACT; - return GetFlag(enum_flag_ContainsPointers) || GetFlag(enum_flag_Collectible); + return GetFlag(enum_flag_ContainsGCPointers) || GetFlag(enum_flag_Collectible); } OBJECTHANDLE GetLoaderAllocatorObjectHandle(); @@ -1813,10 +1814,10 @@ class MethodTable BOOL IsAllGCPointers(); - void SetContainsPointers() + void SetContainsGCPointers() { LIMITED_METHOD_CONTRACT; - SetFlag(enum_flag_ContainsPointers); + SetFlag(enum_flag_ContainsGCPointers); } #ifdef FEATURE_64BIT_ALIGNMENT @@ -3603,7 +3604,7 @@ public : enum_flag_RequiresAlign8 = 0x00800000, // Type requires 8-byte alignment (only set on platforms that require this and don't get it implicitly) #endif - enum_flag_ContainsPointers = 0x01000000, // Contains object references + enum_flag_ContainsGCPointers = 0x01000000, // Contains object references enum_flag_HasTypeEquivalence = 0x02000000, // can be equivalent to another type enum_flag_IsTrackedReferenceWithFinalizer = 0x04000000, // unused = 0x08000000, @@ -3888,8 +3889,23 @@ public : BOOL Validate (); static void GetStaticsOffsets(StaticsOffsetType staticsOffsetType, bool fGenericsStatics, uint32_t *dwGCOffset, uint32_t *dwNonGCOffset); + + template friend struct ::cdac_offsets; }; // class MethodTable +template<> struct cdac_offsets +{ + static constexpr size_t MTFlags = offsetof(MethodTable, m_dwFlags); + static constexpr size_t BaseSize = offsetof(MethodTable, m_BaseSize); + static constexpr size_t MTFlags2 = offsetof(MethodTable, m_dwFlags2); + static constexpr size_t EEClassOrCanonMT = offsetof(MethodTable, m_pEEClass); + static constexpr size_t Module = offsetof(MethodTable, m_pModule); + static constexpr size_t AuxiliaryData = offsetof(MethodTable, m_pAuxiliaryData); + static constexpr size_t ParentMethodTable = offsetof(MethodTable, m_pParentMethodTable); + static constexpr size_t NumInterfaces = offsetof(MethodTable, m_wNumInterfaces); + static constexpr size_t NumVirtuals = offsetof(MethodTable, m_wNumVirtuals); +}; + #ifndef CROSSBITNESS_COMPILE static_assert_no_msg(sizeof(MethodTable) == SIZEOF__MethodTable_); #endif diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 3e684a4bab3a1e..549031c53ba059 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1878,7 +1878,7 @@ MethodTableBuilder::BuildMethodTableThrowing( // GC reqires the series to be sorted. // TODO: fix it so that we emit them in the correct order in the first place. - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { CGCDesc* gcDesc = CGCDesc::GetCGCDescFromMT(pMT); qsort(gcDesc->GetLowestSeries(), (int)gcDesc->GetNumSeries(), sizeof(CGCDescSeries), compareCGCDescSeries); @@ -1907,7 +1907,7 @@ MethodTableBuilder::BuildMethodTableThrowing( // // structs with GC pointers MUST be pointer sized aligned because the GC assumes it - if (IsValueClass() && pMT->ContainsPointers() && (bmtFP->NumInstanceFieldBytes % TARGET_POINTER_SIZE != 0)) + if (IsValueClass() && pMT->ContainsGCPointers() && (bmtFP->NumInstanceFieldBytes % TARGET_POINTER_SIZE != 0)) { BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); } @@ -2098,7 +2098,7 @@ MethodTableBuilder::ResolveInterfaces( MethodTable * pParentClass = GetParentMethodTable(); PREFIX_ASSUME(pParentClass != NULL); - bmtParent->NumParentPointerSeries = pParentClass->ContainsPointers() ? + bmtParent->NumParentPointerSeries = pParentClass->ContainsGCPointers() ? (DWORD)CGCDesc::GetCGCDescFromMT(pParentClass)->GetNumSeries() : 0; if (pParentClass->HasFieldsWhichMustBeInited()) @@ -8317,7 +8317,7 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach } else #endif // FEATURE_64BIT_ALIGNMENT - if (pByValueMT->ContainsPointers()) + if (pByValueMT->ContainsGCPointers()) { // this field type has GC pointers in it, which need to be pointer-size aligned // so do this if it has not been done already @@ -8335,13 +8335,13 @@ VOID MethodTableBuilder::PlaceInstanceFields(MethodTable ** pByValueClassCach pFieldDescList[i].SetOffset(dwCumulativeInstanceFieldPos - dwOffsetBias); dwCumulativeInstanceFieldPos += pByValueMT->GetNumInstanceFieldBytes(); - if (pByValueMT->ContainsPointers()) + if (pByValueMT->ContainsGCPointers()) { // Add pointer series for by-value classes dwNumGCPointerSeries += (DWORD)CGCDesc::GetCGCDescFromMT(pByValueMT)->GetNumSeries(); } - if (!pByValueMT->ContainsPointers() || !pByValueMT->IsAllGCPointers()) + if (!pByValueMT->ContainsGCPointers() || !pByValueMT->IsAllGCPointers()) { isAllGCPointers = false; } @@ -8684,7 +8684,7 @@ MethodTableBuilder::HandleExplicitLayout( else { MethodTable *pByValueMT = pByValueClassCache[valueClassCacheIndex]; - if (pByValueMT->IsByRefLike() || pByValueMT->ContainsPointers()) + if (pByValueMT->IsByRefLike() || pByValueMT->ContainsGCPointers()) { if ((pFD->GetOffset() & ((ULONG)TARGET_POINTER_SIZE - 1)) != 0) { @@ -8881,7 +8881,7 @@ MethodTableBuilder::HandleExplicitLayout( memset((void*)vcLayout, nonoref, fieldSize); // If the type contains pointers fill it out from the GC data - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { // use pointer series to locate the orefs CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); @@ -9097,7 +9097,7 @@ MethodTableBuilder::HandleGCForExplicitLayout() if (bmtFP->NumGCPointerSeries != 0) { - pMT->SetContainsPointers(); + pMT->SetContainsGCPointers(); // Copy the pointer series map from the parent CGCDesc::Init( (PVOID) pMT, bmtFP->NumGCPointerSeries ); @@ -10643,7 +10643,7 @@ MethodTableBuilder::SetupMethodTable2( pMT->SetHasClassConstructor(); CONSISTENCY_CHECK(pMT->GetClassConstructorSlot() == bmtVT->pCCtor->GetSlotIndex()); } - + if (bmtVT->pDefaultCtor != NULL) { pMT->SetHasDefaultConstructor(); @@ -11569,7 +11569,7 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac CGCDescSeries *pSeries; CGCDescSeries *pHighest; - pMT->SetContainsPointers(); + pMT->SetContainsGCPointers(); CGCDesc::Init( (PVOID) pMT, bmtFP->NumGCPointerSeries ); @@ -11625,7 +11625,7 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac { MethodTable* pByValueMT = pByValueClassCache[i]; - if (pByValueMT->ContainsPointers()) + if (pByValueMT->ContainsGCPointers()) { // Offset of the by value class in the class we are building, does NOT include Object DWORD dwCurrentOffset = pFieldDescList[i].GetOffset(); diff --git a/src/coreclr/vm/mlinfo.cpp b/src/coreclr/vm/mlinfo.cpp index f3401ae2c8fd7b..8131e4fd3053a4 100644 --- a/src/coreclr/vm/mlinfo.cpp +++ b/src/coreclr/vm/mlinfo.cpp @@ -1174,7 +1174,7 @@ namespace TypeHandle sigTH = sig.GetTypeHandleThrowing(pModule, pTypeContext); MethodTable* pMT = sigTH.GetMethodTable(); - if (!pMT->IsValueType() || pMT->ContainsPointers()) + if (!pMT->IsValueType() || pMT->ContainsGCPointers()) { *errorResIDOut = IDS_EE_BADMARSHAL_MARSHAL_DISABLED; return MarshalInfo::MARSHAL_TYPE_UNKNOWN; diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index 2a05ca470bff16..d74d368b0bafdb 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -364,7 +364,7 @@ void STDCALL CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT) _ASSERTE(!pMT->IsArray()); // bunch of assumptions about arrays wrong. - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { memmoveGCRefs(dest, src, pMT->GetNumInstanceFieldBytes()); } diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index 7c90d9e1753ecb..3cffa14b12f4a2 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -1166,7 +1166,7 @@ bool HeapWalkHelper(Object * pBO, void * pvContext) ProfilerWalkHeapContext * pProfilerWalkHeapContext = (ProfilerWalkHeapContext *) pvContext; - if (pMT->ContainsPointersOrCollectible()) + if (pMT->ContainsGCPointersOrCollectible()) { // First round through calculates the number of object refs for this class GCHeapUtilities::GetGCHeap()->DiagWalkObject(pBO, &CountContainedObjectRef, (void *)&cNumRefs); @@ -6747,7 +6747,7 @@ HRESULT ProfToEEInterfaceImpl::EnumerateObjectReferences(ObjectID objectId, Obje Object* pBO = (Object*)objectId; MethodTable *pMT = pBO->GetMethodTable(); - if (pMT->ContainsPointersOrCollectible()) + if (pMT->ContainsGCPointersOrCollectible()) { GCHeapUtilities::GetGCHeap()->DiagWalkObject2(pBO, (walk_fn2)callback, clientData); return S_OK; diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 115e0f85ac96c7..aec38501adf60d 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -647,7 +647,7 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, *(PVOID *)pArgDst = pStackCopy; // save the info into ValueClassInfo - if (pMT->ContainsPointers()) + if (pMT->ContainsGCPointers()) { pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pStackCopy, pMT, pValueClasses); } diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 108e95cc39e53c..facb809cd4841a 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -5062,7 +5062,7 @@ void ReportPointersFromValueType(promote_func *fn, ScanContext *sc, PTR_MethodTa reporter.Find(pMT, 0 /* baseOffset */); } - if (!pMT->ContainsPointers()) + if (!pMT->ContainsGCPointers()) return; CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); @@ -5091,7 +5091,7 @@ void ReportPointersFromValueTypeArg(promote_func *fn, ScanContext *sc, PTR_Metho { WRAPPER_NO_CONTRACT; - if (!pMT->ContainsPointers() && !pMT->IsByRefLike()) + if (!pMT->ContainsGCPointers() && !pMT->IsByRefLike()) { return; } diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 29595b9414d514..283b6d3fa2d883 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -2657,7 +2657,7 @@ void ILStubLinker::TransformArgForJIT(LocalDesc *pLoc) // JIT will handle structures if (pLoc->InternalToken.IsValueType()) { - _ASSERTE(pLoc->InternalToken.IsNativeValueType() || !pLoc->InternalToken.GetMethodTable()->ContainsPointers()); + _ASSERTE(pLoc->InternalToken.IsNativeValueType() || !pLoc->InternalToken.GetMethodTable()->ContainsGCPointers()); break; } FALLTHROUGH; diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index e9fb3a75852ea1..4d9c60838b54f5 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -281,7 +281,7 @@ bool TailCallHelp::GenerateGCDescriptor( TypeHandle tyHnd = val.TyHnd; if (tyHnd.IsValueType()) { - if (!tyHnd.GetMethodTable()->ContainsPointers()) + if (!tyHnd.GetMethodTable()->ContainsGCPointers()) { #ifndef TARGET_X86 // The generic instantiation arg is right after this pointer diff --git a/src/native/managed/cdacreader/src/Constants.cs b/src/native/managed/cdacreader/src/Constants.cs index c4003c96306563..5c95db68d227f9 100644 --- a/src/native/managed/cdacreader/src/Constants.cs +++ b/src/native/managed/cdacreader/src/Constants.cs @@ -15,5 +15,7 @@ internal static class Globals internal const string FeatureEHFunclets = nameof(FeatureEHFunclets); internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion); + + internal const string FreeObjectMethodTable = nameof(FreeObjectMethodTable); } } diff --git a/src/native/managed/cdacreader/src/Contracts/Registry.cs b/src/native/managed/cdacreader/src/Contracts/Registry.cs index 98f2a28d7b564a..9f7151adec432c 100644 --- a/src/native/managed/cdacreader/src/Contracts/Registry.cs +++ b/src/native/managed/cdacreader/src/Contracts/Registry.cs @@ -20,6 +20,7 @@ public Registry(Target target) public IException Exception => GetContract(); public IThread Thread => GetContract(); + public IRuntimeTypeSystem RuntimeTypeSystem => GetContract(); private T GetContract() where T : IContract { diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs new file mode 100644 index 00000000000000..8d37ca5441fcd3 --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs @@ -0,0 +1,67 @@ +// 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.Reflection.Metadata.Ecma335; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +// an opaque handle to a method table. See IMetadata.GetMethodTableData +internal readonly struct MethodTableHandle +{ + internal MethodTableHandle(TargetPointer address) + { + Address = address; + } + + internal TargetPointer Address { get; } +} + +internal interface IRuntimeTypeSystem : IContract +{ + static string IContract.Name => nameof(RuntimeTypeSystem); + static IContract IContract.Create(Target target, int version) + { + TargetPointer targetPointer = target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable); + TargetPointer freeObjectMethodTable = target.ReadPointer(targetPointer); + return version switch + { + 1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable), + _ => default(RuntimeTypeSystem), + }; + } + + #region MethodTable inspection APIs + public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer) => throw new NotImplementedException(); + + public virtual TargetPointer GetModule(MethodTableHandle methodTable) => throw new NotImplementedException(); + // A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the + // MethodTable of the prototypical instance. + public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException(); + public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException(); + + public virtual uint GetBaseSize(MethodTableHandle methodTable) => throw new NotImplementedException(); + // The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes) + public virtual uint GetComponentSize(MethodTableHandle methodTable) => throw new NotImplementedException(); + + // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap + public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException(); + public virtual bool IsString(MethodTableHandle methodTable) => throw new NotImplementedException(); + // True if the MethodTable represents a type that contains managed references + public virtual bool ContainsGCPointers(MethodTableHandle methodTable) => throw new NotImplementedException(); + public virtual bool IsDynamicStatics(MethodTableHandle methodTable) => throw new NotImplementedException(); + public virtual ushort GetNumMethods(MethodTableHandle methodTable) => throw new NotImplementedException(); + public virtual ushort GetNumInterfaces(MethodTableHandle methodTable) => throw new NotImplementedException(); + + // Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation + public virtual uint GetTypeDefToken(MethodTableHandle methodTable) => throw new NotImplementedException(); + // Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type, + // or for its generic type definition if it is a generic instantiation + public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable) => throw new NotImplementedException(); + #endregion MethodTable inspection APIs +} + +internal struct RuntimeTypeSystem : IRuntimeTypeSystem +{ + // Everything throws NotImplementedException +} diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs new file mode 100644 index 00000000000000..e6f98f0d8e2f08 --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal partial struct RuntimeTypeSystem_1 +{ + // The lower 16-bits of the MTFlags field are used for these flags, + // if WFLAGS_HIGH.HasComponentSize is unset + [Flags] + internal enum WFLAGS_LOW : uint + { + GenericsMask = 0x00000030, + GenericsMask_NonGeneric = 0x00000000, // no instantiation + + StringArrayValues = + GenericsMask_NonGeneric | + 0, + } + + // Upper bits of MTFlags + [Flags] + internal enum WFLAGS_HIGH : uint + { + Category_Mask = 0x000F0000, + Category_Array = 0x00080000, + Category_Array_Mask = 0x000C0000, + Category_Interface = 0x000C0000, + ContainsGCPointers = 0x01000000, + HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size, + // otherwise the lower bits are used for WFLAGS_LOW + } + + [Flags] + internal enum WFLAGS2_ENUM : uint + { + DynamicStatics = 0x0002, + } + + internal struct MethodTableFlags + { + public uint MTFlags { get; init; } + public uint MTFlags2 { get; init; } + public uint BaseSize { get; init; } + + private const int MTFlags2TypeDefRidShift = 8; + private WFLAGS_HIGH FlagsHigh => (WFLAGS_HIGH)MTFlags; + private WFLAGS_LOW FlagsLow => (WFLAGS_LOW)MTFlags; + public int GetTypeDefRid() => (int)(MTFlags2 >> MTFlags2TypeDefRidShift); + + public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) => throw new NotImplementedException("TODO"); + public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) => FlagsHigh & mask; + + public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) => (WFLAGS2_ENUM)MTFlags2 & mask; + + private ushort ComponentSizeBits => (ushort)(MTFlags & 0x0000ffff); // note: caller should check HasComponentSize + + private bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag) + { + if (IsStringOrArray) + { + return (WFLAGS_LOW.StringArrayValues & mask) == flag; + } + else + { + return (FlagsLow & mask) == flag; + } + } + + private bool TestFlagWithMask(WFLAGS2_ENUM mask, WFLAGS2_ENUM flag) + { + return ((WFLAGS2_ENUM)MTFlags2 & mask) == flag; + } + + public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0; + public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface; + public bool IsString => HasComponentSize && !IsArray && ComponentSizeBits == 2; + public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array; + public bool IsStringOrArray => HasComponentSize; + public ushort ComponentSize => HasComponentSize ? ComponentSizeBits : (ushort)0; + public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric); + public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0; + public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0; + } +} diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.NonValidated.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.NonValidated.cs new file mode 100644 index 00000000000000..ef392130e62316 --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.NonValidated.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem +{ + // GC Heap corruption may create situations where a pointer value may point to garbage or even + // to an unmapped memory region. + // All types here have not been validated as actually representing a MethodTable, EEClass, etc. + // All checks are unsafe and may throw if we access an invalid address in target memory. + internal static class NonValidated + { + + // This doesn't need as many properties as MethodTable because we don't want to be operating on + // a NonValidatedMethodTable for too long + internal struct MethodTable + { + private readonly Target _target; + private readonly Target.TypeInfo _type; + internal TargetPointer Address { get; init; } + + private MethodTableFlags? _methodTableFlags; + + internal MethodTable(Target target, TargetPointer methodTablePointer) + { + _target = target; + _type = target.GetTypeInfo(DataType.MethodTable); + Address = methodTablePointer; + _methodTableFlags = null; + } + + private MethodTableFlags GetOrCreateFlags() + { + if (_methodTableFlags == null) + { + // note: may throw if the method table Address is corrupted + MethodTableFlags flags = new MethodTableFlags + { + MTFlags = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags)].Offset), + MTFlags2 = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags2)].Offset), + BaseSize = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.BaseSize)].Offset), + }; + _methodTableFlags = flags; + } + return _methodTableFlags.Value; + } + + internal MethodTableFlags Flags => GetOrCreateFlags(); + + internal TargetPointer EEClassOrCanonMT => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(EEClassOrCanonMT)].Offset); + internal TargetPointer EEClass => GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass"); + internal TargetPointer CanonMT + { + get + { + if (GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.CanonMT) + { + return new TargetPointer((ulong)EEClassOrCanonMT & ~(ulong)EEClassOrCanonMTBits.Mask); + } + else + { + throw new InvalidOperationException("not a canonical method table"); + } + } + } + } + + internal struct EEClass + { + public readonly Target _target; + private readonly Target.TypeInfo _type; + + internal TargetPointer Address { get; init; } + + internal EEClass(Target target, TargetPointer eeClassPointer) + { + _target = target; + Address = eeClassPointer; + _type = target.GetTypeInfo(DataType.EEClass); + } + + internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset); + } + + internal static MethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer) + { + return new MethodTable(target, methodTablePointer); + } + + internal static EEClass GetEEClassData(Target target, TargetPointer eeClassPointer) + { + return new EEClass(target, eeClassPointer); + } + + } + + /// + /// Validates that the given address is a valid MethodTable. + /// + /// + /// If the target process has memory corruption, we may see pointers that are not valid method tables. + /// We validate by looking at the MethodTable -> EEClass -> MethodTable relationship (which may throw if we access invalid memory). + /// And then we do some ad-hoc checks on the method table flags. + private bool ValidateMethodTablePointer(NonValidated.MethodTable umt) + { + try + { + if (!ValidateThrowing(umt)) + { + return false; + } + if (!ValidateMethodTableAdHoc(umt)) + { + return false; + } + } + catch (System.Exception) + { + // TODO(cdac): maybe don't swallow all exceptions? We could consider a richer contract that + // helps to track down what sort of memory corruption caused the validation to fail. + // TODO(cdac): we could also consider a more fine-grained exception type so we don't mask + // programmer mistakes in cdacreader. + return false; + } + return true; + } + + // This portion of validation may throw if we are trying to read an invalid address in the target process + private bool ValidateThrowing(NonValidated.MethodTable methodTable) + { + // For non-generic classes, we can rely on comparing + // object->methodtable->class->methodtable + // to + // object->methodtable + // + // However, for generic instantiation this does not work. There we must + // compare + // + // object->methodtable->class->methodtable->class + // to + // object->methodtable->class + TargetPointer eeClassPtr = GetClassThrowing(methodTable); + if (eeClassPtr != TargetPointer.Null) + { + NonValidated.EEClass eeClass = NonValidated.GetEEClassData(_target, eeClassPtr); + TargetPointer methodTablePtrFromClass = eeClass.MethodTable; + if (methodTable.Address == methodTablePtrFromClass) + { + return true; + } + if (methodTable.Flags.HasInstantiation || methodTable.Flags.IsArray) + { + NonValidated.MethodTable methodTableFromClass = NonValidated.GetMethodTableData(_target, methodTablePtrFromClass); + TargetPointer classFromMethodTable = GetClassThrowing(methodTableFromClass); + return classFromMethodTable == eeClassPtr; + } + } + return false; + } + + private bool ValidateMethodTableAdHoc(NonValidated.MethodTable methodTable) + { + // ad-hoc checks; add more here as needed + if (!methodTable.Flags.IsInterface && !methodTable.Flags.IsString) + { + if (methodTable.Flags.BaseSize == 0 || !_target.IsAlignedToPointerSize(methodTable.Flags.BaseSize)) + { + return false; + } + } + return true; + } + + internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr) + { + return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask); + } + private TargetPointer GetClassThrowing(NonValidated.MethodTable methodTable) + { + TargetPointer eeClassOrCanonMT = methodTable.EEClassOrCanonMT; + + if (GetEEClassOrCanonMTBits(eeClassOrCanonMT) == EEClassOrCanonMTBits.EEClass) + { + return methodTable.EEClass; + } + else + { + TargetPointer canonicalMethodTablePtr = methodTable.CanonMT; + NonValidated.MethodTable umt = NonValidated.GetMethodTableData(_target, canonicalMethodTablePtr); + return umt.EEClass; + } + } + + +} diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs new file mode 100644 index 00000000000000..da2ea6c8b01b3e --- /dev/null +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs @@ -0,0 +1,157 @@ +// 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.Reflection.Metadata.Ecma335; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + + + +internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem +{ + private readonly Target _target; + private readonly TargetPointer _freeObjectMethodTablePointer; + + // TODO(cdac): we mutate this dictionary - copies of the RuntimeTypeSystem_1 struct share this instance. + // If we need to invalidate our view of memory, we should clear this dictionary. + private readonly Dictionary _methodTables = new(); + + + internal struct MethodTable + { + internal MethodTableFlags Flags { get; } + internal ushort NumInterfaces { get; } + internal ushort NumVirtuals { get; } + internal TargetPointer ParentMethodTable { get; } + internal TargetPointer Module { get; } + internal TargetPointer EEClassOrCanonMT { get; } + internal MethodTable(Data.MethodTable data) + { + Flags = new MethodTableFlags + { + MTFlags = data.MTFlags, + MTFlags2 = data.MTFlags2, + BaseSize = data.BaseSize, + }; + NumInterfaces = data.NumInterfaces; + NumVirtuals = data.NumVirtuals; + EEClassOrCanonMT = data.EEClassOrCanonMT; + Module = data.Module; + ParentMethodTable = data.ParentMethodTable; + } + } + + // Low order bit of EEClassOrCanonMT. + // See MethodTable::LowBits UNION_EECLASS / UNION_METHODABLE + [Flags] + internal enum EEClassOrCanonMTBits + { + EEClass = 0, + CanonMT = 1, + Mask = 1, + } + + internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer) + { + _target = target; + _freeObjectMethodTablePointer = freeObjectMethodTablePointer; + } + + internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer; + + + public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer) + { + // if we already validated this address, return a handle + if (_methodTables.ContainsKey(methodTablePointer)) + { + return new MethodTableHandle(methodTablePointer); + } + // Check if we cached the underlying data already + if (_target.ProcessedData.TryGet(methodTablePointer, out Data.MethodTable? methodTableData)) + { + // we already cached the data, we must have validated the address, create the representation struct for our use + MethodTable trustedMethodTable = new MethodTable(methodTableData); + _ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable); + return new MethodTableHandle(methodTablePointer); + } + + // If it's the free object method table, we trust it to be valid + if (methodTablePointer == FreeObjectMethodTablePointer) + { + Data.MethodTable freeObjectMethodTableData = _target.ProcessedData.GetOrAdd(methodTablePointer); + MethodTable trustedMethodTable = new MethodTable(freeObjectMethodTableData); + _ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable); + return new MethodTableHandle(methodTablePointer); + } + + // Otherwse, get ready to validate + NonValidated.MethodTable nonvalidatedMethodTable = NonValidated.GetMethodTableData(_target, methodTablePointer); + + if (!ValidateMethodTablePointer(nonvalidatedMethodTable)) + { + throw new InvalidOperationException("Invalid method table pointer"); + } + // ok, we validated it, cache the data and add the MethodTable_1 struct to the dictionary + Data.MethodTable trustedMethodTableData = _target.ProcessedData.GetOrAdd(methodTablePointer); + MethodTable trustedMethodTableF = new MethodTable(trustedMethodTableData); + _ = _methodTables.TryAdd(methodTablePointer, trustedMethodTableF); + return new MethodTableHandle(methodTablePointer); + } + + + public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize; + + public uint GetComponentSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ComponentSize; + + private TargetPointer GetClassPointer(MethodTableHandle methodTableHandle) + { + MethodTable methodTable = _methodTables[methodTableHandle.Address]; + switch (GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT)) + { + case EEClassOrCanonMTBits.EEClass: + return methodTable.EEClassOrCanonMT; + case EEClassOrCanonMTBits.CanonMT: + TargetPointer canonMTPtr = new TargetPointer((ulong)methodTable.EEClassOrCanonMT & ~(ulong)RuntimeTypeSystem_1.EEClassOrCanonMTBits.Mask); + MethodTableHandle canonMTHandle = GetMethodTableHandle(canonMTPtr); + MethodTable canonMT = _methodTables[canonMTHandle.Address]; + return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass + default: + throw new InvalidOperationException(); + } + } + + // only called on validated method tables, so we don't need to re-validate the EEClass + private Data.EEClass GetClassData(MethodTableHandle methodTableHandle) + { + TargetPointer clsPtr = GetClassPointer(methodTableHandle); + return _target.ProcessedData.GetOrAdd(clsPtr); + } + + public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable; + + public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module; + public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable; + + public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address; + + public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString; + public bool ContainsGCPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsGCPointers; + + public uint GetTypeDefToken(MethodTableHandle methodTableHandle) + { + MethodTable methodTable = _methodTables[methodTableHandle.Address]; + return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24)); + } + + public ushort GetNumMethods(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).NumMethods; + + public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces; + + public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).CorTypeAttr; + + public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsDynamicStatics; + +} diff --git a/src/native/managed/cdacreader/src/Data/EEClass.cs b/src/native/managed/cdacreader/src/Data/EEClass.cs new file mode 100644 index 00000000000000..e697b3f40756c8 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/EEClass.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +public sealed class EEClass : IData +{ + static EEClass IData.Create(Target target, TargetPointer address) => new EEClass(target, address); + public EEClass(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEClass); + + MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset); + NumMethods = target.Read(address + (ulong)type.Fields[nameof(NumMethods)].Offset); + CorTypeAttr = target.Read(address + (ulong)type.Fields[nameof(CorTypeAttr)].Offset); + } + + public TargetPointer MethodTable { get; init; } + public ushort NumMethods { get; init; } + public uint CorTypeAttr { get; init; } +} diff --git a/src/native/managed/cdacreader/src/Data/MethodTable.cs b/src/native/managed/cdacreader/src/Data/MethodTable.cs new file mode 100644 index 00000000000000..3319c6547f0568 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/MethodTable.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class MethodTable : IData +{ + static MethodTable IData.Create(Target target, TargetPointer address) => new MethodTable(target, address); + public MethodTable(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.MethodTable); + + MTFlags = target.Read(address + (ulong)type.Fields[nameof(MTFlags)].Offset); + BaseSize = target.Read(address + (ulong)type.Fields[nameof(BaseSize)].Offset); + MTFlags2 = target.Read(address + (ulong)type.Fields[nameof(MTFlags2)].Offset); + EEClassOrCanonMT = target.ReadPointer(address + (ulong)type.Fields[nameof(EEClassOrCanonMT)].Offset); + Module = target.ReadPointer(address + (ulong)type.Fields[nameof(Module)].Offset); + ParentMethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(ParentMethodTable)].Offset); + NumInterfaces = target.Read(address + (ulong)type.Fields[nameof(NumInterfaces)].Offset); + NumVirtuals = target.Read(address + (ulong)type.Fields[nameof(NumVirtuals)].Offset); + } + + public uint MTFlags { get; init; } + public uint BaseSize { get; init; } + public uint MTFlags2 { get; init; } + public TargetPointer EEClassOrCanonMT { get; init; } + public TargetPointer Module { get; init; } + public TargetPointer ParentMethodTable { get; init; } + public ushort NumInterfaces { get; init; } + public ushort NumVirtuals { get; init; } +} diff --git a/src/native/managed/cdacreader/src/Data/MethodTableAuxiliaryData.cs b/src/native/managed/cdacreader/src/Data/MethodTableAuxiliaryData.cs new file mode 100644 index 00000000000000..bdc711a5104735 --- /dev/null +++ b/src/native/managed/cdacreader/src/Data/MethodTableAuxiliaryData.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class MethodTableAuxiliaryData : IData +{ + static MethodTableAuxiliaryData IData.Create(Target target, TargetPointer address) => new MethodTableAuxiliaryData(target, address); + + private MethodTableAuxiliaryData(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.MethodTableAuxiliaryData); + + AuxFlags = target.Read(address + (ulong)type.Fields[nameof(AuxFlags)].Offset); + + } + + public uint AuxFlags { get; init; } +} diff --git a/src/native/managed/cdacreader/src/DataType.cs b/src/native/managed/cdacreader/src/DataType.cs index 94274d5f47a8ba..338a544ab488c5 100644 --- a/src/native/managed/cdacreader/src/DataType.cs +++ b/src/native/managed/cdacreader/src/DataType.cs @@ -24,5 +24,9 @@ public enum DataType ThreadStore, GCAllocContext, ExceptionInfo, - RuntimeThreadLocals + RuntimeThreadLocals, + + MethodTable, + EEClass, + MethodTableAuxiliaryData, } diff --git a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs index bd0d20e65b77c0..6a806e631731cd 100644 --- a/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdacreader/src/Legacy/ISOSDacInterface.cs @@ -42,6 +42,25 @@ internal struct DacpThreadData public ulong lastThrownObjectHandle; public ulong nextThread; } + +internal struct DacpMethodTableData +{ + public int bIsFree; // everything else is NULL if this is true. + public ulong module; + public ulong klass; + public ulong parentMethodTable; + public ushort wNumInterfaces; + public ushort wNumMethods; + public ushort wNumVtableSlots; + public ushort wNumVirtuals; + public uint baseSize; + public uint componentSize; + public uint /*mdTypeDef*/ cl; // Metadata token + public uint dwAttrClass; // cached metadata + public int bIsShared; // Always false, preserved for backward compatibility + public int bIsDynamic; + public int bContainsGCPointers; +} #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value [GeneratedComInterface] @@ -139,7 +158,7 @@ internal unsafe partial interface ISOSDacInterface [PreserveSig] int GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded); [PreserveSig] - int GetMethodTableData(ulong mt, /*struct DacpMethodTableData*/ void* data); + int GetMethodTableData(ulong mt, DacpMethodTableData* data); [PreserveSig] int GetMethodTableSlot(ulong mt, uint slot, ulong* value); [PreserveSig] diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 8dc36985a9addc..240d98bf7d2952 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -81,9 +82,71 @@ public int GetBreakingChangeVersion() public unsafe int GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) => HResults.E_NOTIMPL; public unsafe int GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) => HResults.E_NOTIMPL; public unsafe int GetMethodDescTransparencyData(ulong methodDesc, void* data) => HResults.E_NOTIMPL; - public unsafe int GetMethodTableData(ulong mt, void* data) => HResults.E_NOTIMPL; + public unsafe int GetMethodTableData(ulong mt, DacpMethodTableData* data) + { + if (mt == 0 || data == null) + return HResults.E_INVALIDARG; + + try + { + Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem; + Contracts.MethodTableHandle methodTable = contract.GetMethodTableHandle(mt); + + DacpMethodTableData result = default; + result.baseSize = contract.GetBaseSize(methodTable); + // [compat] SOS DAC APIs added this base size adjustment for strings + // due to: "2008/09/25 Title: New implementation of StringBuilder and improvements in String class" + // which changed StringBuilder not to use a String as an internal buffer and in the process + // changed the String internals so that StringObject::GetBaseSize() now includes the nul terminator character, + // which is apparently not expected by SOS. + if (contract.IsString(methodTable)) + result.baseSize -= sizeof(char); + + result.componentSize = contract.GetComponentSize(methodTable); + bool isFreeObjectMT = contract.IsFreeObjectMethodTable(methodTable); + result.bIsFree = isFreeObjectMT ? 1 : 0; + if (!isFreeObjectMT) + { + result.module = contract.GetModule(methodTable); + // Note: really the canonical method table, not the EEClass, which we don't expose + result.klass = contract.GetCanonicalMethodTable(methodTable); + result.parentMethodTable = contract.GetParentMethodTable(methodTable); + result.wNumInterfaces = contract.GetNumInterfaces(methodTable); + result.wNumMethods = contract.GetNumMethods(methodTable); + result.wNumVtableSlots = 0; // always return 0 since .NET 9 + result.wNumVirtuals = 0; // always return 0 since .NET 9 + result.cl = contract.GetTypeDefToken(methodTable); + result.dwAttrClass = contract.GetTypeDefTypeAttributes(methodTable); + result.bContainsGCPointers = contract.ContainsGCPointers(methodTable) ? 1 : 0; + result.bIsShared = 0; + result.bIsDynamic = contract.IsDynamicStatics(methodTable) ? 1 : 0; + } + *data = result; + return HResults.S_OK; + } + catch (Exception ex) + { + return ex.HResult; + } + } public unsafe int GetMethodTableFieldData(ulong mt, void* data) => HResults.E_NOTIMPL; - public unsafe int GetMethodTableForEEClass(ulong eeClass, ulong* value) => HResults.E_NOTIMPL; + public unsafe int GetMethodTableForEEClass(ulong eeClassReallyCanonMT, ulong* value) + { + if (eeClassReallyCanonMT == 0 || value == null) + return HResults.E_INVALIDARG; + + try + { + Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem; + Contracts.MethodTableHandle methodTableHandle = contract.GetMethodTableHandle(eeClassReallyCanonMT); + *value = methodTableHandle.Address; + return HResults.S_OK; + } + catch (Exception ex) + { + return ex.HResult; + } + } public unsafe int GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded) => HResults.E_NOTIMPL; public unsafe int GetMethodTableSlot(ulong mt, uint slot, ulong* value) => HResults.E_NOTIMPL; public unsafe int GetMethodTableTransparencyData(ulong mt, void* data) => HResults.E_NOTIMPL; diff --git a/src/native/managed/cdacreader/src/Target.cs b/src/native/managed/cdacreader/src/Target.cs index d42f581581884f..06b201d3957d55 100644 --- a/src/native/managed/cdacreader/src/Target.cs +++ b/src/native/managed/cdacreader/src/Target.cs @@ -11,7 +11,7 @@ namespace Microsoft.Diagnostics.DataContractReader; -public readonly struct TargetPointer +public readonly struct TargetPointer : IEquatable { public static TargetPointer Null = new(0); @@ -20,6 +20,14 @@ public readonly struct TargetPointer public static implicit operator ulong(TargetPointer p) => p.Value; public static implicit operator TargetPointer(ulong v) => new TargetPointer(v); + + public static bool operator ==(TargetPointer left, TargetPointer right) => left.Value == right.Value; + public static bool operator !=(TargetPointer left, TargetPointer right) => left.Value != right.Value; + + public override bool Equals(object? obj) => obj is TargetPointer pointer && Equals(pointer); + public bool Equals(TargetPointer other) => Value == other.Value; + + public override int GetHashCode() => Value.GetHashCode(); } public readonly struct TargetNUInt @@ -309,6 +317,16 @@ private static bool TryReadNUInt(ulong address, Configuration config, Reader rea return false; } + public static bool IsAligned(ulong value, int alignment) + => (value & (ulong)(alignment - 1)) == 0; + + public bool IsAlignedToPointerSize(uint value) + => IsAligned(value, _config.PointerSize); + public bool IsAlignedToPointerSize(ulong value) + => IsAligned(value, _config.PointerSize); + public bool IsAlignedToPointerSize(TargetPointer pointer) + => IsAligned(pointer.Value, _config.PointerSize); + public T ReadGlobal(string name) where T : struct, INumber => ReadGlobal(name, out _); @@ -384,7 +402,7 @@ public T GetOrAdd(TargetPointer address) where T : IData return result!; } - private bool TryGet(ulong address, [NotNullWhen(true)] out T? data) + public bool TryGet(ulong address, [NotNullWhen(true)] out T? data) { data = default; if (!_readDataByAddress.TryGetValue((address, typeof(T)), out object? dataObj)) diff --git a/src/native/managed/cdacreader/src/cdacreader.csproj b/src/native/managed/cdacreader/src/cdacreader.csproj index 20ecd197c70460..1c973cac51a571 100644 --- a/src/native/managed/cdacreader/src/cdacreader.csproj +++ b/src/native/managed/cdacreader/src/cdacreader.csproj @@ -15,6 +15,9 @@ + + + diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs new file mode 100644 index 00000000000000..dd451fa075f6ae --- /dev/null +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -0,0 +1,376 @@ +// 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.Text; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests; + +public unsafe class MethodTableTests +{ + const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; + const ulong TestFreeObjectMethodTableAddress = 0x00000000_7a0000a8; + + private static readonly Target.TypeInfo MethodTableTypeInfo = new() + { + Fields = { + { nameof(Data.MethodTable.MTFlags), new() { Offset = 4, Type = DataType.uint32}}, + { nameof(Data.MethodTable.BaseSize), new() { Offset = 8, Type = DataType.uint32}}, + { nameof(Data.MethodTable.MTFlags2), new() { Offset = 12, Type = DataType.uint32}}, + { nameof(Data.MethodTable.EEClassOrCanonMT), new () { Offset = 16, Type = DataType.nuint}}, + { nameof(Data.MethodTable.Module), new () { Offset = 24, Type = DataType.pointer}}, + { nameof(Data.MethodTable.ParentMethodTable), new () { Offset = 40, Type = DataType.pointer}}, + { nameof(Data.MethodTable.NumInterfaces), new () { Offset = 48, Type = DataType.uint16}}, + { nameof(Data.MethodTable.NumVirtuals), new () { Offset = 50, Type = DataType.uint16}}, + } + }; + + private static readonly Target.TypeInfo EEClassTypeInfo = new Target.TypeInfo() + { + Fields = { + { nameof (Data.EEClass.MethodTable), new () { Offset = 8, Type = DataType.pointer}}, + { nameof (Data.EEClass.CorTypeAttr), new () { Offset = 16, Type = DataType.uint32}}, + { nameof (Data.EEClass.NumMethods), new () { Offset = 20, Type = DataType.uint16}}, + } + }; + + private static readonly (DataType Type, Target.TypeInfo Info)[] RTSTypes = + [ + (DataType.MethodTable, MethodTableTypeInfo), + (DataType.EEClass, EEClassTypeInfo), + ]; + + + private static readonly (string Name, ulong Value, string? Type)[] RTSGlobals = + [ + (nameof(Constants.Globals.FreeObjectMethodTable), TestFreeObjectMethodTableGlobalAddress, null), + ]; + + private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + { + MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of Free Object Method Table", Address = TestFreeObjectMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; + targetTestHelpers.WritePointer(globalAddr.Data, TestFreeObjectMethodTableAddress); + return builder.AddHeapFragments([ + globalAddr, + new () { Name = "Free Object Method Table", Address = TestFreeObjectMethodTableAddress, Data = new byte[targetTestHelpers.SizeOfTypeInfo(MethodTableTypeInfo)] } + ]); + } + + private static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods) + { + MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(EEClassTypeInfo)] }; + Span dest = eeClassFragment.Data; + targetTestHelpers.WritePointer(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); + targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); + targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); + return builder.AddHeapFragment(eeClassFragment); + + } + + private static MockMemorySpace.Builder AddMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, + TargetPointer module, TargetPointer parentMethodTable, ushort numInterfaces, ushort numVirtuals) + { + MockMemorySpace.HeapFragment methodTableFragment = new() { Name = $"MethodTable '{name}'", Address = methodTablePtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(MethodTableTypeInfo)] }; + Span dest = methodTableFragment.Data; + targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset), eeClassOrCanonMT); + targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags)].Offset), mtflags); + targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags2)].Offset), mtflags2); + targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.BaseSize)].Offset), baseSize); + targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.Module)].Offset), module); + targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.ParentMethodTable)].Offset), parentMethodTable); + targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.NumInterfaces)].Offset), numInterfaces); + targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.NumVirtuals)].Offset), numVirtuals); + + // TODO fill in the rest of the fields + return builder.AddHeapFragment(methodTableFragment); + } + + // a delegate for adding more heap fragments to the context builder + private delegate MockMemorySpace.Builder ConfigureContextBuilder(MockMemorySpace.Builder builder); + + private static void RTSContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action testCase) + { + TargetTestHelpers targetTestHelpers = new(arch); + string metadataTypesJson = TargetTestHelpers.MakeTypesJson(RTSTypes); + string metadataGlobalsJson = TargetTestHelpers.MakeGlobalsJson(RTSGlobals); + byte[] json = Encoding.UTF8.GetBytes($$""" + { + "version": 0, + "baseline": "empty", + "contracts": { + "{{nameof(Contracts.RuntimeTypeSystem)}}": 1 + }, + "types": { {{metadataTypesJson}} }, + "globals": { {{metadataGlobalsJson}} } + } + """); + Span descriptor = stackalloc byte[targetTestHelpers.ContractDescriptorSize]; + targetTestHelpers.ContractDescriptorFill(descriptor, json.Length, RTSGlobals.Length); + + int pointerSize = targetTestHelpers.PointerSize; + Span pointerData = stackalloc byte[RTSGlobals.Length * pointerSize]; + for (int i = 0; i < RTSGlobals.Length; i++) + { + var (_, value, _) = RTSGlobals[i]; + targetTestHelpers.WritePointer(pointerData.Slice(i * pointerSize), value); + } + + fixed (byte* jsonPtr = json) + { + MockMemorySpace.Builder builder = new(); + + builder = builder.SetDescriptor(descriptor) + .SetJson(json) + .SetPointerData(pointerData); + + builder = AddFreeObjectMethodTable(targetTestHelpers, builder); + + if (configure != null) + { + builder = configure(builder); + } + + using MockMemorySpace.ReadContext context = builder.Create(); + + bool success = MockMemorySpace.TryCreateTarget(&context, out Target? target); + Assert.True(success); + + testCase(target); + } + GC.KeepAlive(json); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) + { + RTSContractHelper(arch, default, (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Contracts.MethodTableHandle handle = metadataContract.GetMethodTableHandle(TestFreeObjectMethodTableAddress); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(metadataContract.IsFreeObjectMethodTable(handle)); + }); + } + + private static MockMemorySpace.Builder AddSystemObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) + { + System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; + const int numMethods = 8; // System.Object has 8 methods + const int numVirtuals = 3; // System.Object has 3 virtual methods + builder = AddEEClass(targetTestHelpers, builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods); + builder = AddMethodTable(targetTestHelpers, builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, + mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); + return builder; + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) + { + const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; + const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; + TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); + TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + TargetTestHelpers targetTestHelpers = new(arch); + RTSContractHelper(arch, + (builder) => + { + builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + return builder; + }, + (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Contracts.MethodTableHandle systemObjectMethodTableHandle = metadataContract.GetMethodTableHandle(systemObjectMethodTablePtr); + Assert.Equal(systemObjectMethodTablePtr.Value, systemObjectMethodTableHandle.Address.Value); + Assert.False(metadataContract.IsFreeObjectMethodTable(systemObjectMethodTableHandle)); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) + { + const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; + const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; + TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); + TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + + const ulong SystemStringMethodTableAddress = 0x00000000_7c002010; + const ulong SystemStringEEClassAddress = 0x00000000_7c0020d0; + TargetPointer systemStringMethodTablePtr = new TargetPointer(SystemStringMethodTableAddress); + TargetPointer systemStringEEClassPtr = new TargetPointer(SystemStringEEClassAddress); + TargetTestHelpers targetTestHelpers = new(arch); + RTSContractHelper(arch, + (builder) => + { + builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed; + const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String + const int numInterfaces = 8; // Arbitrary + const int numVirtuals = 3; // at least as many as System.Object + uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; + builder = AddEEClass(targetTestHelpers, builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods); + builder = AddMethodTable(targetTestHelpers, builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, + mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize, + module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + return builder; + }, + (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Contracts.MethodTableHandle systemStringMethodTableHandle = metadataContract.GetMethodTableHandle(systemStringMethodTablePtr); + Assert.Equal(systemStringMethodTablePtr.Value, systemStringMethodTableHandle.Address.Value); + Assert.False(metadataContract.IsFreeObjectMethodTable(systemStringMethodTableHandle)); + Assert.True(metadataContract.IsString(systemStringMethodTableHandle)); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) + { + TargetTestHelpers targetTestHelpers = new(arch); + const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; + const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; + TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); + TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + + const ulong badMethodTableAddress = 0x00000000_4a000100; // place a normal-looking MethodTable here + const ulong badMethodTableEEClassAddress = 0x00000010_afafafafa0; // bad address + TargetPointer badMethodTablePtr = new TargetPointer(badMethodTableAddress); + TargetPointer badMethodTableEEClassPtr = new TargetPointer(badMethodTableEEClassAddress); + RTSContractHelper(arch, + (builder) => + { + builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddMethodTable(targetTestHelpers, builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); + return builder; + }, + (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Assert.Throws(() => metadataContract.GetMethodTableHandle(badMethodTablePtr)); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) + { + TargetTestHelpers targetTestHelpers = new(arch); + const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; + const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; + TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); + TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + + const ulong genericDefinitionMethodTableAddress = 0x00000000_5d004040; + const ulong genericDefinitionEEClassAddress = 0x00000000_5d0040c0; + TargetPointer genericDefinitionMethodTablePtr = new TargetPointer(genericDefinitionMethodTableAddress); + TargetPointer genericDefinitionEEClassPtr = new TargetPointer(genericDefinitionEEClassAddress); + + const ulong genericInstanceMethodTableAddress = 0x00000000_330000a0; + TargetPointer genericInstanceMethodTablePtr = new TargetPointer(genericInstanceMethodTableAddress); + + const int numMethods = 17; + + RTSContractHelper(arch, + (builder) => + { + builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + + System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; + const int numInterfaces = 0; + const int numVirtuals = 3; + const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst + builder = AddEEClass(targetTestHelpers, builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods); + builder = AddMethodTable(targetTestHelpers, builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, + mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + + const uint ginst_mtflags = 0x00000010; // TODO: GenericsMask_GenericInst + TargetPointer ginstCanonMT = new TargetPointer(genericDefinitionMethodTablePtr.Value | (ulong)1); + builder = AddMethodTable(targetTestHelpers, builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, + mtflags: ginst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: genericDefinitionMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + + return builder; + }, + (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Contracts.MethodTableHandle genericInstanceMethodTableHandle = metadataContract.GetMethodTableHandle(genericInstanceMethodTablePtr); + Assert.Equal(genericInstanceMethodTablePtr.Value, genericInstanceMethodTableHandle.Address.Value); + Assert.False(metadataContract.IsFreeObjectMethodTable(genericInstanceMethodTableHandle)); + Assert.False(metadataContract.IsString(genericInstanceMethodTableHandle)); + Assert.Equal(numMethods, metadataContract.GetNumMethods(genericInstanceMethodTableHandle)); + }); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) + { + TargetTestHelpers targetTestHelpers = new(arch); + const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; + const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; + TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); + TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + + const ulong SystemArrayMethodTableAddress = 0x00000000_7c00a010; + const ulong SystemArrayEEClassAddress = 0x00000000_7c00a0d0; + TargetPointer systemArrayMethodTablePtr = new TargetPointer(SystemArrayMethodTableAddress); + TargetPointer systemArrayEEClassPtr = new TargetPointer(SystemArrayEEClassAddress); + + const ulong arrayInstanceMethodTableAddress = 0x00000000_330000a0; + const ulong arrayInstanceEEClassAddress = 0x00000000_330001d0; + TargetPointer arrayInstanceMethodTablePtr = new TargetPointer(arrayInstanceMethodTableAddress); + TargetPointer arrayInstanceEEClassPtr = new TargetPointer(arrayInstanceEEClassAddress); + + const uint arrayInstanceComponentSize = 392; + + RTSContractHelper(arch, + (builder) => + { + builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + const ushort systemArrayNumInterfaces = 4; + const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array + const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); + + builder = AddEEClass(targetTestHelpers, builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods); + builder = AddMethodTable(targetTestHelpers, builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, + mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); + + const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; + const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); + + builder = AddEEClass(targetTestHelpers, builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods); + builder = AddMethodTable(targetTestHelpers, builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, + mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); + + return builder; + }, + (target) => + { + Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; + Assert.NotNull(metadataContract); + Contracts.MethodTableHandle arrayInstanceMethodTableHandle = metadataContract.GetMethodTableHandle(arrayInstanceMethodTablePtr); + Assert.Equal(arrayInstanceMethodTablePtr.Value, arrayInstanceMethodTableHandle.Address.Value); + Assert.False(metadataContract.IsFreeObjectMethodTable(arrayInstanceMethodTableHandle)); + Assert.False(metadataContract.IsString(arrayInstanceMethodTableHandle)); + Assert.Equal(arrayInstanceComponentSize, metadataContract.GetComponentSize(arrayInstanceMethodTableHandle)); + }); + + } +} diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.cs new file mode 100644 index 00000000000000..c74771a7f5b5c3 --- /dev/null +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.cs @@ -0,0 +1,257 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests; + +/// +/// Helper for creating a mock memory space for testing. +/// +/// +/// Use MockMemorySpace.CreateContext to create a mostly empty context for reading from the target. +/// Use MockMemorySpace.ContextBuilder to create a context with additional MockMemorySpace.HeapFragment data. +/// +/// +/// All the spans should be stackalloc or pinned while the context is being used. +/// +internal unsafe static class MockMemorySpace +{ + internal const ulong ContractDescriptorAddr = 0xaaaaaaaa; + internal const uint JsonDescriptorAddr = 0xdddddddd; + internal const uint ContractPointerDataAddr = 0xeeeeeeee; + + + internal struct HeapFragment + { + public ulong Address; + public byte[] Data; + public string? Name; + } + + /// + /// Helper to populate a virtual memory space for reading from a target. + /// + /// + /// All the spans should be stackalloc or pinned while the context is being used. + /// + internal unsafe ref struct Builder + { + private bool _created = false; + private byte* _descriptor = null; + private int _descriptorLength = 0; + private byte* _json = null; + private int _jsonLength = 0; + private byte* _pointerData = null; + private int _pointerDataLength = 0; + private List _heapFragments = new(); + + public Builder() + { + + } + + public Builder SetDescriptor(scoped ReadOnlySpan descriptor) + { + if (_created) + throw new InvalidOperationException("Context already created"); + _descriptor = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(descriptor)); + _descriptorLength = descriptor.Length; + return this; + } + + public Builder SetJson(scoped ReadOnlySpan json) + { + if (_created) + throw new InvalidOperationException("Context already created"); + _json = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(json)); + _jsonLength = json.Length; + return this; + } + + public Builder SetPointerData(scoped ReadOnlySpan pointerData) + { + if (_created) + throw new InvalidOperationException("Context already created"); + if (pointerData.Length >= 0) + { + _pointerData = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pointerData)); + _pointerDataLength = pointerData.Length; + } + return this; + } + + public Builder AddHeapFragment(HeapFragment fragment) + { + if (_created) + throw new InvalidOperationException("Context already created"); + if (fragment.Data is null || fragment.Data.Length == 0) + throw new InvalidOperationException($"Fragment '{fragment.Name}' data is empty"); + if (!FragmentFits(fragment)) + throw new InvalidOperationException($"Fragment '{fragment.Name}' does not fit in the address space"); + _heapFragments.Add(fragment); + return this; + } + + public Builder AddHeapFragments(IEnumerable fragments) + { + foreach (var f in fragments) + { + // add fragments one at a time to check for overlaps + AddHeapFragment(f); + } + return this; + } + + public ReadContext Create() + { + if (_created) + throw new InvalidOperationException("Context already created"); + GCHandle fragmentReaderHandle = default; ; + if (_heapFragments.Count > 0) + { + fragmentReaderHandle = GCHandle.Alloc(new HeapFragmentReader(_heapFragments)); + } + ReadContext context = new ReadContext + { + ContractDescriptor = _descriptor, + ContractDescriptorLength = _descriptorLength, + JsonDescriptor = _json, + JsonDescriptorLength = _jsonLength, + PointerData = _pointerData, + PointerDataLength = _pointerDataLength, + HeapFragmentReader = GCHandle.ToIntPtr(fragmentReaderHandle) + }; + _created = true; + return context; + } + + private bool FragmentFits(HeapFragment f) + { + foreach (var fragment in _heapFragments) + { + // f and fragment overlap if either: + // 1. f starts before fragment starts and ends after fragment starts + // 2. f starts before fragment ends + if ((f.Address <= fragment.Address && f.Address + (ulong)f.Data.Length > fragment.Address) || + (f.Address >= fragment.Address && f.Address < fragment.Address + (ulong)fragment.Data.Length)) + { + return false; + } + + } + return true; + } + } + + // Note: all the spans should be stackalloc or pinned. + public static ReadContext CreateContext(ReadOnlySpan descriptor, ReadOnlySpan json, ReadOnlySpan pointerData = default) + { + Builder builder = new Builder() + .SetJson(json) + .SetDescriptor(descriptor) + .SetPointerData(pointerData); + return builder.Create(); + } + + public static bool TryCreateTarget(ReadContext* context, out Target? target) + { + return Target.TryCreate(ContractDescriptorAddr, &ReadFromTarget, context, out target); + } + + [UnmanagedCallersOnly] + private static int ReadFromTarget(ulong address, byte* buffer, uint length, void* context) + { + ReadContext* readContext = (ReadContext*)context; + var span = new Span(buffer, (int)length); + + // Populate the span with the requested portion of the contract descriptor + if (address >= ContractDescriptorAddr && address <= ContractDescriptorAddr + (ulong)readContext->ContractDescriptorLength - length) + { + ulong offset = address - ContractDescriptorAddr; + new ReadOnlySpan(readContext->ContractDescriptor + offset, (int)length).CopyTo(span); + return 0; + } + + // Populate the span with the JSON descriptor - this assumes the product will read it all at once. + if (address == JsonDescriptorAddr) + { + new ReadOnlySpan(readContext->JsonDescriptor, readContext->JsonDescriptorLength).CopyTo(span); + return 0; + } + + // Populate the span with the requested portion of the pointer data + if (address >= ContractPointerDataAddr && address <= ContractPointerDataAddr + (ulong)readContext->PointerDataLength - length) + { + ulong offset = address - ContractPointerDataAddr; + new ReadOnlySpan(readContext->PointerData + offset, (int)length).CopyTo(span); + return 0; + } + + HeapFragmentReader? heapFragmentReader = GCHandle.FromIntPtr(readContext->HeapFragmentReader).Target as HeapFragmentReader; + if (heapFragmentReader is not null) + { + return heapFragmentReader.ReadFragment(address, span); + } + + return -1; + } + + // Used by ReadFromTarget to return the appropriate bytes + internal ref struct ReadContext : IDisposable + { + public byte* ContractDescriptor; + public int ContractDescriptorLength; + + public byte* JsonDescriptor; + public int JsonDescriptorLength; + + public byte* PointerData; + public int PointerDataLength; + + public IntPtr HeapFragmentReader; + + public void Dispose() + { + if (HeapFragmentReader != IntPtr.Zero) + { + GCHandle.FromIntPtr(HeapFragmentReader).Free(); + HeapFragmentReader = IntPtr.Zero; + } + } + } + + private class HeapFragmentReader + { + private readonly IReadOnlyList _fragments; + public HeapFragmentReader(IReadOnlyList fragments) + { + _fragments = fragments; + } + + public int ReadFragment(ulong address, Span buffer) + { + foreach (var fragment in _fragments) + { + if (address >= fragment.Address && address < fragment.Address + (ulong)fragment.Data.Length) + { + int offset = (int)(address - fragment.Address); + int availableLength = fragment.Data.Length - offset; + if (availableLength >= buffer.Length) + { + fragment.Data.AsSpan(offset, buffer.Length).CopyTo(buffer); + return 0; + } + else + { + throw new InvalidOperationException($"Not enough data in fragment at {fragment.Address:X} ('{fragment.Name}') to read {buffer.Length} bytes at {address:X} (only {availableLength} bytes available)"); + } + } + } + return -1; + } + } +} diff --git a/src/native/managed/cdacreader/tests/MockTarget.cs b/src/native/managed/cdacreader/tests/MockTarget.cs new file mode 100644 index 00000000000000..5ef528139b7d57 --- /dev/null +++ b/src/native/managed/cdacreader/tests/MockTarget.cs @@ -0,0 +1,41 @@ +// 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.Generic; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests; + +public class MockTarget +{ + public struct Architecture + { + public bool IsLittleEndian { get; init; } + public bool Is64Bit { get; init; } + } + + /// + /// Xunit enumeration of standard test architectures + /// + /// + /// [Theory] + /// [ClassData(typeof(MockTarget.StdArch))] + /// public void TestMethod(MockTarget.Architecture arch) + /// { + /// ... + /// } + /// + public class StdArch : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return [new Architecture { IsLittleEndian = true, Is64Bit = true }]; + yield return [new Architecture { IsLittleEndian = true, Is64Bit = false }]; + yield return [new Architecture { IsLittleEndian = false, Is64Bit = true }]; + yield return [new Architecture { IsLittleEndian = false, Is64Bit = false }]; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + +} diff --git a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs new file mode 100644 index 00000000000000..df95785d828c91 --- /dev/null +++ b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs @@ -0,0 +1,251 @@ +// 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.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests; +internal unsafe class TargetTestHelpers +{ + public MockTarget.Architecture Arch { get; init; } + + public TargetTestHelpers(MockTarget.Architecture arch) + { + Arch = arch; + } + + public int PointerSize => Arch.Is64Bit ? sizeof(ulong) : sizeof(uint); + public int ContractDescriptorSize => ContractDescriptor.Size(Arch.Is64Bit); + + + #region Contract and data descriptor creation + + public void ContractDescriptorFill(Span dest, int jsonDescriptorSize, int pointerDataCount) + { + ContractDescriptor.Fill(dest, Arch, jsonDescriptorSize, pointerDataCount); + } + + internal static class ContractDescriptor + { + public static int Size(bool is64Bit) => is64Bit ? sizeof(ContractDescriptor64) : sizeof(ContractDescriptor32); + + public static void Fill(Span dest, MockTarget.Architecture arch, int jsonDescriptorSize, int pointerDataCount) + { + if (arch.Is64Bit) + { + ContractDescriptor64.Fill(dest, arch.IsLittleEndian, jsonDescriptorSize, pointerDataCount); + } + else + { + ContractDescriptor32.Fill(dest, arch.IsLittleEndian, jsonDescriptorSize, pointerDataCount); + } + } + + private struct ContractDescriptor32 + { + public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); + public uint Flags = 0x2 /*32-bit*/ | 0x1; + public uint DescriptorSize; + public uint Descriptor = MockMemorySpace.JsonDescriptorAddr; + public uint PointerDataCount; + public uint Pad0 = 0; + public uint PointerData = MockMemorySpace.ContractPointerDataAddr; + + public ContractDescriptor32() { } + + public static void Fill(Span dest, bool isLittleEndian, int jsonDescriptorSize, int pointerDataCount) + { + ContractDescriptor32 descriptor = new() + { + DescriptorSize = (uint)jsonDescriptorSize, + PointerDataCount = (uint)pointerDataCount, + }; + if (BitConverter.IsLittleEndian != isLittleEndian) + descriptor.ReverseEndianness(); + + MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); + } + + private void ReverseEndianness() + { + Magic = BinaryPrimitives.ReverseEndianness(Magic); + Flags = BinaryPrimitives.ReverseEndianness(Flags); + DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); + Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); + PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); + Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); + PointerData = BinaryPrimitives.ReverseEndianness(PointerData); + } + } + + private struct ContractDescriptor64 + { + public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); + public uint Flags = 0x1; + public uint DescriptorSize; + public ulong Descriptor = MockMemorySpace.JsonDescriptorAddr; + public uint PointerDataCount; + public uint Pad0 = 0; + public ulong PointerData = MockMemorySpace.ContractPointerDataAddr; + + public ContractDescriptor64() { } + + public static void Fill(Span dest, bool isLittleEndian, int jsonDescriptorSize, int pointerDataCount) + { + ContractDescriptor64 descriptor = new() + { + DescriptorSize = (uint)jsonDescriptorSize, + PointerDataCount = (uint)pointerDataCount, + }; + if (BitConverter.IsLittleEndian != isLittleEndian) + descriptor.ReverseEndianness(); + + MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); + } + + private void ReverseEndianness() + { + Magic = BinaryPrimitives.ReverseEndianness(Magic); + Flags = BinaryPrimitives.ReverseEndianness(Flags); + DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); + Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); + PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); + Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); + PointerData = BinaryPrimitives.ReverseEndianness(PointerData); + } + } + } + + #endregion Contract and data descriptor creation + + #region Data descriptor json formatting + private static string GetTypeJson(string name, Target.TypeInfo info) + { + string ret = string.Empty; + List fields = info.Size is null ? [] : [$"\"!\":{info.Size}"]; + fields.AddRange(info.Fields.Select(f => $"\"{f.Key}\":{(f.Value.TypeName is null ? f.Value.Offset : $"[{f.Value.Offset},\"{f.Value.TypeName}\"]")}")); + return $"\"{name}\":{{{string.Join(',', fields)}}}"; + } + + public static string MakeTypesJson(IEnumerable<(DataType Type, Target.TypeInfo Info)> types) + { + return string.Join(',', types.Select(t => GetTypeJson(t.Type.ToString(), t.Info))); + } + + public static string MakeGlobalsJson(IEnumerable<(string Name, ulong Value, string? Type)> globals) + { + return string.Join(',', globals.Select(i => $"\"{i.Name}\": {(i.Type is null ? i.Value.ToString() : $"[{i.Value}, \"{i.Type}\"]")}")); + } + + #endregion Data descriptor json formatting + + + + + #region Mock memory initialization + + internal uint ObjHeaderSize => (uint)(Arch.Is64Bit ? 2 * sizeof(uint) /*alignpad + syncblock*/: sizeof(uint) /* syncblock */); + internal uint ObjectSize => (uint)PointerSize /* methtab */; + + internal uint ObjectBaseSize => ObjHeaderSize + ObjectSize; + + internal uint ArrayBaseSize => Arch.Is64Bit ? ObjectSize + sizeof(uint) /* numComponents */ + sizeof(uint) /* pad*/ : ObjectSize + sizeof(uint) /* numComponents */; + + internal uint ArrayBaseBaseSize => ObjHeaderSize + ArrayBaseSize; + + internal uint StringBaseSize => ObjectBaseSize + sizeof(uint) /* length */ + sizeof(char) /* nul terminator */; + + internal void Write(Span dest, byte b) => dest[0] = b; + internal void Write(Span dest, ushort u) + { + if (Arch.IsLittleEndian) + { + BinaryPrimitives.WriteUInt16LittleEndian(dest, u); + } + else + { + BinaryPrimitives.WriteUInt16BigEndian(dest, u); + } + } + + internal void Write(Span dest, uint u) + { + if (Arch.IsLittleEndian) + { + BinaryPrimitives.WriteUInt32LittleEndian(dest, u); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(dest, u); + } + } + + internal void Write(Span dest, ulong u) + { + if (Arch.IsLittleEndian) + { + BinaryPrimitives.WriteUInt64LittleEndian(dest, u); + } + else + { + BinaryPrimitives.WriteUInt64BigEndian(dest, u); + } + } + + + internal void WritePointer(Span dest, ulong value) + { + if (Arch.Is64Bit) + { + if (Arch.IsLittleEndian) + { + BinaryPrimitives.WriteUInt64LittleEndian(dest, value); + } + else + { + BinaryPrimitives.WriteUInt64BigEndian(dest, value); + } + } + else + { + if (Arch.IsLittleEndian) + { + BinaryPrimitives.WriteUInt32LittleEndian(dest, (uint)value); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(dest, (uint)value); + } + } + } + + internal int SizeOfPrimitive(DataType type) + { + return type switch + { + DataType.uint8 or DataType.int8 => sizeof(byte), + DataType.uint16 or DataType.int16 => sizeof(ushort), + DataType.uint32 or DataType.int32 => sizeof(uint), + DataType.uint64 or DataType.int64 => sizeof(ulong), + DataType.pointer or DataType.nint or DataType.nuint => PointerSize, + _ => throw new InvalidOperationException($"Not a primitive: {type}"), + }; + } + + internal int SizeOfTypeInfo(Target.TypeInfo info) + { + int size = 0; + foreach (var (_, field) in info.Fields) + { + size = Math.Max(size, field.Offset + SizeOfPrimitive(field.Type)); + } + + return size; + } + + #endregion Mock memory initialization + +} diff --git a/src/native/managed/cdacreader/tests/TargetTests.cs b/src/native/managed/cdacreader/tests/TargetTests.cs index ec1899327b3fdf..56e36ec7dc2e20 100644 --- a/src/native/managed/cdacreader/tests/TargetTests.cs +++ b/src/native/managed/cdacreader/tests/TargetTests.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers.Binary; -using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using Xunit; @@ -14,12 +11,9 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public unsafe class TargetTests { - private const ulong ContractDescriptorAddr = 0xaaaaaaaa; - private const uint JsonDescriptorAddr = 0xdddddddd; - private const uint PointerDataAddr = 0xeeeeeeee; private static readonly (DataType Type, Target.TypeInfo Info)[] TestTypes = - [ + [ // Size and fields (DataType.Thread, new(){ Size = 56, @@ -41,35 +35,27 @@ private static readonly (DataType Type, Target.TypeInfo Info)[] TestTypes = ]; [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public void GetTypeInfo(bool isLittleEndian, bool is64Bit) + [ClassData(typeof(MockTarget.StdArch))] + public void GetTypeInfo(MockTarget.Architecture arch) { - string typesJson = string.Join(',', TestTypes.Select(t => GetTypeJson(t.Type.ToString(), t.Info))); + TargetTestHelpers targetTestHelpers = new(arch); + string typesJson = TargetTestHelpers.MakeTypesJson(TestTypes); byte[] json = Encoding.UTF8.GetBytes($$""" - { - "version": 0, - "baseline": "empty", - "contracts": {}, - "types": { {{typesJson}} }, - "globals": {} - } - """); - Span descriptor = stackalloc byte[ContractDescriptor.Size(is64Bit)]; - ContractDescriptor.Fill(descriptor, isLittleEndian, is64Bit, json.Length, 0); + { + "version": 0, + "baseline": "empty", + "contracts": {}, + "types": { {{typesJson}} }, + "globals": {} + } + """); + Span descriptor = stackalloc byte[targetTestHelpers.ContractDescriptorSize]; + targetTestHelpers.ContractDescriptorFill(descriptor, json.Length, 0); fixed (byte* jsonPtr = json) { - ReadContext context = new ReadContext - { - ContractDescriptor = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(descriptor)), - ContractDescriptorLength = descriptor.Length, - JsonDescriptor = jsonPtr, - JsonDescriptorLength = json.Length, - }; + using MockMemorySpace.ReadContext context = MockMemorySpace.CreateContext(descriptor, json); - bool success = Target.TryCreate(ContractDescriptorAddr, &ReadFromTarget, &context, out Target? target); + bool success = MockMemorySpace.TryCreateTarget(&context, out Target? target); Assert.True(success); foreach ((DataType type, Target.TypeInfo info) in TestTypes) @@ -88,14 +74,6 @@ public void GetTypeInfo(bool isLittleEndian, bool is64Bit) } } } - - static string GetTypeJson(string name, Target.TypeInfo info) - { - string ret = string.Empty; - List fields = info.Size is null ? [] : [$"\"!\":{info.Size}"]; - fields.AddRange(info.Fields.Select(f => $"\"{f.Key}\":{(f.Value.TypeName is null ? f.Value.Offset : $"[{f.Value.Offset},\"{f.Value.TypeName}\"]")}")); - return $"\"{name}\":{{{string.Join(',', fields)}}}"; - } } private static readonly (string Name, ulong Value, string? Type)[] TestGlobals = @@ -115,13 +93,11 @@ private static readonly (string Name, ulong Value, string? Type)[] TestGlobals = ]; [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public void ReadGlobalValue(bool isLittleEndian, bool is64Bit) + [ClassData(typeof(MockTarget.StdArch))] + public void ReadGlobalValue(MockTarget.Architecture arch) { - string globalsJson = string.Join(',', TestGlobals.Select(i => $"\"{i.Name}\": {(i.Type is null ? i.Value.ToString() : $"[{i.Value}, \"{i.Type}\"]")}")); + TargetTestHelpers targetTestHelpers = new(arch); + string globalsJson = TargetTestHelpers.MakeGlobalsJson(TestGlobals); byte[] json = Encoding.UTF8.GetBytes($$""" { "version": 0, @@ -131,19 +107,13 @@ public void ReadGlobalValue(bool isLittleEndian, bool is64Bit) "globals": { {{globalsJson}} } } """); - Span descriptor = stackalloc byte[ContractDescriptor.Size(is64Bit)]; - ContractDescriptor.Fill(descriptor, isLittleEndian, is64Bit, json.Length, 0); + Span descriptor = stackalloc byte[targetTestHelpers.ContractDescriptorSize]; + targetTestHelpers.ContractDescriptorFill(descriptor, json.Length, 0); fixed (byte* jsonPtr = json) { - ReadContext context = new ReadContext - { - ContractDescriptor = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(descriptor)), - ContractDescriptorLength = descriptor.Length, - JsonDescriptor = jsonPtr, - JsonDescriptorLength = json.Length, - }; + using MockMemorySpace.ReadContext context = MockMemorySpace.CreateContext(descriptor, json); - bool success = Target.TryCreate(ContractDescriptorAddr, &ReadFromTarget, &context, out Target? target); + bool success = MockMemorySpace.TryCreateTarget(&context, out Target? target); Assert.True(success); ValidateGlobals(target, TestGlobals); @@ -151,18 +121,16 @@ public void ReadGlobalValue(bool isLittleEndian, bool is64Bit) } [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public void ReadIndirectGlobalValue(bool isLittleEndian, bool is64Bit) + [ClassData(typeof(MockTarget.StdArch))] + public void ReadIndirectGlobalValue(MockTarget.Architecture arch) { - int pointerSize = is64Bit ? sizeof(ulong) : sizeof(uint); + TargetTestHelpers targetTestHelpers = new(arch); + int pointerSize = targetTestHelpers.PointerSize; Span pointerData = stackalloc byte[TestGlobals.Length * pointerSize]; for (int i = 0; i < TestGlobals.Length; i++) { var (_, value, _) = TestGlobals[i]; - WritePointer(pointerData.Slice(i * pointerSize), value, isLittleEndian, pointerSize); + targetTestHelpers.WritePointer(pointerData.Slice(i * pointerSize), value); } string globalsJson = string.Join(',', TestGlobals.Select((g, i) => $"\"{g.Name}\": {(g.Type is null ? $"[{i}]" : $"[[{i}], \"{g.Type}\"]")}")); @@ -175,25 +143,17 @@ public void ReadIndirectGlobalValue(bool isLittleEndian, bool is64Bit) "globals": { {{globalsJson}} } } """); - Span descriptor = stackalloc byte[ContractDescriptor.Size(is64Bit)]; - ContractDescriptor.Fill(descriptor, isLittleEndian, is64Bit, json.Length, pointerData.Length / pointerSize); + Span descriptor = stackalloc byte[targetTestHelpers.ContractDescriptorSize]; + targetTestHelpers.ContractDescriptorFill(descriptor, json.Length, pointerData.Length / pointerSize); fixed (byte* jsonPtr = json) { - ReadContext context = new ReadContext - { - ContractDescriptor = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(descriptor)), - ContractDescriptorLength = descriptor.Length, - JsonDescriptor = jsonPtr, - JsonDescriptorLength = json.Length, - PointerData = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pointerData)), - PointerDataLength = pointerData.Length - }; + using MockMemorySpace.ReadContext context = MockMemorySpace.CreateContext(descriptor, json, pointerData); - bool success = Target.TryCreate(ContractDescriptorAddr, &ReadFromTarget, &context, out Target? target); + bool success = MockMemorySpace.TryCreateTarget(&context, out Target? target); Assert.True(success); // Indirect values are pointer-sized, so max 32-bits for a 32-bit target - var expected = is64Bit + var expected = arch.Is64Bit ? TestGlobals : TestGlobals.Select(g => (g.Name, g.Value & 0xffffffff, g.Type)).ToArray(); @@ -201,32 +161,6 @@ public void ReadIndirectGlobalValue(bool isLittleEndian, bool is64Bit) } } - private static void WritePointer(Span dest, ulong value, bool isLittleEndian, int pointerSize) - { - if (pointerSize == sizeof(ulong)) - { - if (isLittleEndian) - { - BinaryPrimitives.WriteUInt64LittleEndian(dest, value); - } - else - { - BinaryPrimitives.WriteUInt64BigEndian(dest, value); - } - } - else if (pointerSize == sizeof(uint)) - { - if (isLittleEndian) - { - BinaryPrimitives.WriteUInt32LittleEndian(dest, (uint)value); - } - else - { - BinaryPrimitives.WriteUInt32BigEndian(dest, (uint)value); - } - } - } - private static void ValidateGlobals( Target target, (string Name, ulong Value, string? Type)[] globals, @@ -302,146 +236,10 @@ private static void ValidateGlobals( } } - void AssertEqualsWithCallerInfo(T expected, T actual) + void AssertEqualsWithCallerInfo(T expected, T actual) { Assert.True((expected is null && actual is null) || expected.Equals(actual), $"Expected: {expected}. Actual: {actual}. [test case: {caller} in {filePath}:{lineNumber}]"); } } - [UnmanagedCallersOnly] - private static int ReadFromTarget(ulong address, byte* buffer, uint length, void* context) - { - ReadContext* readContext = (ReadContext*)context; - var span = new Span(buffer, (int)length); - - // Populate the span with the requested portion of the contract descriptor - if (address >= ContractDescriptorAddr && address <= ContractDescriptorAddr + (ulong)readContext->ContractDescriptorLength - length) - { - ulong offset = address - ContractDescriptorAddr; - new ReadOnlySpan(readContext->ContractDescriptor + offset, (int)length).CopyTo(span); - return 0; - } - - // Populate the span with the JSON descriptor - this assumes the product will read it all at once. - if (address == JsonDescriptorAddr) - { - new ReadOnlySpan(readContext->JsonDescriptor, readContext->JsonDescriptorLength).CopyTo(span); - return 0; - } - - // Populate the span with the requested portion of the pointer data - if (address >= PointerDataAddr && address <= PointerDataAddr + (ulong)readContext->PointerDataLength - length) - { - ulong offset = address - PointerDataAddr; - new ReadOnlySpan(readContext->PointerData + offset, (int)length).CopyTo(span); - return 0; - } - - return -1; - } - - // Used by ReadFromTarget to return the appropriate bytes - private struct ReadContext - { - public byte* ContractDescriptor; - public int ContractDescriptorLength; - - public byte* JsonDescriptor; - public int JsonDescriptorLength; - - public byte* PointerData; - public int PointerDataLength; - } - - private static class ContractDescriptor - { - public static int Size(bool is64Bit) => is64Bit ? sizeof(ContractDescriptor64) : sizeof(ContractDescriptor32); - - public static void Fill(Span dest, bool isLittleEndian, bool is64Bit, int jsonDescriptorSize, int pointerDataCount) - { - if (is64Bit) - { - ContractDescriptor64.Fill(dest, isLittleEndian, jsonDescriptorSize, pointerDataCount); - } - else - { - ContractDescriptor32.Fill(dest, isLittleEndian, jsonDescriptorSize, pointerDataCount); - } - } - - private struct ContractDescriptor32 - { - public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); - public uint Flags = 0x2 /*32-bit*/ | 0x1; - public uint DescriptorSize; - public uint Descriptor = JsonDescriptorAddr; - public uint PointerDataCount; - public uint Pad0 = 0; - public uint PointerData = PointerDataAddr; - - public ContractDescriptor32() { } - - public static void Fill(Span dest, bool isLittleEndian, int jsonDescriptorSize, int pointerDataCount) - { - ContractDescriptor32 descriptor = new() - { - DescriptorSize = (uint)jsonDescriptorSize, - PointerDataCount = (uint)pointerDataCount, - }; - if (BitConverter.IsLittleEndian != isLittleEndian) - descriptor.ReverseEndianness(); - - MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); - } - - private void ReverseEndianness() - { - Magic = BinaryPrimitives.ReverseEndianness(Magic); - Flags = BinaryPrimitives.ReverseEndianness(Flags); - DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); - Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); - PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); - Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); - PointerData = BinaryPrimitives.ReverseEndianness(PointerData); - } - } - - private struct ContractDescriptor64 - { - public ulong Magic = BitConverter.ToUInt64("DNCCDAC\0"u8); - public uint Flags = 0x1; - public uint DescriptorSize; - public ulong Descriptor = JsonDescriptorAddr; - public uint PointerDataCount; - public uint Pad0 = 0; - public ulong PointerData = PointerDataAddr; - - public ContractDescriptor64() { } - - public static void Fill(Span dest, bool isLittleEndian, int jsonDescriptorSize, int pointerDataCount) - { - ContractDescriptor64 descriptor = new() - { - DescriptorSize = (uint)jsonDescriptorSize, - PointerDataCount = (uint)pointerDataCount, - }; - if (BitConverter.IsLittleEndian != isLittleEndian) - descriptor.ReverseEndianness(); - - MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref descriptor, 1)).CopyTo(dest); - } - - private void ReverseEndianness() - { - Magic = BinaryPrimitives.ReverseEndianness(Magic); - Flags = BinaryPrimitives.ReverseEndianness(Flags); - DescriptorSize = BinaryPrimitives.ReverseEndianness(DescriptorSize); - Descriptor = BinaryPrimitives.ReverseEndianness(Descriptor); - PointerDataCount = BinaryPrimitives.ReverseEndianness(PointerDataCount); - Pad0 = BinaryPrimitives.ReverseEndianness(Pad0); - PointerData = BinaryPrimitives.ReverseEndianness(PointerData); - } - } - } - } From 101c0daf5aa76451304704481a0d82d328498950 Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Mon, 8 Jul 2024 08:23:32 -0700 Subject: [PATCH 56/72] Remove unnecessary endianness dependent logic (#104332) * Remove unnecessary endianness logic * Revert back to the old more performant approach * Write 2 values once --- .../Text/Base64Helper/Base64EncoderHelper.cs | 66 +++----------- .../Text/Base64Url/Base64UrlEncoder.cs | 87 ++++++++----------- 2 files changed, 47 insertions(+), 106 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64EncoderHelper.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64EncoderHelper.cs index 252b892bed0ca0..4788384515df4d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64EncoderHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64EncoderHelper.cs @@ -628,6 +628,12 @@ private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap) uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F)); + return ConstructResult(i0, i1, i2, i3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint ConstructResult(uint i0, uint i1, uint i2, uint i3) + { if (BitConverter.IsLittleEndian) { return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24); @@ -677,20 +683,8 @@ public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10)); uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F)); - if (BitConverter.IsLittleEndian) - { - dest[0] = (byte)i0; - dest[1] = (byte)i1; - dest[2] = (byte)EncodingPad; - dest[3] = (byte)EncodingPad; - } - else - { - dest[3] = (byte)i0; - dest[2] = (byte)i1; - dest[1] = (byte)EncodingPad; - dest[0] = (byte)EncodingPad; - } + uint result = ConstructResult(i0, i1, EncodingPad, EncodingPad); + Unsafe.WriteUnaligned(dest, result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -705,20 +699,8 @@ public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byt uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - if (BitConverter.IsLittleEndian) - { - dest[0] = (byte)i0; - dest[1] = (byte)i1; - dest[2] = (byte)i2; - dest[3] = (byte)EncodingPad; - } - else - { - dest[3] = (byte)i0; - dest[2] = (byte)i1; - dest[1] = (byte)i2; - dest[0] = (byte)EncodingPad; - } + uint result = ConstructResult(i0, i1, i2, EncodingPad); + Unsafe.WriteUnaligned(dest, result); } #if NET @@ -757,33 +739,9 @@ public unsafe void StoreArmVector128x4ToDestination(byte* dest, byte* destStart, [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void EncodeThreeAndWrite(byte* threeBytes, byte* destination, ref byte encodingMap) { - uint t0 = threeBytes[0]; - uint t1 = threeBytes[1]; - uint t2 = threeBytes[2]; - - uint i = (t0 << 16) | (t1 << 8) | t2; - - byte i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); - byte i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); - byte i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - byte i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F)); - - if (BitConverter.IsLittleEndian) - { - destination[0] = i0; - destination[1] = i1; - destination[2] = i2; - destination[3] = i3; - } - else - { - destination[3] = i0; - destination[2] = i1; - destination[1] = i2; - destination[0] = i3; - } + uint result = Encode(threeBytes, ref encodingMap); + Unsafe.WriteUnaligned(destination, result); } } - } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Url/Base64UrlEncoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Url/Base64UrlEncoder.cs index d3185b37ca799b..675824b9b258b8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Url/Base64UrlEncoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Url/Base64UrlEncoder.cs @@ -251,19 +251,21 @@ public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte uint i = t0 << 8; - uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10)); - uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F)); + byte i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10)); + byte i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F)); + + ushort result; if (BitConverter.IsLittleEndian) { - dest[0] = (byte)i0; - dest[1] = (byte)i1; + result = (ushort)(i0 | (i1 << 8)); } else { - dest[1] = (byte)i0; - dest[0] = (byte)i1; + result = (ushort)((i0 << 8) | i1); } + + Unsafe.WriteUnaligned(dest, result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -274,22 +276,13 @@ public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byt uint i = (t0 << 16) | (t1 << 8); - uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); - uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); - uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); + byte i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); + byte i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); + byte i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - if (BitConverter.IsLittleEndian) - { - dest[0] = (byte)i0; - dest[1] = (byte)i1; - dest[2] = (byte)i2; - } - else - { - dest[2] = (byte)i0; - dest[1] = (byte)i1; - dest[0] = (byte)i2; - } + dest[0] = i0; + dest[1] = i1; + dest[2] = i2; } #if NET @@ -351,16 +344,18 @@ public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref by uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10)); uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F)); + uint result; + if (BitConverter.IsLittleEndian) { - dest[0] = (ushort)i0; - dest[1] = (ushort)i1; + result = (i0 | (i1 << 16)); } else { - dest[1] = (ushort)i0; - dest[0] = (ushort)i1; + result = ((i0 << 16) | i1); } + + Unsafe.WriteUnaligned(dest, result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -371,22 +366,13 @@ public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref b uint i = (t0 << 16) | (t1 << 8); - uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); - uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); - uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); + ushort i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); + ushort i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); + ushort i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - if (BitConverter.IsLittleEndian) - { - dest[0] = (ushort)i0; - dest[1] = (ushort)i1; - dest[2] = (ushort)i2; - } - else - { - dest[2] = (ushort)i0; - dest[1] = (ushort)i1; - dest[0] = (ushort)i2; - } + dest[0] = i0; + dest[1] = i1; + dest[2] = i2; } #if NET @@ -441,25 +427,22 @@ public unsafe void EncodeThreeAndWrite(byte* threeBytes, ushort* destination, re uint i = (t0 << 16) | (t1 << 8) | t2; - byte i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); - byte i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); - byte i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - byte i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F)); + ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18)); + ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); + ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); + ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F)); + ulong result; if (BitConverter.IsLittleEndian) { - destination[0] = i0; - destination[1] = i1; - destination[2] = i2; - destination[3] = i3; + result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48); } else { - destination[3] = i0; - destination[2] = i1; - destination[1] = i2; - destination[0] = i3; + result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3; } + + Unsafe.WriteUnaligned(destination, result); } } } From 1c51cf05801868ca3511ed333eb648a0d30dc252 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 9 Jul 2024 00:06:40 +0800 Subject: [PATCH 57/72] Delete dead AVX detection code in vm (#104544) --- src/coreclr/vm/cgensys.h | 5 --- src/coreclr/vm/i386/cgenx86.cpp | 78 --------------------------------- 2 files changed, 83 deletions(-) diff --git a/src/coreclr/vm/cgensys.h b/src/coreclr/vm/cgensys.h index a493ab4ab4c68e..5cb30c18608380 100644 --- a/src/coreclr/vm/cgensys.h +++ b/src/coreclr/vm/cgensys.h @@ -71,11 +71,6 @@ extern "C" void STDCALL DelayLoad_Helper_Obj(); extern "C" void STDCALL DelayLoad_Helper_ObjObj(); #endif -#if (defined(TARGET_X86) || defined(TARGET_AMD64)) -extern "C" DWORD xmmYmmStateSupport(); -extern "C" DWORD avx512StateSupport(); -#endif - #ifdef DACCESS_COMPILE // Used by dac/strike to make sense of non-jit/non-jit-helper call targets diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index 69e2fe9e5360b0..15ddb6f6d79524 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -995,84 +995,6 @@ void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP) #endif // !FEATURE_METADATA_UPDATER -#ifndef TARGET_UNIX -#pragma warning(push) -#pragma warning(disable: 4035) -extern "C" DWORD xmmYmmStateSupport() -{ - // No CONTRACT - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - - __asm - { - mov ecx, 0 ; Specify xcr0 - xgetbv ; result in EDX:EAX - and eax, 06H - cmp eax, 06H ; check OS has enabled both XMM and YMM state support - jne not_supported - mov eax, 1 - jmp done - not_supported: - mov eax, 0 - done: - } -} -#pragma warning(pop) - -#pragma warning(push) -#pragma warning(disable: 4035) -extern "C" DWORD avx512StateSupport() -{ - // No CONTRACT - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - - __asm - { - mov ecx, 0 ; Specify xcr0 - xgetbv ; result in EDX:EAX - and eax, 0E6H - cmp eax, 0E6H ; check OS has enabled XMM, YMM and ZMM state support - jne not_supported - mov eax, 1 - jmp done - not_supported: - mov eax, 0 - done: - } -} -#pragma warning(pop) - - -#else // !TARGET_UNIX - -extern "C" DWORD xmmYmmStateSupport() -{ - DWORD eax; - __asm(" xgetbv\n" \ - : "=a"(eax) /*output in eax*/\ - : "c"(0) /*inputs - 0 in ecx*/\ - : "edx" /* registers that are clobbered*/ - ); - // check OS has enabled both XMM and YMM state support - return ((eax & 0x06) == 0x06) ? 1 : 0; -} - -extern "C" DWORD avx512StateSupport() -{ - DWORD eax; - __asm(" xgetbv\n" \ - : "=a"(eax) /*output in eax*/\ - : "c"(0) /*inputs - 0 in ecx*/\ - : "edx" /* registers that are clobbered*/ - ); - // check OS has enabled XMM, YMM and ZMM state support - return ((eax & 0x0E6) == 0x0E6) ? 1 : 0; -} - -#endif // !TARGET_UNIX - void UMEntryThunkCode::Encode(UMEntryThunkCode *pEntryThunkCodeRX, BYTE* pTargetCode, void* pvSecretParam) { LIMITED_METHOD_CONTRACT; From 670d11f4d30d16153a3f5a667b1bb5b5ced9e0c9 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Mon, 8 Jul 2024 11:43:55 -0500 Subject: [PATCH 58/72] Use ConcurrentDictionary to avoid threading issues (#104407) --- ...System.ComponentModel.TypeConverter.csproj | 1 + .../ComponentModel/PropertyDescriptor.cs | 35 +++++++++----- .../ReflectTypeDescriptionProvider.cs | 48 ++++++------------- .../System/ComponentModel/TypeDescriptor.cs | 5 +- .../tests/PropertyDescriptorTests.cs | 38 +++++++++++---- 5 files changed, 69 insertions(+), 58 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index 588c6a970ae5ed..6979742610c466 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -250,6 +250,7 @@ + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index 23d05036265ec8..30d6a262e31db2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Threading; namespace System.ComponentModel { @@ -16,10 +18,11 @@ public abstract class PropertyDescriptor : MemberDescriptor internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered."; private TypeConverter? _converter; - private Dictionary? _valueChangedHandlers; + private ConcurrentDictionary? _valueChangedHandlers; private object?[]? _editors; private Type[]? _editorTypes; private int _editorCount; + private object? _syncObject; /// /// Initializes a new instance of the class with the specified name and @@ -49,6 +52,8 @@ protected PropertyDescriptor(MemberDescriptor descr, Attribute[]? attrs) : base( { } + private object SyncObject => LazyInitializer.EnsureInitialized(ref _syncObject); + /// /// When overridden in a derived class, gets the type of the /// component this property is bound to. @@ -165,10 +170,11 @@ public virtual void AddValueChanged(object component, EventHandler handler) ArgumentNullException.ThrowIfNull(component); ArgumentNullException.ThrowIfNull(handler); - _valueChangedHandlers ??= new Dictionary(); - - EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); - _valueChangedHandlers[component] = (EventHandler?)Delegate.Combine(h, handler); + lock (SyncObject) + { + _valueChangedHandlers ??= new ConcurrentDictionary(concurrencyLevel: 1, capacity: 0); + _valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler)); + } } /// @@ -433,15 +439,18 @@ public virtual void RemoveValueChanged(object component, EventHandler handler) if (_valueChangedHandlers != null) { - EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); - h = (EventHandler?)Delegate.Remove(h, handler); - if (h != null) - { - _valueChangedHandlers[component] = h; - } - else + lock (SyncObject) { - _valueChangedHandlers.Remove(component); + EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null); + h = (EventHandler?)Delegate.Remove(h, handler); + if (h != null) + { + _valueChangedHandlers[component] = h; + } + else + { + _valueChangedHandlers.Remove(component, out EventHandler? _); + } } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 531989154e4c6a..d7c74e719d36ce 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Collections.Concurrent; using System.ComponentModel.Design; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -23,7 +24,7 @@ namespace System.ComponentModel internal sealed partial class ReflectTypeDescriptionProvider : TypeDescriptionProvider { // ReflectedTypeData contains all of the type information we have gathered for a given type. - private Dictionary? _typeData; + private readonly ConcurrentDictionary _typeData = new ConcurrentDictionary(); // This is the signature we look for when creating types that are generic, but // want to know what type they are dealing with. Enums are a good example of this; @@ -281,8 +282,7 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table) public override bool? RequireRegisteredTypes => true; public override bool IsRegisteredType(Type type) { - if (_typeData != null && - _typeData.TryGetValue(type, out ReflectedTypeData? data) && + if (_typeData.TryGetValue(type, out ReflectedTypeData? data) && data.IsRegistered) { return true; @@ -862,18 +862,11 @@ internal Type[] GetPopulatedTypes(Module module) { List typeList = new List(); - lock (TypeDescriptor.s_commonSyncObject) + foreach (KeyValuePair kvp in _typeData) { - Dictionary? typeData = _typeData; - if (typeData != null) + if (kvp.Key.Module == module && kvp.Value!.IsPopulated) { - foreach (KeyValuePair kvp in typeData) - { - if (kvp.Key.Module == module && kvp.Value!.IsPopulated) - { - typeList.Add(kvp.Key); - } - } + typeList.Add(kvp.Key); } } @@ -927,9 +920,7 @@ public override Type GetReflectionType( /// private ReflectedTypeData? GetTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool createIfNeeded) { - ReflectedTypeData? td = null; - - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out ReflectedTypeData? td)) { Debug.Assert(td != null); return td; @@ -937,7 +928,7 @@ public override Type GetReflectionType( lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out td)) { Debug.Assert(td != null); @@ -958,7 +949,6 @@ public override Type GetReflectionType( if (createIfNeeded) { td = new ReflectedTypeData(type, isRegisteredType: false); - _typeData ??= new Dictionary(); _typeData[type] = td; } } @@ -968,7 +958,7 @@ public override Type GetReflectionType( private ReflectedTypeData GetTypeDataFromRegisteredType(Type type) { - if (_typeData == null || !_typeData.TryGetValue(type, out ReflectedTypeData? td)) + if (!_typeData.TryGetValue(type, out ReflectedTypeData? td)) { if (IsIntrinsicType(type)) { @@ -991,41 +981,34 @@ private ReflectedTypeData GetTypeDataFromRegisteredType(Type type) public override void RegisterType<[DynamicallyAccessedMembers(TypeDescriptor.RegisteredTypesDynamicallyAccessedMembers)] T>() { Type componentType = typeof(T); - ReflectedTypeData? td = null; - if (_typeData != null && _typeData.ContainsKey(componentType)) + if (_typeData.ContainsKey(componentType)) { return; } lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.ContainsKey(componentType)) + if (_typeData.ContainsKey(componentType)) { return; } - if (td == null) - { - td = new ReflectedTypeData(componentType, isRegisteredType: true); - _typeData ??= new Dictionary(); - _typeData[componentType] = td; - } + ReflectedTypeData td = new ReflectedTypeData(componentType, isRegisteredType: true); + _typeData[componentType] = td; } } private ReflectedTypeData GetOrRegisterType(Type type) { - ReflectedTypeData? td = null; - - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out ReflectedTypeData? td)) { return td; } lock (TypeDescriptor.s_commonSyncObject) { - if (_typeData != null && _typeData.TryGetValue(type, out td)) + if (_typeData.TryGetValue(type, out td)) { return td; } @@ -1033,7 +1016,6 @@ private ReflectedTypeData GetOrRegisterType(Type type) if (td == null) { td = new ReflectedTypeData(type, isRegisteredType: true); - _typeData ??= new Dictionary(); _typeData[type] = td; } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 78f34efbfb73a6..6b782a9fdda039 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel.Design; @@ -56,12 +57,12 @@ public sealed class TypeDescriptor internal static readonly object s_commonSyncObject = new object(); // A direct mapping from type to provider. - private static readonly Dictionary s_providerTypeTable = new Dictionary(); + private static readonly ConcurrentDictionary s_providerTypeTable = new ConcurrentDictionary(); // Tracks DefaultTypeDescriptionProviderAttributes. // A value of `null` indicates initialization is in progress. // A value of s_initializedDefaultProvider indicates the provider is initialized. - private static readonly Dictionary s_defaultProviderInitialized = new Dictionary(); + private static readonly ConcurrentDictionary s_defaultProviderInitialized = new ConcurrentDictionary(); private static readonly object s_initializedDefaultProvider = new object(); diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs index f791d46824f829..4786d3966a7e18 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/PropertyDescriptorTests.cs @@ -28,13 +28,21 @@ public void RaiseAddedValueChangedHandler() var component = new DescriptorTestComponent(); var properties = TypeDescriptor.GetProperties(component.GetType()); PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.Property), false); - var handlerWasCalled = false; - EventHandler valueChangedHandler = (_, __) => handlerWasCalled = true; + int handlerCalledCount = 0; - propertyDescriptor.AddValueChanged(component, valueChangedHandler); - propertyDescriptor.SetValue(component, int.MaxValue); + EventHandler valueChangedHandler1 = (_, __) => handlerCalledCount++; + EventHandler valueChangedHandler2 = (_, __) => handlerCalledCount++; + + propertyDescriptor.AddValueChanged(component, valueChangedHandler1); + + // Add case. + propertyDescriptor.SetValue(component, int.MaxValue); // Add to delegate. + Assert.Equal(1, handlerCalledCount); - Assert.True(handlerWasCalled); + + propertyDescriptor.AddValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); // Update delegate. + Assert.Equal(3, handlerCalledCount); } [Fact] @@ -42,15 +50,25 @@ public void RemoveAddedValueChangedHandler() { var component = new DescriptorTestComponent(); var properties = TypeDescriptor.GetProperties(component.GetType()); - var handlerWasCalled = false; - EventHandler valueChangedHandler = (_, __) => handlerWasCalled = true; + int handlerCalledCount = 0; + + EventHandler valueChangedHandler1 = (_, __) => handlerCalledCount++; + EventHandler valueChangedHandler2 = (_, __) => handlerCalledCount++; + PropertyDescriptor propertyDescriptor = properties.Find(nameof(component.Property), false); - propertyDescriptor.AddValueChanged(component, valueChangedHandler); - propertyDescriptor.RemoveValueChanged(component, valueChangedHandler); + propertyDescriptor.AddValueChanged(component, valueChangedHandler1); + propertyDescriptor.AddValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(2, handlerCalledCount); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(4, handlerCalledCount); - Assert.False(handlerWasCalled); + propertyDescriptor.RemoveValueChanged(component, valueChangedHandler1); + propertyDescriptor.RemoveValueChanged(component, valueChangedHandler2); + propertyDescriptor.SetValue(component, int.MaxValue); + Assert.Equal(4, handlerCalledCount); } [Fact] From 90d4c7d41a3fb3390cdcca598fc34cc6b986c75e Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 8 Jul 2024 17:57:59 +0100 Subject: [PATCH 59/72] Improve `JsonNode.DeepEquals` numeric equality. (#104255) * Attempt at improving JsonNode.DeepEquals numeric equality. * Implement arbitrary-precision decimal equality comparison. * Address feedback * Add more comments. * Update src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs Co-authored-by: Stephen Toub * Address feedback * Improve comments * Update src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs * Trim frac trailing zeros before trimming leading zeros. * Add handling for exponent values > Int32 --------- Co-authored-by: Stephen Toub --- .../src/Resources/Strings.resx | 3 + .../System/Text/Json/Document/JsonElement.cs | 4 +- .../src/System/Text/Json/JsonHelpers.cs | 238 ++++++++++++++++++ .../src/System/Text/Json/ThrowHelper.cs | 6 + .../JsonNode/JsonValueTests.cs | 98 ++++++++ 5 files changed, 347 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index b8791ea8fb171a..dc9458c6d705bb 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -246,6 +246,9 @@ The requested operation requires an element of type '{0}', but the target element has type '{1}'. + + The exponent value in the specified JSON number is too large. + Cannot add callbacks to the 'Modifiers' property after the resolver has been used for the first time. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index f5d2ff099e7233..0a08875c7c562f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Text; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -1241,8 +1242,7 @@ internal static bool DeepEquals(JsonElement left, JsonElement right) return true; case JsonValueKind.Number: - // JSON numbers are equal if their raw representations are equal. - return left.GetRawValue().Span.SequenceEqual(right.GetRawValue().Span); + return JsonHelpers.AreEqualJsonNumbers(left.GetRawValue().Span, right.GetRawValue().Span); case JsonValueKind.String: if (right.ValueIsEscaped) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index 2b189272f5d1e5..760d169f35cc78 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Buffers.Text; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -289,5 +290,242 @@ public static bool HasAllSet(this BitArray bitArray) #else private static Regex CreateIntegerRegex() => new(IntegerRegexPattern, RegexOptions.Compiled, TimeSpan.FromMilliseconds(IntegerRegexTimeoutMs)); #endif + + /// + /// Compares two valid UTF-8 encoded JSON numbers for decimal equality. + /// + public static bool AreEqualJsonNumbers(ReadOnlySpan left, ReadOnlySpan right) + { + Debug.Assert(left.Length > 0 && right.Length > 0); + + ParseNumber(left, + out bool leftIsNegative, + out ReadOnlySpan leftIntegral, + out ReadOnlySpan leftFractional, + out int leftExponent); + + ParseNumber(right, + out bool rightIsNegative, + out ReadOnlySpan rightIntegral, + out ReadOnlySpan rightFractional, + out int rightExponent); + + int nDigits; + if (leftIsNegative != rightIsNegative || + leftExponent != rightExponent || + (nDigits = (leftIntegral.Length + leftFractional.Length)) != + rightIntegral.Length + rightFractional.Length) + { + return false; + } + + if (leftIntegral.Length == rightIntegral.Length) + { + return leftIntegral.SequenceEqual(rightIntegral) && + leftFractional.SequenceEqual(rightFractional); + } + + // There is differentiation in the integral and fractional lengths, + // concatenate both into singular buffers and compare them. + scoped Span leftDigits; + scoped Span rightDigits; + byte[]? rentedLeftBuffer; + byte[]? rentedRightBuffer; + + if (nDigits <= JsonConstants.StackallocByteThreshold) + { + leftDigits = stackalloc byte[JsonConstants.StackallocByteThreshold]; + rightDigits = stackalloc byte[JsonConstants.StackallocByteThreshold]; + rentedLeftBuffer = rentedRightBuffer = null; + } + else + { + leftDigits = (rentedLeftBuffer = ArrayPool.Shared.Rent(nDigits)); + rightDigits = (rentedRightBuffer = ArrayPool.Shared.Rent(nDigits)); + } + + leftIntegral.CopyTo(leftDigits); + leftFractional.CopyTo(leftDigits.Slice(leftIntegral.Length)); + rightIntegral.CopyTo(rightDigits); + rightFractional.CopyTo(rightDigits.Slice(rightIntegral.Length)); + + bool result = leftDigits.Slice(0, nDigits).SequenceEqual(rightDigits.Slice(0, nDigits)); + + if (rentedLeftBuffer != null) + { + Debug.Assert(rentedRightBuffer != null); + rentedLeftBuffer.AsSpan(0, nDigits).Clear(); + rentedRightBuffer.AsSpan(0, nDigits).Clear(); + ArrayPool.Shared.Return(rentedLeftBuffer); + ArrayPool.Shared.Return(rentedRightBuffer); + } + + return result; + + static void ParseNumber( + ReadOnlySpan span, + out bool isNegative, + out ReadOnlySpan integral, + out ReadOnlySpan fractional, + out int exponent) + { + // Parses a JSON number into its integral, fractional, and exponent parts. + // The returned components use a normal-form decimal representation: + // + // Number := sign * * 10^exponent + // + // where integral and fractional are sequences of digits whose concatenation + // represents the significand of the number without leading or trailing zeros. + // Two such normal-form numbers are treated as equal if and only if they have + // equal signs, significands, and exponents. + + bool neg; + ReadOnlySpan intg; + ReadOnlySpan frac; + int exp; + + Debug.Assert(span.Length > 0); + + if (span[0] == '-') + { + neg = true; + span = span.Slice(1); + } + else + { + Debug.Assert(char.IsDigit((char)span[0]), "leading plus not allowed in valid JSON numbers."); + neg = false; + } + + int i = span.IndexOfAny((byte)'.', (byte)'e', (byte)'E'); + if (i < 0) + { + intg = span; + frac = default; + exp = 0; + goto Normalize; + } + + intg = span.Slice(0, i); + + if (span[i] == '.') + { + span = span.Slice(i + 1); + i = span.IndexOfAny((byte)'e', (byte)'E'); + if (i < 0) + { + frac = span; + exp = 0; + goto Normalize; + } + + frac = span.Slice(0, i); + } + else + { + frac = default; + } + + Debug.Assert(span[i] is (byte)'e' or (byte)'E'); + if (!Utf8Parser.TryParse(span.Slice(i + 1), out exp, out _)) + { + Debug.Assert(span.Length >= 10); + ThrowHelper.ThrowArgumentOutOfRangeException_JsonNumberExponentTooLarge(nameof(exponent)); + } + + Normalize: // Calculates the normal form of the number. + + if (IndexOfFirstTrailingZero(frac) is >= 0 and int iz) + { + // Trim trailing zeros from the fractional part. + // e.g. 3.1400 -> 3.14 + frac = frac.Slice(0, iz); + } + + if (intg[0] == '0') + { + Debug.Assert(intg.Length == 1, "Leading zeros not permitted in JSON numbers."); + + if (IndexOfLastLeadingZero(frac) is >= 0 and int lz) + { + // Trim leading zeros from the fractional part + // and update the exponent accordingly. + // e.g. 0.000123 -> 0.123e-3 + frac = frac.Slice(lz + 1); + exp -= lz + 1; + } + + // Normalize "0" to the empty span. + intg = default; + } + + if (frac.IsEmpty && IndexOfFirstTrailingZero(intg) is >= 0 and int fz) + { + // There is no fractional part, trim trailing zeros from + // the integral part and increase the exponent accordingly. + // e.g. 1000 -> 1e3 + exp += intg.Length - fz; + intg = intg.Slice(0, fz); + } + + // Normalize the exponent by subtracting the length of the fractional part. + // e.g. 3.14 -> 314e-2 + exp -= frac.Length; + + if (intg.IsEmpty && frac.IsEmpty) + { + // Normalize zero representations. + neg = false; + exp = 0; + } + + // Copy to out parameters. + isNegative = neg; + integral = intg; + fractional = frac; + exponent = exp; + + static int IndexOfLastLeadingZero(ReadOnlySpan span) + { +#if NET + int firstNonZero = span.IndexOfAnyExcept((byte)'0'); + return firstNonZero < 0 ? span.Length - 1 : firstNonZero - 1; +#else + for (int i = 0; i < span.Length; i++) + { + if (span[i] != '0') + { + return i - 1; + } + } + + return span.Length - 1; +#endif + } + + static int IndexOfFirstTrailingZero(ReadOnlySpan span) + { +#if NET + int lastNonZero = span.LastIndexOfAnyExcept((byte)'0'); + return lastNonZero == span.Length - 1 ? -1 : lastNonZero + 1; +#else + if (span.IsEmpty) + { + return -1; + } + + for (int i = span.Length - 1; i >= 0; i--) + { + if (span[i] != '0') + { + return i == span.Length - 1 ? -1 : i + 1; + } + } + + return 0; +#endif + } + } + } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index 6976d42b967bc9..fb1786be551dae 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -36,6 +36,12 @@ public static void ThrowArgumentOutOfRangeException_MaxDepthMustBePositive(strin throw GetArgumentOutOfRangeException(parameterName, SR.MaxDepthMustBePositive); } + [DoesNotReturn] + public static void ThrowArgumentOutOfRangeException_JsonNumberExponentTooLarge(string parameterName) + { + throw GetArgumentOutOfRangeException(parameterName, SR.JsonNumberExponentTooLarge); + } + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(string parameterName, string message) { return new ArgumentOutOfRangeException(parameterName, message); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs index 94505ff194149b..29ae9662eedd4b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs @@ -380,6 +380,104 @@ public static void DeepEqualsPrimitiveType() JsonNodeTests.AssertNotDeepEqual(JsonValue.Create(10), JsonValue.Create("10")); } + [Theory] + [InlineData("-0.0", "0")] + [InlineData("0", "0.0000e4")] + [InlineData("0", "0.0000e-4")] + [InlineData("1", "1.0")] + [InlineData("1", "1e0")] + [InlineData("1", "1.0000")] + [InlineData("1", "1.0000e0")] + [InlineData("1", "0.10000e1")] + [InlineData("1", "10.0000e-1")] + [InlineData("10001", "1.0001e4")] + [InlineData("10001e-3", "1.0001e1")] + [InlineData("1", "0.1e1")] + [InlineData("0.1", "1e-1")] + [InlineData("0.001", "1e-3")] + [InlineData("1e9", "1000000000")] + [InlineData("11", "1.100000000e1")] + [InlineData("3.141592653589793", "3141592653589793E-15")] + [InlineData("0.000000000000000000000000000000000000000001", "1e-42")] + [InlineData("1000000000000000000000000000000000000000000", "1e42")] + [InlineData("-1.1e3", "-1100")] + [InlineData("79228162514264337593543950336", "792281625142643375935439503360e-1")] // decimal.MaxValue + 1 + [InlineData("79228162514.264337593543950336", "792281625142643375935439503360e-19")] + [InlineData("1.75e+300", "1.75E+300")] // Variations in exponent casing + [InlineData( // > 256 digits + "1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" , + + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" + "E-512")] + public static void DeepEqualsNumericType(string leftStr, string rightStr) + { + JsonNode left = JsonNode.Parse(leftStr); + JsonNode right = JsonNode.Parse(rightStr); + + JsonNodeTests.AssertDeepEqual(left, right); + } + + [Theory] + [InlineData("0", "1")] + [InlineData("1", "-1")] + [InlineData("1.1", "-1.1")] + [InlineData("1.1e5", "-1.1e5")] + [InlineData("0", "1e-1024")] + [InlineData("1", "0.1")] + [InlineData("1", "1.1")] + [InlineData("1", "1e1")] + [InlineData("1", "1.00001")] + [InlineData("1", "1.0000e1")] + [InlineData("1", "0.1000e-1")] + [InlineData("1", "10.0000e-2")] + [InlineData("10001", "1.0001e3")] + [InlineData("10001e-3", "1.0001e2")] + [InlineData("1", "0.1e2")] + [InlineData("0.1", "1e-2")] + [InlineData("0.001", "1e-4")] + [InlineData("1e9", "1000000001")] + [InlineData("11", "1.100000001e1")] + [InlineData("0.000000000000000000000000000000000000000001", "1e-43")] + [InlineData("1000000000000000000000000000000000000000000", "1e43")] + [InlineData("-1.1e3", "-1100.1")] + [InlineData("79228162514264337593543950336", "7922816251426433759354395033600e-1")] // decimal.MaxValue + 1 + [InlineData("79228162514.264337593543950336", "7922816251426433759354395033601e-19")] + [InlineData("1.75e+300", "1.75E+301")] // Variations in exponent casing + [InlineData("1e2147483647", "1e-2147483648")] // int.MaxValue, int.MinValue exponents + [InlineData( // > 256 digits + "1.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + + "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003" + "E-512")] + public static void NotDeepEqualsNumericType(string leftStr, string rightStr) + { + JsonNode left = JsonNode.Parse(leftStr); + JsonNode right = JsonNode.Parse(rightStr); + + JsonNodeTests.AssertNotDeepEqual(left, right); + } + + [Theory] + [InlineData(int.MinValue - 1L)] + [InlineData(int.MaxValue + 1L)] + [InlineData(long.MinValue)] + [InlineData(long.MaxValue)] + public static void DeepEquals_ExponentExceedsInt32_ThrowsArgumentOutOfRangeException(long exponent) + { + JsonNode node = JsonNode.Parse($"1e{exponent}"); + Assert.Throws(() => JsonNode.DeepEquals(node, node)); + } + [Fact] public static void DeepEqualsJsonElement() { From 90bd757ba612e4081813ce9446c9a735471a7d0f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:44:59 +0200 Subject: [PATCH 60/72] [main] Update dependencies from dotnet/roslyn (#104363) * Update dependencies from https://github.com/dotnet/roslyn build 20240702.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.11.0-3.24352.5 * Update dependencies from https://github.com/dotnet/roslyn build 20240702.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.11.0-3.24352.5 * Update dependencies from https://github.com/dotnet/roslyn build 20240702.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.11.0-3.24352.5 * Update dependencies from https://github.com/dotnet/roslyn build 20240702.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.11.0-3.24352.5 * Update dependencies from https://github.com/dotnet/roslyn build 20240702.5 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.11.0-3.24352.5 * Update dependencies from https://github.com/dotnet/roslyn build 20240705.3 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.12.0-1.24355.3 * Update dependencies from https://github.com/dotnet/roslyn build 20240705.3 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.11.0-3.24329.1 -> To Version 4.12.0-1.24355.3 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6dd6e6920a7f14..458372435e0d6c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -360,17 +360,17 @@ https://github.com/dotnet/runtime-assets ec2da34cd7e31a605d6f30f02021a8d76947c99d - + https://github.com/dotnet/roslyn - 92051d4c24bc13ff58232104a647910bf22cd105 + 19b5e961ecb97b008106f1b646c077e0bffde4a7 - + https://github.com/dotnet/roslyn - 92051d4c24bc13ff58232104a647910bf22cd105 + 19b5e961ecb97b008106f1b646c077e0bffde4a7 - + https://github.com/dotnet/roslyn - 92051d4c24bc13ff58232104a647910bf22cd105 + 19b5e961ecb97b008106f1b646c077e0bffde4a7 https://github.com/dotnet/roslyn-analyzers @@ -381,9 +381,9 @@ 43709af7570da7140fb3e9a5237f55ffb24677e7 - + https://github.com/dotnet/roslyn - 92051d4c24bc13ff58232104a647910bf22cd105 + 19b5e961ecb97b008106f1b646c077e0bffde4a7 diff --git a/eng/Versions.props b/eng/Versions.props index 9d043a524df5cd..9c5aae615e1b31 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -42,9 +42,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.11.0-3.24329.1 - 4.11.0-3.24329.1 - 4.11.0-3.24329.1 + 4.12.0-1.24355.3 + 4.12.0-1.24355.3 + 4.12.0-1.24355.3 diff --git a/eng/native/configurecompiler.cmake b/eng/native/configurecompiler.cmake index 76c441de6f2f56..aedc4825273003 100644 --- a/eng/native/configurecompiler.cmake +++ b/eng/native/configurecompiler.cmake @@ -12,6 +12,11 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# We need to set this to Release as there's no way to intercept configuration-specific linker flags +# for try_compile-style tests (like check_c_source_compiles) and some of the default Debug flags +# (ie. /INCREMENTAL) conflict with our own flags. +set(CMAKE_TRY_COMPILE_CONFIGURATION Release) + include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckLinkerFlag) @@ -58,6 +63,7 @@ if (MSVC) define_property(TARGET PROPERTY CLR_CONTROL_FLOW_GUARD INHERITED BRIEF_DOCS "Controls the /guard:cf flag presence" FULL_DOCS "Set this property to ON or OFF to indicate if the /guard:cf compiler and linker flag should be present") define_property(TARGET PROPERTY CLR_EH_CONTINUATION INHERITED BRIEF_DOCS "Controls the /guard:ehcont flag presence" FULL_DOCS "Set this property to ON or OFF to indicate if the /guard:ehcont compiler flag should be present") define_property(TARGET PROPERTY CLR_EH_OPTION INHERITED BRIEF_DOCS "Defines the value of the /EH option" FULL_DOCS "Set this property to one of the valid /EHxx options (/EHa, /EHsc, /EHa-, ...)") + define_property(TARGET PROPERTY MSVC_WARNING_LEVEL INHERITED BRIEF_DOCS "Define the warning level for the /Wn option" FULL_DOCS "Set this property to one of the valid /Wn options (/W0, /W1, /W2, /W3, /W4)") set_property(GLOBAL PROPERTY CLR_CONTROL_FLOW_GUARD ON) @@ -779,7 +785,8 @@ if (MSVC) # [[! Microsoft.Security.SystemsADM.10086 !]] - SDL required warnings # set default warning level to 4 but allow targets to override it. - add_compile_options($<$:/W$>,$,4>>>) + set_property(GLOBAL PROPERTY MSVC_WARNING_LEVEL 4) + add_compile_options($<$:/W$>) add_compile_options($<$:/WX>) # treat warnings as errors add_compile_options($<$:/Oi>) # enable intrinsics add_compile_options($<$:/Oy->) # disable suppressing of the creation of frame pointers on the call stack for quicker function calls diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index 20851f8617423d..4589c63e7d5bf4 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -500,3 +500,10 @@ if(LOWERCASE_CMAKE_BUILD_TYPE STREQUAL debug) string(REPLACE "-D_FORTIFY_SOURCE=2 " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE "-D_FORTIFY_SOURCE=2 " "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") endif() + +if (CLR_CMAKE_TARGET_ANDROID OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS OR CLR_CMAKE_HOST_ARCH_ARMV6) + # Some platforms are opted-out from using the in-tree zlib-ng by default: + # - Android and iOS-like platforms: concerns about extra binary size + # - Armv6: zlib-ng has build breaks + set(CLR_CMAKE_USE_SYSTEM_ZLIB 1) +endif() diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake index 6629e926afacf6..b1cb09d6620436 100644 --- a/eng/native/functions.cmake +++ b/eng/native/functions.cmake @@ -221,7 +221,7 @@ function(preprocess_file inputFilename outputFilename) get_compile_definitions(PREPROCESS_DEFINITIONS) get_include_directories(PREPROCESS_INCLUDE_DIRECTORIES) get_source_file_property(SOURCE_FILE_DEFINITIONS ${inputFilename} COMPILE_DEFINITIONS) - + foreach(DEFINITION IN LISTS SOURCE_FILE_DEFINITIONS) list(APPEND PREPROCESS_DEFINITIONS -D${DEFINITION}) endforeach() @@ -508,7 +508,7 @@ function(install_static_library targetName destination component) if (WIN32) set_target_properties(${targetName} PROPERTIES COMPILE_PDB_NAME "${targetName}" - COMPILE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}" + COMPILE_PDB_OUTPUT_DIRECTORY "$" ) install (FILES "$/${targetName}.pdb" DESTINATION ${destination} COMPONENT ${component}) endif() diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index d3dc85e04f82e4..3ff044e94d9d2a 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -24,6 +24,7 @@ The .NET Foundation licenses this file to you under the MIT license. lld bfd 1572864 + true @@ -129,6 +130,7 @@ The .NET Foundation licenses this file to you under the MIT license. + @@ -188,7 +190,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets index 8035484bf04a53..7be88b5fde9912 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Windows.targets @@ -46,6 +46,7 @@ The .NET Foundation licenses this file to you under the MIT license. + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 0de334e7ff2113..33b474c4ee3495 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -277,6 +277,9 @@ + + + diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index ba41fa4d9a489f..fac1d9fb0e33ca 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -249,7 +249,6 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "emscripten") set(DISABLE_SHARED_LIBS 1) # sys/random.h exists, but its not found set(HAVE_SYS_RANDOM_H 1) - set(INTERNAL_ZLIB 1) elseif(CLR_CMAKE_HOST_OS STREQUAL "wasi") set(HOST_WASI 1) add_definitions(-D_WASI_EMULATED_PROCESS_CLOCKS -D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN -DHOST_WASI) @@ -261,7 +260,6 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "wasi") add_compile_options(-Wno-unused-but-set-variable) set(ENABLE_PERFTRACING 0) set(DISABLE_SHARED_LIBS 1) - set(INTERNAL_ZLIB 1) set(DISABLE_EXECUTABLES 1) set(STATIC_COMPONENTS 1) elseif(CLR_CMAKE_HOST_OS STREQUAL "windows") @@ -270,7 +268,6 @@ elseif(CLR_CMAKE_HOST_OS STREQUAL "windows") set(HOST_NO_SYMLINKS 1) set(MONO_KEYWORD_THREAD "__declspec (thread)") set(MONO_ZERO_LEN_ARRAY 1) - set(INTERNAL_ZLIB 1) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") # statically link VC runtime library add_compile_options($<$:/W4>) # set warning level 4 add_compile_options($<$:/WX>) # treat warnings as errors @@ -511,16 +508,15 @@ if ((HOST_MACCAT AND HOST_ARM64) OR (TARGET_MACCAT AND TARGET_ARM64)) set(TARGET_APPLE_MOBILE 1) endif() -# Decide if we need zlib, and if so whether we want the system zlib or the in-tree copy. +# Decide if we need zlib-ng. if(NOT DISABLE_EMBEDDED_PDB OR NOT DISABLE_LOG_PROFILER_GZ) - if(INTERNAL_ZLIB) - # defines ZLIB_SOURCES - include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) - else() - # if we're not on a platform where we use the in-tree zlib, require system zlib + if (CLR_CMAKE_USE_SYSTEM_ZLIB) + # if we're not on a platform where we use the in-tree zlib-ng, require system zlib include(${CLR_SRC_NATIVE_DIR}/libs/System.IO.Compression.Native/extra_libs.cmake) set(Z_LIBS) append_extra_compression_libs(Z_LIBS) + else() + include(${CLR_SRC_NATIVE_DIR}/external/zlib-ng.cmake) endif() endif() @@ -664,12 +660,17 @@ if(LLVM_PREFIX) endif() set(llvm_includedir "${LLVM_PREFIX}/include") + set(llvm_system_libs "") if(HOST_LINUX) # llvm-config --system-libs - set(llvm_system_libs ${MONO_cxx_lib} "-lz" "-lrt" "-ldl" "-lpthread" "-lm") + list(APPEND llvm_system_libs ${MONO_cxx_lib} "-lrt" "-ldl" "-lpthread" "-lm") elseif(HOST_OSX) # llvm-config --system-libs - set(llvm_system_libs "-lz" "-lm") + list(APPEND llvm_system_libs "-lm") + endif() + + if (CLR_CMAKE_USE_SYSTEM_ZLIB AND (HOST_LINUX OR HOST_OSX)) + list(APPEND llvm_system_libs "-lz") endif() # llvm-config --libs analysis core bitwriter mcjit orcjit diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 81768d43e6a536..47a351fb53b415 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -63,6 +63,7 @@ + diff --git a/src/mono/browser/runtime/CMakeLists.txt b/src/mono/browser/runtime/CMakeLists.txt index 8b7939330f8c3a..f707431fc38d55 100644 --- a/src/mono/browser/runtime/CMakeLists.txt +++ b/src/mono/browser/runtime/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(dotnet.native ${MONO_ARTIFACTS_DIR}/libmono-wasm-${CONFIGURATION_INTERPSIMDTABLES_LIB}.a ${MONO_ARTIFACTS_DIR}/libmono-profiler-aot.a ${MONO_ARTIFACTS_DIR}/libmono-profiler-browser.a + ${MONO_ARTIFACTS_DIR}/libz.a ${NATIVE_BIN_DIR}/wasm-bundled-timezones.a ${NATIVE_BIN_DIR}/libSystem.Native.a ${NATIVE_BIN_DIR}/libSystem.Globalization.Native.a diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index e972500d1cd81d..8ac0c0f7c3c290 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -168,9 +168,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNWIND_H 1 -/* Use in-tree zlib */ -#cmakedefine INTERNAL_ZLIB 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_POLL_H 1 diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 14056eb3a7e24e..ceb783599086e5 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -1189,6 +1189,9 @@ JS_ENGINES = [NODE_JS] <_MonoRuntimeArtifacts Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)out\lib\libmono-wasm-nosimd.a"> $(RuntimeBinDir)libmono-wasm-nosimd.a + <_MonoRuntimeArtifacts Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(BuildMonoAOTCrossCompilerOnly)' != 'true'" Include="$(MonoObjDir)_deps\fetchzlibng-build\libz.a"> + $(RuntimeBinDir)libz.a + <_MonoICorDebugArtifacts Condition="'$(MonoMsCorDbi)' == 'true'" Include="$(MonoObjDir)out\lib\$(LibPrefix)mscordbi$(LibSuffix)"> $(RuntimeBinDir)$(LibPrefix)mscordbi$(LibSuffix) diff --git a/src/mono/mono/eventpipe/test/CMakeLists.txt b/src/mono/mono/eventpipe/test/CMakeLists.txt index d313e630adaaba..92ddfb3ce17f96 100644 --- a/src/mono/mono/eventpipe/test/CMakeLists.txt +++ b/src/mono/mono/eventpipe/test/CMakeLists.txt @@ -39,7 +39,16 @@ if(ENABLE_PERFTRACING) set(CMAKE_SKIP_RPATH 1) add_executable(ep-test ${EVENTPIPE_TEST_SOURCES} ${EVENTPIPE_TEST_HEADERS}) target_sources(ep-test PRIVATE "${mono-components-objects}") - target_link_libraries(ep-test PRIVATE eglib_api monosgen-static ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS} monoapi) + + set(EPTEST_LINKABLE_LIBS "") + list(APPEND EPTEST_LINKABLE_LIBS eglib_api monosgen-static ${OS_LIBS} ${LLVM_LIBS} monoapi) + if (CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND EPTEST_LINKABLE_LIBS ${EPTEST_LINKABLE_LIBS} ${Z_LIBS}) + else() + list(APPEND EPTEST_LINKABLE_LIBS ${EPTEST_LINKABLE_LIBS} zlib) + endif() + target_link_libraries(ep-test PRIVATE ${EPTEST_LINKABLE_LIBS}) + install_with_stripped_symbols(ep-test TARGETS bin) else(ENABLE_EVENTPIPE_TEST AND STATIC_COMPONENTS AND (NOT DISABLE_COMPONENTS) AND (NOT DISABLE_LIBS) AND (NOT DISABLE_EXECUTABLES)) message(VERBOSE "Skip building native EventPipe library test runner.") diff --git a/src/mono/mono/metadata/CMakeLists.txt b/src/mono/mono/metadata/CMakeLists.txt index 773381d6c1c9ad..e3463b514d0c0f 100644 --- a/src/mono/mono/metadata/CMakeLists.txt +++ b/src/mono/mono/metadata/CMakeLists.txt @@ -200,7 +200,14 @@ set(metadata_sources "${metadata_platform_sources};${metadata_common_sources};${ if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) add_library(metadata_objects_shared OBJECT ${metadata_sources}) target_compile_definitions(metadata_objects_shared PRIVATE ${metadata_compile_definitions}) - target_link_libraries(metadata_objects_shared PRIVATE monoapi eglib_api utils_objects_shared) + + set(METADATAOBJECTSSHARED_LINKABLE_LIBS "") + list(APPEND METADATAOBJECTSSHARED_LINKABLE_LIBS monoapi eglib_api utils_objects_shared) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND METADATAOBJECTSSHARED_LINKABLE_LIBS zlib) + endif() + target_link_libraries(metadata_objects_shared PRIVATE ${METADATAOBJECTSSHARED_LINKABLE_LIBS}) + # note: metadata_objects is an object library, so this doesn't force linking with sgen, # it just adds the relevant include directories - which we need even with Boehm target_link_libraries(metadata_objects_shared PRIVATE sgen_objects_shared) @@ -212,7 +219,14 @@ endif() add_library(metadata_objects OBJECT ${metadata_sources}) target_compile_definitions(metadata_objects PRIVATE ${metadata_compile_definitions}) -target_link_libraries(metadata_objects PRIVATE monoapi eglib_api utils_objects) + +set(METADATAOBJECTS_LINKABLE_LIBS "") +list(APPEND METADATAOBJECTS_LINKABLE_LIBS monoapi eglib_api utils_objects) +if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND METADATAOBJECTS_LINKABLE_LIBS zlib) +endif() +target_link_libraries(metadata_objects PRIVATE ${METADATAOBJECTS_LINKABLE_LIBS}) + # note: metadata_objects is an object library, so this doesn't force linking with sgen, # it just adds the relevant include directories - which we need even with Boehm target_link_libraries(metadata_objects PRIVATE sgen_objects) diff --git a/src/mono/mono/metadata/debug-mono-ppdb.c b/src/mono/mono/metadata/debug-mono-ppdb.c index bcbbb4c7f2be03..3ed40e9243dad3 100644 --- a/src/mono/mono/metadata/debug-mono-ppdb.c +++ b/src/mono/mono/metadata/debug-mono-ppdb.c @@ -30,12 +30,8 @@ #include #ifndef DISABLE_EMBEDDED_PDB -#ifdef INTERNAL_ZLIB -#include -#else #include -#endif -#endif +#endif // DISABLE_EMBEDDED_PDB #include "debug-mono-ppdb.h" diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 93ff6c431ef03b..af9987869f5ac2 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -314,10 +314,7 @@ elseif(NOT HOST_BROWSER AND NOT HOST_WASI) set(mini_sources "${mini_sources};${VERSION_FILE_PATH}") # this is generated by GenerateNativeVersionFile in Arcade endif() -set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") -set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") - -set(monosgen-sources "${mini_sources};${ZLIB_SOURCES}") +set(monosgen-sources "${mini_sources}") if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) add_library(monosgen-objects_shared OBJECT "${monosgen-sources}") @@ -326,7 +323,14 @@ if(HOST_WIN32 AND NOT DISABLE_SHARED_LIBS) endif() add_library(monosgen-objects OBJECT "${monosgen-sources}") -target_link_libraries (monosgen-objects PRIVATE monoapi eglib_api utils_objects sgen_objects metadata_objects) + +set(MONOSGEN_OBJECTS_LINKABLE_LIBS "") +list(APPEND MONOSGEN_OBJECTS_LINKABLE_LIBS monoapi eglib_api utils_objects sgen_objects metadata_objects) +if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND MONOSGEN_OBJECTS_LINKABLE_LIBS zlib) +endif() +target_link_libraries (monosgen-objects PRIVATE ${MONOSGEN_OBJECTS_LINKABLE_LIBS}) + if(NOT HOST_WIN32) target_compile_definitions(monosgen-objects PRIVATE -DMONO_DLL_EXPORT) endif() @@ -353,15 +357,23 @@ if(NOT DISABLE_SHARED_LIBS) # musl-libc implements ucontext in a different library on s390x if(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) target_link_libraries(monosgen-shared PRIVATE ucontext) - endif(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) + endif() set_target_properties(monosgen-shared PROPERTIES OUTPUT_NAME ${MONO_SHARED_LIB_NAME}) if(MONO_SET_RPATH_ORIGIN) set_target_properties(monosgen-shared PROPERTIES INSTALL_RPATH "$ORIGIN") endif() + + set(MONOSGENSHARED_LINKABLE_LIBS "") + list(APPEND MONOSGENSHARED_LINKABLE_LIBS monoapi eglib_objects dn-containers) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND MONOSGENSHARED_LINKABLE_LIBS zlib) + endif() if(HOST_WIN32) - target_link_libraries(monosgen-shared PRIVATE monoapi eglib_objects utils_objects_shared sgen_objects_shared metadata_objects_shared dn-containers) + list(APPEND MONOSGENSHARED_LINKABLE_LIBS utils_objects_shared sgen_objects_shared metadata_objects_shared) + target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_LIBS}) else() - target_link_libraries(monosgen-shared PRIVATE monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) + list(APPEND MONOSGENSHARED_LINKABLE_LIBS utils_objects sgen_objects metadata_objects) + target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_LIBS}) endif() target_include_directories (monosgen-shared PRIVATE monoapi) if(TARGET_WIN32) @@ -369,11 +381,16 @@ if(NOT DISABLE_SHARED_LIBS) # to avoid a conflict we rename the import library with the .import.lib suffix set_target_properties(monosgen-shared PROPERTIES IMPORT_SUFFIX ".import.lib") endif() - if(CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) - target_link_libraries(monosgen-shared PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) + + set(MONOSGENSHARED_LINKABLE_EXTRALIBS "") + list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) + if (CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS ${Z_LIBS}) else() - target_link_libraries(monosgen-shared PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) + list(APPEND MONOSGENSHARED_LINKABLE_EXTRALIBS zlib) endif() + target_link_libraries(monosgen-shared PRIVATE ${MONOSGENSHARED_LINKABLE_EXTRALIBS}) + if(TARGET_DARWIN) set_property(TARGET monosgen-shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0") endif() @@ -417,8 +434,22 @@ if(NOT DISABLE_SHARED_LIBS) endif() add_library(${frameworkconfig} SHARED $) target_compile_definitions(${frameworkconfig} PRIVATE -DMONO_DLL_EXPORT) - target_link_libraries(${frameworkconfig} PRIVATE monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) - target_link_libraries(${frameworkconfig} PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) + + set(FRAMEWORKCONFIG_LINKABLE_LIBS "") + list(APPEND FRAMEWORKCONFIG_LINKABLE_LIBS monoapi eglib_objects utils_objects sgen_objects metadata_objects dn-containers) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND FRAMEWORKCONFIG_LINKABLE_LIBS zlib) + endif() + target_link_libraries(${frameworkconfig} PRIVATE ${FRAMEWORKCONFIG_LINKABLE_LIBS}) + + set(FRAMEWORKCONFIG_LINKABLE_EXTRALIBS "") + list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS zlib) + else() + list(APPEND FRAMEWORKCONFIG_LINKABLE_EXTRALIBS ${Z_LIBS}) + endif() + target_link_libraries(${frameworkconfig} PRIVATE ${FRAMEWORKCONFIG_LINKABLE_EXTRALIBS}) set_property(TARGET ${frameworkconfig} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-compatibility_version -Wl,2.0 -Wl,-current_version -Wl,2.0") string(REPLACE "*" ";" componentsobjects-whitespace "${componentsobjects}") @@ -534,11 +565,27 @@ if(NOT DISABLE_EXECUTABLES) set_target_properties(mono-sgen PROPERTIES INSTALL_RPATH "$ORIGIN") endif() endif() - target_link_libraries(mono-sgen PRIVATE monoapi eglib_api monosgen-static dn-containers) + + set(MONOSGEN_LINKABLE_LIBS "") + list(APPEND MONOSGEN_LINKABLE_LIBS monoapi eglib_api monosgen-static dn-containers) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND MONOSGEN_LINKABLE_LIBS zlib) + endif() + target_link_libraries(mono-sgen PRIVATE ${MONOSGEN_LINKABLE_LIBS}) + if (HOST_WASM) target_link_libraries(mono-sgen PRIVATE mono-wasm-nosimd) endif() - target_link_libraries(mono-sgen PRIVATE ${OS_LIBS} ${LLVM_LIBS} ${Z_LIBS}) + + set(MONOSGEN_LINKABLE_EXTRALIBS "") + list(APPEND MONOSGEN_LINKABLE_EXTRALIBS ${OS_LIBS} ${LLVM_LIBS}) + if (CLR_CMAKE_USE_SYSTEM_ZLIB) + list(APPEND MONOSGEN_LINKABLE_EXTRALIBS ${Z_LIBS}) + else() + list(APPEND MONOSGEN_LINKABLE_EXTRALIBS zlib) + endif() + target_link_libraries(mono-sgen PRIVATE ${MONOSGEN_LINKABLE_EXTRALIBS}) + # musl-libc implements ucontext in a different library on s390x if(CLR_CMAKE_TARGET_LINUX_MUSL AND TARGET_S390X) target_link_libraries(mono-sgen PRIVATE ucontext) diff --git a/src/mono/mono/profiler/CMakeLists.txt b/src/mono/mono/profiler/CMakeLists.txt index 511c19dbe5a322..e172774a6ebc5a 100644 --- a/src/mono/mono/profiler/CMakeLists.txt +++ b/src/mono/mono/profiler/CMakeLists.txt @@ -12,9 +12,7 @@ include_directories( if(NOT DISABLE_LIBS) if(HOST_ANDROID OR HOST_IOS OR HOST_TVOS OR HOST_S390X) # Build the logging profiler only for certain platforms - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") - add_library(mono-profiler-log SHARED helper.c log.c log-args.c ${ZLIB_SOURCES}) + add_library(mono-profiler-log SHARED helper.c log.c log-args.c) target_compile_definitions(mono-profiler-log PRIVATE -DMONO_DLL_EXPORT) target_link_libraries(mono-profiler-log PRIVATE monosgen-shared monoapi eglib_objects) if(HOST_ANDROID) @@ -28,7 +26,11 @@ if(NOT DISABLE_LIBS) install(TARGETS mono-profiler-log-static LIBRARY) if(NOT DISABLE_LOG_PROFILER_GZ) - target_link_libraries(mono-profiler-log PRIVATE ${Z_LIBS}) + if (CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(mono-profiler-log PRIVATE ${Z_LIBS}) + else() + target_link_libraries(mono-profiler-log PRIVATE zlib) + endif() endif() endif() diff --git a/src/mono/mono/profiler/log.c b/src/mono/mono/profiler/log.c index 9d4304faae3ecb..abe5f3f479f578 100644 --- a/src/mono/mono/profiler/log.c +++ b/src/mono/mono/profiler/log.c @@ -72,12 +72,8 @@ #include #endif #ifndef DISABLE_LOG_PROFILER_GZ -#ifdef INTERNAL_ZLIB -#include -#else #include -#endif -#endif +#endif // DISABLE_LOG_PROFILER_GZ #ifdef HOST_WIN32 #include diff --git a/src/mono/mono/profiler/mprof-report.c b/src/mono/mono/profiler/mprof-report.c index 35c88fe0a2fd9d..5e2cc5ad7a20e1 100644 --- a/src/mono/mono/profiler/mprof-report.c +++ b/src/mono/mono/profiler/mprof-report.c @@ -23,12 +23,8 @@ #endif #include #ifndef DISABLE_LOG_PROFILER_GZ -#ifdef INTERNAL_ZLIB -#include -#else #include -#endif -#endif +#endif // DISABLE_LOG_PROFILER_GZ #include #include #include diff --git a/src/mono/msbuild/apple/build/AppleBuild.targets b/src/mono/msbuild/apple/build/AppleBuild.targets index 35d0f16c35b4b8..483ae04924a728 100644 --- a/src/mono/msbuild/apple/build/AppleBuild.targets +++ b/src/mono/msbuild/apple/build/AppleBuild.targets @@ -6,6 +6,7 @@ false <_ProcessRuntimeComponentsForLibraryMode Condition="'$(_IsLibraryMode)' == 'true' and '$(UseNativeAOTRuntime)' != 'true'">_ProcessRuntimeComponentsForLibraryMode false + true - <_CommonLinkerArgs Include="-lz" /> + <_CommonLinkerArgs Include="-lz" Condition="'$(UseSystemZlib)' == 'true'" /> <_CommonLinkerArgs Include="-lc++" /> <_CommonLinkerArgs Include="-liconv" /> <_CommonLinkerArgs Include="-licucore" /> diff --git a/src/mono/wasi/runtime/CMakeLists.txt b/src/mono/wasi/runtime/CMakeLists.txt index 1d2a220494779a..ec45f3aa2e488e 100644 --- a/src/mono/wasi/runtime/CMakeLists.txt +++ b/src/mono/wasi/runtime/CMakeLists.txt @@ -27,6 +27,7 @@ target_link_libraries(dotnet ${MONO_ARTIFACTS_DIR}/libmonosgen-2.0.a ${MONO_ARTIFACTS_DIR}/libmono-icall-table.a ${MONO_ARTIFACTS_DIR}/libmono-wasm-${CONFIGURATION_INTERPSIMDTABLES_LIB}.a + ${MONO_ARTIFACTS_DIR}/libz.a ${NATIVE_BIN_DIR}/wasm-bundled-timezones.a ${NATIVE_BIN_DIR}/libSystem.Native.a ${NATIVE_BIN_DIR}/libSystem.Globalization.Native.a diff --git a/src/mono/wasi/wasi.proj b/src/mono/wasi/wasi.proj index 9c386481a26e19..99bf8a6ee54f80 100644 --- a/src/mono/wasi/wasi.proj +++ b/src/mono/wasi/wasi.proj @@ -49,6 +49,7 @@ + diff --git a/src/native/external/zlib-intel.cmake b/src/native/external/zlib-intel.cmake deleted file mode 100644 index a664f154dbef09..00000000000000 --- a/src/native/external/zlib-intel.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# IMPORTANT: do not use add_compile_options(), add_definitions() or similar functions here since it will leak to the including projects - -set(ZLIB_SOURCES_BASE - adler32.c - compress.c - crc_folding.c - crc32.c - deflate_medium.c - deflate_quick.c - deflate.c - inffast.c - inflate.c - inftrees.c - match.c - slide_sse.c - trees.c - x86.c - zutil.c - ../../libs/System.IO.Compression.Native/zlib_allocator_win.c -) - -addprefix(ZLIB_SOURCES "${CMAKE_CURRENT_LIST_DIR}/zlib-intel" "${ZLIB_SOURCES_BASE}") - -# enable custom zlib allocator -set(ZLIB_COMPILE_DEFINITIONS "MY_ZCALLOC") - -if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) - set(ZLIB_COMPILE_OPTIONS "/wd4127;/wd4131") -endif() diff --git a/src/native/external/zlib-ng.cmake b/src/native/external/zlib-ng.cmake new file mode 100644 index 00000000000000..3f005eaa54f3d3 --- /dev/null +++ b/src/native/external/zlib-ng.cmake @@ -0,0 +1,33 @@ +include(FetchContent) + +FetchContent_Declare( + fetchzlibng + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/zlib-ng") + +set(ZLIB_COMPAT ON) +set(ZLIB_ENABLE_TESTS OFF) +set(ZLIBNG_ENABLE_TESTS OFF) +set(Z_PREFIX ON) + +add_compile_options($<$:-Wno-unused-command-line-argument>) # clang : error : argument unused during compilation: '-fno-semantic-interposition' +add_compile_options($<$:-Wno-logical-op-parentheses>) # place parentheses around the '&&' expression to silence this warning +add_compile_options($<$:/wd4127>) # warning C4127: conditional expression is constant +add_compile_options($<$:/wd4242>) # 'function': conversion from 'unsigned int' to 'Pos', possible loss of data, in various deflate_*.c files +add_compile_options($<$:/wd4244>) # 'function': conversion from 'unsigned int' to 'Pos', possible loss of data, in various deflate_*.c files + +# 'aligned_alloc' is not available in browser/wasi, yet it is set by zlib-ng/CMakeLists.txt. +if (CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) + set(HAVE_ALIGNED_ALLOC FALSE CACHE BOOL "have aligned_alloc" FORCE) +endif() + +set(BUILD_SHARED_LIBS OFF) # Shared libraries aren't supported in wasm +set(SKIP_INSTALL_ALL ON) +FetchContent_MakeAvailable(fetchzlibng) +set(SKIP_INSTALL_ALL OFF) + +set_property(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/zlib-ng PROPERTY MSVC_WARNING_LEVEL 3) # Set the MSVC warning level for all zlib-ng targets to 3. +target_compile_options(zlib PRIVATE $<$:-Wno-unused-command-line-argument>) # Make sure MacOS respects ignoring unused CLI arguments +target_compile_options(zlib PRIVATE $<$:-Wno-logical-op-parentheses>) # place parentheses around the '&&' expression to silence this warning +target_compile_options(zlib PRIVATE $<$:/guard:cf>) # Enable CFG always for zlib-ng so we don't need to build two flavors. + +set_target_properties(zlib PROPERTIES DEBUG_POSTFIX "") # Workaround: zlib's debug lib name is zlibd.lib diff --git a/src/native/external/zlib.cmake b/src/native/external/zlib.cmake deleted file mode 100644 index 498ec977960e01..00000000000000 --- a/src/native/external/zlib.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# IMPORTANT: do not use add_compile_options(), add_definitions() or similar functions here since it will leak to the including projects - -set(ZLIB_SOURCES_BASE - adler32.c - compress.c - crc32.c - uncompr.c - deflate.c - gzguts.h - trees.c - zutil.c - inflate.c - infback.c - inftrees.c - inffast.c - crc32.h - deflate.h - inffast.h - inffixed.h - inflate.h - inftrees.h - trees.h - zconf.h - zlib.h - zutil.h -) - -if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) - set(ZLIB_SOURCES_BASE ${ZLIB_SOURCES_BASE} ../../libs/System.IO.Compression.Native/zlib_allocator_win.c) -else() - set(ZLIB_SOURCES_BASE ${ZLIB_SOURCES_BASE} ../../libs/System.IO.Compression.Native/zlib_allocator_unix.c) -endif() - -addprefix(ZLIB_SOURCES "${CMAKE_CURRENT_LIST_DIR}/zlib" "${ZLIB_SOURCES_BASE}") - -# enable custom zlib allocator -set(ZLIB_COMPILE_DEFINITIONS "MY_ZCALLOC") - -if(HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) - set(ZLIB_COMPILE_OPTIONS "/wd4127;/wd4131") -endif() diff --git a/src/native/libs/Common/pal_utilities.h b/src/native/libs/Common/pal_utilities.h index 7b5fa63b6cac03..a1a57c8ed0e8ce 100644 --- a/src/native/libs/Common/pal_utilities.h +++ b/src/native/libs/Common/pal_utilities.h @@ -12,7 +12,9 @@ #include #include #include -#include +#ifndef _WIN32 + #include +#endif #include #include @@ -69,6 +71,7 @@ inline static int ToFileDescriptorUnchecked(intptr_t fd) return (int)fd; } +#ifndef _WIN32 /** * Converts an intptr_t to a file descriptor. * intptr_t is the type used to marshal file descriptors so we can use SafeHandles effectively. @@ -86,6 +89,7 @@ static inline bool CheckInterrupted(ssize_t result) { return result < 0 && errno == EINTR; } +#endif inline static uint32_t Int32ToUint32(int32_t value) { diff --git a/src/native/libs/System.IO.Compression.Native/CMakeLists.txt b/src/native/libs/System.IO.Compression.Native/CMakeLists.txt index 089363cf5554ec..69d333ae53f9c5 100644 --- a/src/native/libs/System.IO.Compression.Native/CMakeLists.txt +++ b/src/native/libs/System.IO.Compression.Native/CMakeLists.txt @@ -2,10 +2,24 @@ project(System.IO.Compression.Native C) include(${CMAKE_CURRENT_LIST_DIR}/extra_libs.cmake) +if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + include(${CLR_SRC_NATIVE_DIR}/external/zlib-ng.cmake) +endif() + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/../Common/pal_config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/pal_config.h) + set(NATIVECOMPRESSION_SOURCES pal_zlib.c ) +if (HOST_WIN32 OR CLR_CMAKE_TARGET_WIN32) + list(APPEND NATIVECOMPRESSION_SOURCES "zlib_allocator_win.c") +else() + list(APPEND NATIVECOMPRESSION_SOURCES "zlib_allocator_unix.c") +endif() + if (NOT CLR_CMAKE_TARGET_BROWSER AND NOT CLR_CMAKE_TARGET_WASI) if (CLR_CMAKE_USE_SYSTEM_BROTLI) @@ -29,14 +43,6 @@ if (CLR_CMAKE_TARGET_UNIX OR CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) set(NATIVE_LIBS_EXTRA) append_extra_compression_libs(NATIVE_LIBS_EXTRA) - if (CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) - include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) - add_definitions(-DINTERNAL_ZLIB) - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") - set(NATIVECOMPRESSION_SOURCES ${ZLIB_SOURCES} ${NATIVECOMPRESSION_SOURCES}) - endif() - # Disable implicit fallthrough warning for Zlib and Brotli set(FLAGS -Wno-implicit-fallthrough) @@ -96,24 +102,16 @@ if (CLR_CMAKE_TARGET_UNIX OR CLR_CMAKE_TARGET_BROWSER OR CLR_CMAKE_TARGET_WASI) ${NATIVECOMPRESSION_SOURCES} ) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(System.IO.Compression.Native-Static PRIVATE zlibstatic) + endif() + set_target_properties(System.IO.Compression.Native-Static PROPERTIES OUTPUT_NAME System.IO.Compression.Native CLEAN_DIRECT_OUTPUT 1) else () if (GEN_SHARED_LIB) include (GenerateExportHeader) endif () - if (CLR_CMAKE_HOST_ARCH_I386 OR CLR_CMAKE_HOST_ARCH_AMD64) - include(${CLR_SRC_NATIVE_DIR}/external/zlib-intel.cmake) - add_definitions(-DINTERNAL_ZLIB_INTEL) - else () - include(${CLR_SRC_NATIVE_DIR}/external/zlib.cmake) - endif () - - add_definitions(-DINTERNAL_ZLIB) - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_DEFINITIONS "${ZLIB_COMPILE_DEFINITIONS}") - set_source_files_properties(${ZLIB_SOURCES} PROPERTIES COMPILE_OPTIONS "${ZLIB_COMPILE_OPTIONS}") - set(NATIVECOMPRESSION_SOURCES ${ZLIB_SOURCES} ${NATIVECOMPRESSION_SOURCES}) - if (GEN_SHARED_LIB) add_definitions(-DVER_FILEDESCRIPTION_STR="System.IO.Compression.Native") add_library(System.IO.Compression.Native @@ -122,6 +120,10 @@ else () System.IO.Compression.Native.def ${VERSION_FILE_RC_PATH} ) + + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(System.IO.Compression.Native PRIVATE zlib) + endif() endif () if (NOT GEN_SHARED_LIB AND NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_ANDROID AND NOT CLR_CMAKE_TARGET_BROWSER AND NOT CLR_CMAKE_TARGET_WASI) @@ -133,11 +135,20 @@ else () ${NATIVECOMPRESSION_SOURCES} ) + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(System.IO.Compression.Native-Static PRIVATE zlibstatic) + endif() + if(STATIC_LIBS_ONLY) add_library(System.IO.Compression.Native.Aot STATIC ${NATIVECOMPRESSION_SOURCES} ) + + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(System.IO.Compression.Native.Aot PRIVATE zlibstatic) + endif() + set_target_properties(System.IO.Compression.Native.Aot PROPERTIES CLR_CONTROL_FLOW_GUARD OFF) set_target_properties(System.IO.Compression.Native.Aot PROPERTIES INTERPROCEDURAL_OPTIMIZATION OFF) @@ -145,6 +156,11 @@ else () STATIC ${NATIVECOMPRESSION_SOURCES} ) + + if (NOT CLR_CMAKE_USE_SYSTEM_ZLIB) + target_link_libraries(System.IO.Compression.Native.Aot.GuardCF PRIVATE zlibstatic) + endif() + set_target_properties(System.IO.Compression.Native.Aot.GuardCF PROPERTIES INTERPROCEDURAL_OPTIMIZATION OFF) endif() @@ -167,4 +183,8 @@ else () endif () +if((NOT CLR_CMAKE_USE_SYSTEM_ZLIB) AND STATIC_LIBS_ONLY) + install_static_library(zlib aotsdk nativeaot) +endif() + install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/native/libs/System.IO.Compression.Native/extra_libs.cmake b/src/native/libs/System.IO.Compression.Native/extra_libs.cmake index 78530ae98e8ff7..6ad60233fce3f2 100644 --- a/src/native/libs/System.IO.Compression.Native/extra_libs.cmake +++ b/src/native/libs/System.IO.Compression.Native/extra_libs.cmake @@ -1,16 +1,19 @@ - macro(append_extra_compression_libs NativeLibsExtra) + set(ZLIB_LIBRARIES "") # TODO: remove the mono-style HOST_ variable checks once Mono is using eng/native/configureplatform.cmake to define the CLR_CMAKE_TARGET_ defines if (CLR_CMAKE_TARGET_BROWSER OR HOST_BROWSER OR CLR_CMAKE_TARGET_WASI OR HOST_WASI) # nothing special to link elseif (CLR_CMAKE_TARGET_ANDROID OR HOST_ANDROID) # need special case here since we want to link against libz.so but find_package() would resolve libz.a - set(ZLIB_LIBRARIES z) - elseif (CLR_CMAKE_TARGET_SUNOS OR HOST_SOLARIS) - set(ZLIB_LIBRARIES z m) - else () + list(APPEND ZLIB_LIBRARIES z) + elseif (CLR_CMAKE_HOST_ARCH_ARMV6) + find_package(ZLIB REQUIRED) + list(APPEND ZLIB_LIBRARIES z) + elseif (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) find_package(ZLIB REQUIRED) - set(ZLIB_LIBRARIES ${ZLIB_LIBRARIES} m) + list(APPEND ZLIB_LIBRARIES m) + else() + list(APPEND ZLIB_LIBRARIES zlib) endif () list(APPEND ${NativeLibsExtra} ${ZLIB_LIBRARIES}) diff --git a/src/native/libs/System.IO.Compression.Native/pal_zlib.c b/src/native/libs/System.IO.Compression.Native/pal_zlib.c index bed37d1955f481..87b7043ea20ffd 100644 --- a/src/native/libs/System.IO.Compression.Native/pal_zlib.c +++ b/src/native/libs/System.IO.Compression.Native/pal_zlib.c @@ -5,19 +5,14 @@ #include #include "pal_zlib.h" -#ifdef INTERNAL_ZLIB - #ifdef _WIN32 - #define c_static_assert(e) static_assert((e),"") - #endif - #ifdef INTERNAL_ZLIB_INTEL - #include - #else - #include - #endif +#ifdef _WIN32 + #define c_static_assert(e) static_assert((e),"") + #include "../Common/pal_utilities.h" #else #include "pal_utilities.h" - #include #endif +#include +#include c_static_assert(PAL_Z_NOFLUSH == Z_NO_FLUSH); c_static_assert(PAL_Z_FINISH == Z_FINISH); @@ -44,6 +39,10 @@ Initializes the PAL_ZStream by creating and setting its underlying z_stream. static int32_t Init(PAL_ZStream* stream) { z_stream* zStream = (z_stream*)calloc(1, sizeof(z_stream)); + + zStream->zalloc = z_custom_calloc; + zStream->zfree = z_custom_cfree; + stream->internalState = zStream; if (zStream != NULL) diff --git a/src/native/libs/System.IO.Compression.Native/pal_zlib.h b/src/native/libs/System.IO.Compression.Native/pal_zlib.h index b317091b843f62..efb090b0631ae7 100644 --- a/src/native/libs/System.IO.Compression.Native/pal_zlib.h +++ b/src/native/libs/System.IO.Compression.Native/pal_zlib.h @@ -5,12 +5,12 @@ #include #include #define FUNCTIONEXPORT - #define FUNCTIONCALLINGCONVENCTION WINAPI + #define FUNCTIONCALLINGCONVENTION WINAPI #else #include "pal_types.h" #include "pal_compiler.h" #define FUNCTIONEXPORT PALEXPORT - #define FUNCTIONCALLINGCONVENCTION + #define FUNCTIONCALLINGCONVENTION #endif /* @@ -84,7 +84,7 @@ Initializes the PAL_ZStream so the Deflate function can be invoked on it. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_DeflateInit2_( +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_DeflateInit2_( PAL_ZStream* stream, int32_t level, int32_t method, int32_t windowBits, int32_t memLevel, int32_t strategy); /* @@ -93,21 +93,21 @@ compressed bytes in nextOut. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Deflate(PAL_ZStream* stream, int32_t flush); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_Deflate(PAL_ZStream* stream, int32_t flush); /* All dynamically allocated data structures for this stream are freed. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_DeflateEnd(PAL_ZStream* stream); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_DeflateEnd(PAL_ZStream* stream); /* Initializes the PAL_ZStream so the Inflate function can be invoked on it. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_InflateInit2_(PAL_ZStream* stream, int32_t windowBits); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_InflateInit2_(PAL_ZStream* stream, int32_t windowBits); /* Inflates (uncompresses) the bytes in the PAL_ZStream's nextIn buffer and puts the @@ -115,14 +115,14 @@ uncompressed bytes in nextOut. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Inflate(PAL_ZStream* stream, int32_t flush); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_Inflate(PAL_ZStream* stream, int32_t flush); /* All dynamically allocated data structures for this stream are freed. Returns a PAL_ErrorCode indicating success or an error number on failure. */ -FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENCTION CompressionNative_InflateEnd(PAL_ZStream* stream); +FUNCTIONEXPORT int32_t FUNCTIONCALLINGCONVENTION CompressionNative_InflateEnd(PAL_ZStream* stream); /* Update a running CRC-32 with the bytes buffer[0..len-1] and return the @@ -130,4 +130,4 @@ updated CRC-32. Returns the updated CRC-32. */ -FUNCTIONEXPORT uint32_t FUNCTIONCALLINGCONVENCTION CompressionNative_Crc32(uint32_t crc, uint8_t* buffer, int32_t len); +FUNCTIONEXPORT uint32_t FUNCTIONCALLINGCONVENTION CompressionNative_Crc32(uint32_t crc, uint8_t* buffer, int32_t len); diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator.h b/src/native/libs/System.IO.Compression.Native/zlib_allocator.h new file mode 100644 index 00000000000000..cadd00bb5879c5 --- /dev/null +++ b/src/native/libs/System.IO.Compression.Native/zlib_allocator.h @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include // voidpf + +voidpf z_custom_calloc(voidpf opaque, unsigned items, unsigned size); + +void z_custom_cfree(voidpf opaque, voidpf ptr); diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c b/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c index 9eb4bbf2671057..b2c898f70a4e36 100644 --- a/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c +++ b/src/native/libs/System.IO.Compression.Native/zlib_allocator_unix.c @@ -3,7 +3,10 @@ #include #include -#include +#include +#include +#include +#include /* A custom allocator for zlib that provides some defense-in-depth over standard malloc / free. * (non-Windows version) @@ -70,7 +73,7 @@ static void WriteAllocCookieUnaligned(void* pDest, DOTNET_ALLOC_COOKIE vCookie) const size_t DOTNET_ALLOC_HEADER_COOKIE_SIZE_WITH_PADDING = (sizeof(DOTNET_ALLOC_COOKIE) + MEMORY_ALLOCATION_ALIGNMENT - 1) & ~((size_t)MEMORY_ALLOCATION_ALIGNMENT - 1); const size_t DOTNET_ALLOC_TRAILER_COOKIE_SIZE = sizeof(DOTNET_ALLOC_COOKIE); -voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) +voidpf z_custom_calloc(opaque, items, size) voidpf opaque; unsigned items; unsigned size; @@ -79,7 +82,7 @@ voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) // If initializing a fixed-size structure, zero the memory. bool fZeroMemory = (items == 1); - + size_t cbRequested; if (sizeof(items) + sizeof(size) <= sizeof(cbRequested)) { @@ -119,7 +122,7 @@ static void zcfree_trash_cookie(void* pCookie) memset(pCookie, 0, sizeof(DOTNET_ALLOC_COOKIE)); } -void ZLIB_INTERNAL zcfree(opaque, ptr) +void z_custom_cfree(opaque, ptr) voidpf opaque; voidpf ptr; { diff --git a/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c b/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c index 9bdf694495e680..fefba550c16ed5 100644 --- a/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c +++ b/src/native/libs/System.IO.Compression.Native/zlib_allocator_win.c @@ -7,11 +7,10 @@ #include #include /* _ASSERTE */ -#ifdef INTERNAL_ZLIB_INTEL -#include -#else -#include -#endif +#include +#include +#include +#include /* A custom allocator for zlib that provides some defense-in-depth over standard malloc / free. * (Windows-specific version) @@ -92,7 +91,7 @@ typedef struct _DOTNET_ALLOC_COOKIE const SIZE_T DOTNET_ALLOC_HEADER_COOKIE_SIZE_WITH_PADDING = (sizeof(DOTNET_ALLOC_COOKIE) + MEMORY_ALLOCATION_ALIGNMENT - 1) & ~((SIZE_T)MEMORY_ALLOCATION_ALIGNMENT - 1); const SIZE_T DOTNET_ALLOC_TRAILER_COOKIE_SIZE = sizeof(DOTNET_ALLOC_COOKIE); -voidpf ZLIB_INTERNAL zcalloc(opaque, items, size) +voidpf z_custom_calloc(opaque, items, size) voidpf opaque; unsigned items; unsigned size; @@ -150,7 +149,7 @@ void zcfree_cookie_check_failed() __fastfail(FAST_FAIL_HEAP_METADATA_CORRUPTION); } -void ZLIB_INTERNAL zcfree(opaque, ptr) +void z_custom_cfree(opaque, ptr) voidpf opaque; voidpf ptr; { From 55747a58666dcd33ccef5918bdf82720874f2163 Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Mon, 8 Jul 2024 16:15:02 -0700 Subject: [PATCH 72/72] Add more complete linux build instructions (#101631) I'm trying to make it slightly easier to install the needed requirements, and add some validation if a user hasn't installed the requirements. Also, I validated that these instructions still work for Ubuntu 24.04 and have noted that other installs are only community-supported. --- .../requirements/linux-requirements.md | 26 +++++++------------ eng/debian-reqs.txt | 19 ++++++++++++++ eng/install-native-dependencies.sh | 8 +++--- eng/native/build-commons.sh | 3 +++ eng/native/gen-buildsys.sh | 5 ---- 5 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 eng/debian-reqs.txt diff --git a/docs/workflow/requirements/linux-requirements.md b/docs/workflow/requirements/linux-requirements.md index 334e5e2ae354c3..d0e0c48105fec5 100644 --- a/docs/workflow/requirements/linux-requirements.md +++ b/docs/workflow/requirements/linux-requirements.md @@ -22,23 +22,15 @@ Minimum RAM required to build is 1GB. The build is known to fail on 512 MB VMs ( ### Toolchain Setup -Install the following packages for the toolchain: - -* CMake 3.20 or newer -* llvm -* lld -* clang -* build-essential -* python-is-python3 -* curl -* git -* lldb -* libicu-dev -* liblttng-ust-dev -* libssl-dev -* libkrb5-dev -* zlib1g-dev -* ninja-build (optional, enables building native code with ninja instead of make) +Install the packages listed in [debian-reqs.txt](/eng/debian-reqs.txt). + +You can install all the above dependencies by running + +```bash +sudo ./eng/install-native-dependencies.sh +``` + +### Community-supported environments **NOTE**: If you have an Ubuntu version older than 22.04 LTS, or Debian version older than 12, don't install `cmake` using `apt` directly. Follow the note written down below. diff --git a/eng/debian-reqs.txt b/eng/debian-reqs.txt new file mode 100644 index 00000000000000..d549245657c9a7 --- /dev/null +++ b/eng/debian-reqs.txt @@ -0,0 +1,19 @@ +build-essential +clang +cmake +curl +gettext +git +libicu-dev +libkrb5-dev +liblldb-dev +liblttng-ust-dev +libssl-dev +libunwind8-dev +lld +lldb +llvm +locales +ninja-build +python-is-python3 +zlib1g-dev \ No newline at end of file diff --git a/eng/install-native-dependencies.sh b/eng/install-native-dependencies.sh index 42a3727b3188a0..f34da2abda1c72 100755 --- a/eng/install-native-dependencies.sh +++ b/eng/install-native-dependencies.sh @@ -9,22 +9,24 @@ set -e # ./install-native-dependencies.sh os="$(echo "$1" | tr "[:upper:]" "[:lower:]")" +scriptroot="$(dirname "$0")" if [ -z "$os" ]; then + # shellcheck source-path=SCRIPTDIR . "$(dirname "$0")"/common/native/init-os-and-arch.sh fi case "$os" in linux) if [ -e /etc/os-release ]; then + # shellcheck source=/dev/null . /etc/os-release fi if [ "$ID" = "debian" ] || [ "$ID_LIKE" = "debian" ]; then apt update - apt install -y build-essential gettext locales cmake llvm clang lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \ - libssl-dev libkrb5-dev zlib1g-dev + xargs apt-get install -y < "$scriptroot/debian-reqs.txt" localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 elif [ "$ID" = "alpine" ]; then @@ -42,7 +44,7 @@ case "$os" in export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 # Skip brew update for now, see https://github.com/actions/setup-python/issues/577 # brew update --preinstall - brew bundle --no-upgrade --no-lock --file "$(dirname "$0")/Brewfile" + brew bundle --no-upgrade --no-lock --file "$scriptroot/Brewfile" ;; *) diff --git a/eng/native/build-commons.sh b/eng/native/build-commons.sh index 2cf33442a93253..0b480ff5c9cbac 100755 --- a/eng/native/build-commons.sh +++ b/eng/native/build-commons.sh @@ -39,6 +39,9 @@ check_prereqs() # We try again with the PKG_CONFIG_PATH in place, if pkg-config still can't find OpenSSL, exit with an error, cmake won't find OpenSSL either pkg-config openssl || { echo >&2 "Please install openssl before running this script, see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/macos-requirements.md"; exit 1; } fi + else + # Check presence of cmake on the path + command -v cmake 2>/dev/null || { echo >&2 "Please install cmake before running this script, see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/linux-requirements.md"; exit 1; } fi if [[ "$__UseNinja" == 1 ]]; then diff --git a/eng/native/gen-buildsys.sh b/eng/native/gen-buildsys.sh index 4c1ed0943c45ac..c3143e08f9273b 100755 --- a/eng/native/gen-buildsys.sh +++ b/eng/native/gen-buildsys.sh @@ -84,11 +84,6 @@ if [[ "$host_arch" == "armel" ]]; then cmake_extra_defines="$cmake_extra_defines -DARM_SOFTFP=1" fi -if ! cmake_command=$(command -v cmake); then - echo "CMake was not found in PATH." - exit 1 -fi - if [[ "$scan_build" == "ON" && -n "$SCAN_BUILD_COMMAND" ]]; then cmake_command="$SCAN_BUILD_COMMAND $cmake_command" fi